Vorstellung von Framer Motion
Veröffentlicht: 2022-03-10In diesem Artikel sehen wir uns genauer an, wie Framer Motion uns bei der Erstellung fantastischer Animationen hilft. Wir lernen, wie Bewegungskomponenten funktionieren und wie man Animationen miteinander verkettet. Wir werden untersuchen, wie Sie mit Framer-Bewegungen durch Gesten ausgelöste, zeitgesteuerte und Scroll-Animationen erstellen können. Unterwegs verwenden wir die Dinge, die wir lernen, um fünf Demoanwendungen zu erstellen, die ich eingerichtet habe, um uns zu zeigen, wie wir Framer Motion in reale Anwendungen integrieren können.
Dieses Tutorial ist für Leser interessant, die daran interessiert sind, Animationen in ihre React-Anwendung zu integrieren.
Hinweis: Dieser Artikel erfordert ein grundlegendes Verständnis von React und CSS.
Was ist Framer Motion?
Framer Motion ist eine Animationsbibliothek, die das Erstellen von Animationen vereinfacht. Seine vereinfachte API hilft uns, die Komplexität hinter Animationen zu abstrahieren und ermöglicht es uns, Animationen mit Leichtigkeit zu erstellen.
Bewegungskomponenten
Dies sind die Bausteine der Framer-Bewegung. Motion-Komponenten werden erstellt, indem motion
Ihrem regulären HTML- und SVG-Element vorangestellt wird (z. B. motion.h1
). Bewegungskomponenten können mehrere Requisiten akzeptieren, wobei das grundlegende Requisit das animate
Requisit ist. Diese Requisite nimmt ein Objekt auf, in dem wir die Eigenschaften dieser Komponente definieren, die wir animieren möchten. Die von uns definierten Eigenschaften werden animiert, wenn die Komponente im DOM bereitgestellt wird.
Lassen Sie uns einen h1-Text mit Framer Motion animieren. Zuerst installieren wir die Framer-Motion-Bibliothek und importieren motion
.
npm i framer-motion import { motion } from 'framer-motion';
Dann wandeln wir das h1 in eine Bewegungskomponente um.
<motion.h1 animate={{x: 20, y: -20}}> This is a motion component </motion.h1>
Dadurch wird h1
beim Laden um 20 Pixel nach rechts und um 20 Pixel nach oben verschoben. Wenn keine Einheiten hinzugefügt werden, werden Berechnungen mit Pixeln durchgeführt. Sie können jedoch explizit die Einheiten festlegen, auf denen die Berechnungen basieren sollen, animate={{x: "20rem", y: "-20rem"}}>
.
Standardmäßig wird eine Bewegungskomponente von dem Zustand animiert, der von ihren Stilen zu denen in der animate
Requisite definiert wurde. Wenn wir jedoch wollten, könnten wir den anfänglichen Animationszustand der Komponente mithilfe der initial
Requisite entführen und definieren. Während die animate
Requisite verwendet wird, um das Verhalten von Komponenten beim Mounten zu definieren, definiert die initial
Requisite ihr Verhalten vor dem Mounten.
Wenn wir wollen, dass unser h1 von links hereinkommt, steuern wir das mit der anfänglichen Stütze.
<motion.h1 initial={{x: -1000}} animate={{x: 20}}> This is a motion component </motion.h1>
Jetzt, wenn der h1
montiert wird, gleitet er von links hinein.
Wir sind nicht auf eine einzelne Animation beschränkt. Wir können eine Reihe von Animationen definieren, die keyframes
in einem Array von Werten genannt werden. Jeder Wert wird nacheinander animiert.
<motion.h1 initial={{x: -1000}} animate={{x: [20, 50, 0, -70, 40] }}> This is a motion component </motion.h1>
Mit der transition
können wir definieren, wie die Animationen auftreten. Damit definieren wir, wie Werte von einem Zustand in einen anderen animieren. Unter anderem können wir mit diesem Prop die duration
, delay
und type
der Animation definieren.
<motion.h1 initial={{ x: -1000 }} animate={{ x: 0 }} transition={{ type: "tween", duration: "2", delay: "1" }}> This is a motion component </motion.h1>
Angenommen, wir würden mehrere Bewegungskomponenten gleichzeitig animieren, wie im folgenden Code-Snippet.
<div className="App"> <motion.h1 initial={{ x: -1000 }} animate={{ x: 0 }} transition={{ type: "tween", duration: "2", delay: "1" }}> This is a motion h1 </motion.h1> <motion.h2 initial={{ y: -1000 }} animate={{ y: 0 }} transition={{ type: "tween", duration: "1", delay: ".4" }}>This is a motion h2 </motion.h2> <motion.h3 initial={{ x: 100, opacity: 0 }} animate={{ x: 0, opacity: 1 }}> This is a motion h3 </motion.h3> <motion.h4 initial={{ scale: 0.7 }} animate={{ scale: 1.7 }} transition={{ type: "tween", duration: "2", delay: "1" }}> This is a motion h4 </motion.h4> </div>
Während dies funktioniert, ermöglicht uns die variants
-Requisite in Framer Motion, unsere Animationsdefinitionen in ein Variantenobjekt zu extrahieren. variants
machen unseren Code nicht nur sauberer, sondern ermöglichen es uns, noch leistungsfähigere und komplexere Animationen zu erstellen.
Wenn wir unsere Animationsdefinitionen in Variantenobjekte extrahieren, haben wir Folgendes:
const H1Variants = { initial: { x: -1000 }, animate: { x: 0 }, transition: { type: "tween", duration: 2, delay: 1 } } const H2Variants = { initial: { y: -1000 }, animate: { y: 0 }, transition: { type: "tween", duration: 1, delay: .4 } } const H3Variants = { initial:{ x: 100, opacity: 0 }, animate:{ x: 0, opacity: 1 } } const H4Variants = { initial:{ scale: 0.7 }, animate:{ scale: 1.7 }, transition:{ type: "tween", duration: "2", delay: "1" } }
Anstatt die Animationsdefinitionen direkt in die initial
und animate
Requisiten einer Komponente zu übergeben, extrahieren wir diese Definitionen in eigenständige Variantenobjekte. In den Variantenobjekten definieren wir Variantennamen, die den Namen jeder Animation als Varianten beschreiben.
<div className="App"> <motion.h1 variants={H1Variants} initial='initial' animate='animate' > This is a motion h1 </motion.h1> <motion.h2 variants={H2Variants} initial='initial' animate='animate' > This is a motion h2 </motion.h2> <motion.h3 variants={H3Variants} initial='initial' animate='animate' > This is a motion h3 </motion.h3> <motion.h4 variants={H4Variants} initial='initial' animate='animate' > This is a motion h4 </motion.h4> </div>
In der variants
-Requisite übergeben wir den Namen der Variantenobjekte für jede Bewegungskomponente und übergeben dann die Animationen an die initial
und animate
Requisiten.
Wir können unser aktuelles Setup mit Varianten weiter ausbauen, um Wiederholungen zu reduzieren. Mithilfe von Varianten können wir Animationsattribute von einer übergeordneten Bewegungskomponente durch das DOM nach unten propagieren. Damit dies funktioniert, erstellen wir Varianten für die übergeordnete motion.div
mit ähnlichen Animationsnamen in ihrem Variantenobjekt wie in ihren untergeordneten Objekten. Auf diese Weise müssen wir die Animationsnamen nicht an jede untergeordnete Komponente weitergeben. Hinter den Kulissen erledigt das das übergeordnete Element für uns.
const ContainerVariants = { initial: {}, animate: {} }; const H1Variants = { initial: { x: -1000 }, animate: { x: 0 }, transition: { type: "tween", duration: 2, delay: 1 } }; //more variants below <motion.div className="App" variants={ContainerVariants} initial="initial" animate="animate" > <motion.h1 variants={H1Variants}>This is a motion h1</motion.h1> <motion.h2 variants={H2Variants}>This is a motion h2</motion.h2> <motion.h3 variants={H3Variants}>This is a motion h3</motion.h3> <motion.h4 variants={H4Variants}>This is a motion h4</motion.h4> </motion.div>
Jetzt haben wir einen saubereren Code ohne Wiederholungen. Wir haben das Container-Div in eine Bewegungskomponente umgewandelt, damit wir das von uns definierte ContainerVariants
-Objekt übergeben können. Da wir keine Animationen auf dem Container definieren, übergeben wir leere Objekte an initial
und animate
. Ihre Animationsnamen müssen in allen Variantenobjekten gleich sein, damit die Weitergabe funktioniert.
Jetzt verstehen wir die Grundlagen von Framer Motion. Beginnen wir mit der Erstellung unserer ersten von 5 Demoanwendungen.
Icon-Shop
Wir können interaktive Animationen basierend auf Gesten erstellen. Bewegungskomponenten sind derzeit in der Lage, auf die Erkennung von Hover-, Tap-, Pan- und Drag-Gesten zu hören. Wir werden diese Icon-Shop-App mit der whileHover
Prop erstellen.
Komponenten
-
App.js
: enthält die Überschriftentexte. -
Card.jsx
: Hier definieren wir die Animationen für die Symbolkarten. -
CardContainer.jsx
: Wir importieren und durchlaufen die Symbole. -
styles.js
: Erstellen, stylen und exportieren Sie die Bewegungskomponenten. Ich habe styled-components zum Styling der Komponenten verwendet.
Beginnen wir mit App.js
.
import { H1, H2 } from "./Styles"; import CardContainer from "./CardContainer"; return ( <div> <H1 initial={{ y: -100 }} animate={{ y: 0, transition: { delay: 1 } }}> Icon Shop </H1> <H2 initial={{ x: -1000 }} animate={{ x: 0, transition: { delay: 1 } }}> Hover over the cards to see the motion magic </H2> <CardContainer /> </div> );
Wir importieren die von uns erstellten H1
und H2
-Bewegungskomponenten in die Datei Styles.js
. Da es sich um Bewegungskomponenten handelt, verwenden wir die initial
und animate
Requisiten, um ihr Verhalten vor und beim Einsteigen zu definieren. Hier importieren und zeigen wir auch die CardContiner
Komponente an.
Nun, die CardContainer.js
.
import { Container } from "./Styles"; import Card from "./Card"; import { ReactComponent as AddIcon } from "./assets/add.svg"; import { ReactComponent as AirplaneIcon } from "./assets/airplane.svg"; import { ReactComponent as AlarmIcon } from "./assets/alarm.svg"; //more svg imports below... const icons = [ <AddIcon />, <AirplaneIcon />, <AlarmIcon />, //more icons below ]; const CardContainer = () => { return ( <Container initial={{ x: -1000 }} animate={{ x: 0 }}> {icons.map((icon) => ( <Card icon={icon} /> ))} </Container> ); };
Hier importieren wir die Card
, die Container
-Bewegungskomponente und die Kartenkomponente.
Ähnlich wie bei H1
und H2
in App.js
definieren wir Animationen des Container
mithilfe der initial
und animate
Requisiten. Wenn es geladen wird, erzeugt es einen coolen Effekt, indem es von der linken Seite des Browsers hineingleitet.
Nun, Card.js
import { CardBox, IconBox } from "./Styles"; const CardVariants = { beforeHover: {}, onHover: { scale: 1.1 } }; const IconVariants = { beforeHover: { opacity: 0, y: -50 }, onHover: { opacity: 1, y: 0, scale: 1.5, transition: { type: "tween" } } }; const Card = ({ icon }) => { console.log(icon); return ( <CardBox variants={CardVariants} initial="beforeHover" whileHover="onHover"> <IconBox variants={IconVariants}>{icon}</IconBox> </CardBox> ); };
Hier erstellen wir zwei Variantenobjekte mit beforeHover
und onHover
Animationen. Im CardVariants
Objekt wollen wir anfangs nichts tun, also ist beforeHover
ein leeres Objekt. onHover
vergrößern wir den Maßstab der Kartenbox.

Im IconVariants
Objekt definieren wir den Anfangszustand der IconBox
in ihrem beforeHover
. Wir setzen die Deckkraft auf 0 und verschieben sie um 50 Pixel nach oben. Dann setzen wir in onHover
die Deckkraft zurück auf 1, schieben sie zurück auf ihre Standardposition und ändern den Übergangstyp in tween
. Dann übergeben wir die Varianten an ihre jeweiligen Bewegungskomponenten. Wir nutzen die Propagierung, sodass wir die initial
und animate
Requisiten nicht explizit auf die IconBox
Komponente setzen müssen.
Animierte Navigationsleiste
Wir werden eine einfache Navigationskomponente erstellen und sehen, wie wir zeitliche Beziehungen zwischen übergeordneten und untergeordneten Bewegungskomponenten erstellen können.
Komponenten
-
App.js
: enthält die Überschriftentexte. -
Styles.js
: Erstellen, stylen und exportieren Sie die Bewegungskomponenten. Die Komponenten werden mit styled-components gestaltet.
Werfen wir einen Blick auf die App.js
-Datei.
import { Header, Nav, Link, SvgBox } from "./Styles"; function App() { const [isOpen, setIsOpen] = useState(false); const iconVariants = { opened: { rotate: 135 }, closed: { rotate: 0 } }; const menuVariants = { opened: { top: 0, transition: { when: "beforeChildren", staggerChildren: 0.5 } }, closed: { top: "-90vh" } }; const linkVariants = { opened: { opacity: 1, y: 50 }, closed: { opacity: 0, y: 0 } };
Wir erstellen einen isOpen
-Zustand, der verwendet wird, um zu prüfen, ob die Navbar geöffnet ist oder nicht. Wir erstellen 3 Variant-Objekte, iconVariants
, menuVariants
und linkVariants
, wo wir die Animationen für die Bewegungskomponenten SvgBox
, Nav
und Link
definieren. Das iconVariants
wird verwendet, um die SvgBox
um 135 Grad zu drehen, wenn man darüber schwebt. Wir müssen dem Wert kein „deg“ hinzufügen. In den menuVariants
steuern wir die oberste Position des Nav
, wie Sie es mit der position
in CSS tun würden. Wir schalten die oberste Position des Nav
basierend auf dem isOpen
-Zustand um.
Mit Varianten können wir zeitliche Beziehungen zwischen übergeordneten und untergeordneten Bewegungskomponenten erstellen. Wir definieren die Beziehung zwischen dem übergeordneten Nav
und seinem untergeordneten Element Link
, indem wir die Eigenschaft when
im Übergangsobjekt verwenden. Setzen Sie es hier auf beforeChildren
, damit die Animationen der übergeordneten Komponente beendet werden, bevor die Animation der untergeordneten Komponente beginnt.
Mit der Eigenschaft staggerChildren
wir für jeden Link eine zeitliche Reihenfolge fest. Jeder Link dauert 0,5 Sekunden, bis er nacheinander erscheint. Dies erzeugt einen schönen visuellen Hinweis, wenn das Nav
geöffnet wird. In den linkVariants
animieren wir die Deckkraft und die vertikale Position jedes Links.
<div className="App"> <Header> <SvgBox variants={iconVariants} animate={isOpen ? "opened" : "closed"} onClick={() => setIsOpen(!isOpen)} > <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="https://www.w3.org/2000/svg" > <path d="M12 4C11.4477 4 11 4.44772 11 5V11H5C4.44772 11 4 11.4477 4 12C4 12.5523 4.44772 13 5 13H11V19C11 19.5523 11.4477 20 12 20C12.5523 20 13 19.5523 13 19V13H19C19.5523 13 20 12.5523 20 12C20 11.4477 19.5523 11 19 11H13V5C13 4.44772 12.5523 4 12 4Z" fill="#fff" /> </svg> </SvgBox> </Header> <Nav initial={false} variants={menuVariants} animate={isOpen ? "opened" : "closed"} > <Link variants={linkVariants}>home</Link> <Link variants={linkVariants}>about</Link> <Link variants={linkVariants}>gallery</Link> </Nav> </div>
Hier übergeben wir die Varianten an ihre jeweiligen Komponenten. In der SvgBox
wir den Zustand von isOpen
, wenn darauf geklickt wird, und animieren es dann bedingt auf der Grundlage des Zustands. Wie bei der SvgBox
animieren wir Nav
und die Link
bedingt basierend auf dem Zustand von isOpen
.
Animiertes Modal
Wir erstellen eine modale Komponente und erfahren mehr über AnimatePresence
von Framer Motion und wie wir damit Elemente animieren können, wenn sie das DOM verlassen.
Komponenten:
-
App.js
: Hier richten wir denshowModal
-Zustand ein. -
Modal.jsx
: Hier findet die eigentliche Animationsarbeit statt. -
Styles.js
: Erstellen, stylen und exportieren Sie die Bewegungskomponenten. Die Komponenten werden mit styled-components gestaltet.
Schauen wir uns App.js
import { ToggleButton, Container } from "./Styles"; import Modal from "./Modal"; function App() { const [showModal, setShowModal] = useState(false); const toggleModal = () => { setShowModal(!showModal); }; return ( <Container> <ToggleButton initial={{ x: -700 }} animate={{ x: 0, transition: { duration: 0.5 } }} onClick={toggleModal} > Toggle Modal </ToggleButton> <Modal showModal={showModal} /> </Container> ); }
Wir erstellen einen showModal
-Zustand, der verwendet wird, um das Modal bedingt zu rendern. Die toggleModal
Funktion schaltet den Zustand um, wenn auf den ToggleButton
geklickt wird. ToggleButton
ist eine Bewegungskomponente, daher können wir Animationen dafür definieren. Wenn es montiert wird, gleitet es von links hinein. Diese Animation läuft 0,5 Sekunden lang. Wir übergeben auch den Zustand showModal
durch Requisiten an Modal
.
Nun, Modal.jsx
import { AnimatePresence } from "framer-motion"; import { ModalBox, ModalContent, Container } from "./Styles"; <Container> <AnimatePresence> {showModal && ( <ModalBox initial={{ opacity: 0, y: 60, scale: 0.3 }} animate={{ opacity: 1, y: 0, scale: 1, transition: { type: "spring", stiffness: 300 } }} exit={{ opacity: 0, scale: 0.5, transition: { duration: 0.6 } }} > <ModalContent initial={{ y: -30, opacity: 0 }} animate={{ y: 0, opacity: 1, transition: { delay: 1 } }} > Modal content!!!! </ModalContent> </ModalBox> )} </AnimatePresence> </Container>
Wir importieren AnimatePresence
aus framer-motion
. Es ermöglicht uns, Exit-Animationen für Komponenten festzulegen, wenn sie DOM verlassen. Wir rendern das Modal
bedingt basierend auf dem showModal
-Zustand. Wir definieren die Animationen für ModalBox
und ModalContent
durch ihre initial
und animate
Requisiten. Hier gibt es auch eine neue Requisite, exit
. Mit AnimatePresence
als Wrapper können wir Exit-Animationen zu ModalBox
in der exit
-Prop hinzufügen.
Scroll-Animation
Wir verwenden eine Kombination aus dem useAnimation
-Hook und React react-intersection-observer
, um durch Scrollen ausgelöste Animationen zu erstellen.
Komponenten
-
App.js
: Wir richten die Animationen für dieBox
-Komponente ein und rendern sie inApp
-
Styles.js
: Erstellen, stylen und exportieren Sie die Bewegungskomponenten. Die Komponenten werden mit styled-components gestaltet.
import React, { useEffect } from "react"; import { useAnimation } from "framer-motion"; import { useInView } from "react-intersection-observer"; import { Container, H1,StyledBox } from "./Styles"; const BoxVariants = { visible: { opacity: 1, x: 0, transition: { duration: 1 } }, hidden: { opacity: 0, x: 300 }, }; const Box = () => { const controls = useAnimation(); const [ref, inView] = useInView(); useEffect(() => { if (inView) { controls.start("visible"); } }, [controls, inView]); return ( <StyledBox ref={ref} animate={controls} initial="hidden" variants={BoxVariants} /> ); };
Der Hook useAnimation
ermöglicht es uns, die Sequenzen zu steuern, in denen unsere Animationen auftreten. Wir haben Zugriff auf die Methoden " controls.start
und controls.stop
", mit denen wir unsere Animationen manuell starten und stoppen können. Wir übergeben die anfängliche hidden
Animation an StyledBox
. Wir übergeben die Steuerelemente, die wir mit der start
definiert haben, an StyledBox
animate prop.
Der useInView
-Hook von respond react-intersection-observer
ermöglicht uns zu verfolgen, wann eine Komponente im Ansichtsfenster sichtbar ist. Der Hook useInView
gibt uns Zugriff auf ref
, das wir an die Komponente übergeben, die wir beobachten möchten, und den booleschen inView
, der uns sagt, ob dieses Element inView
ist oder nicht. Wir verwenden useEffect
zum Aufrufen von controls.start
, wenn das Element, das wir beobachten, StyledBox
, sichtbar ist. Wir übergeben controls
und inView
als Abhängigkeiten von useEffect
. Außerdem übergeben wir die von uns definierten Varianten BoxVariants
an StyledBox
.
Heldenanimation
Wir werden eine coole Hero-Banner-Animation mit dem useCycle
Hook erstellen. Wir werden verstehen, wie useCycle
es uns ermöglicht, Animationen zu durchlaufen.
import React, { useEffect } from "react"; import { useCycle } from "framer-motion"; import { Container, H1, HeroSection, Banner, TextBox } from "./Styles"; import { ReactComponent as BannerIllustration } from "./bighead.svg"; const H1Variants = { initial: { y: -200, opacity: 0 }, animate: { y: 0, opacity: 1, transition: { delay: 1 } }, }; const TextVariants = { initial: { x: 400 }, animate: { x: 0, transition: { duration: 0.5 } }, }; const BannerVariants = { animationOne: { x: -250, opacity: 1, transition: { duration: 0.5 } }, animationTwo: { y: [0, -20], opacity: 1, transition: { yoyo: Infinity, ease: "easeIn" }, }, };
Wir definieren 3 Varianten, H1Variants
, TextVariants
und BannerVariants
. Unser Fokus liegt jedoch BannerVariants
. Wir definieren 2 Animationen, animationOne
und animationTwo
in BannerVariants
. Dies sind die Animationen, die wir an useCycle
, um sie zu durchlaufen.
const [animation, cycleAnimation] = useCycle("animationOne", "animationTwo"); useEffect(() => { setTimeout(() => { cycleAnimation(); }, 2000); }, []);
useCycle
funktioniert ähnlich wie der useState
Hook. Im destrukturierten Array stellt animation
die aktive Animation dar, entweder animationOne
oder animationTwo
. Die cylceAnimation
Funktion, die zwischen den von uns definierten Animationen wechselt. Wir übergeben die Animationen, die wir durchlaufen wollen, in useCycle
und rufen cylceAnimation
nach 2 Sekunden in useEffect
auf.
<div className="App"> <Container> <H1 variants={H1Variants} initial="initial" animate="animate"> Cool Hero Section Anmiation </H1> <HeroSection> <TextBox variants={TextVariants} initial="initial" animate="animate"> Storage shed, troughs feed bale manure, is garden wheat oats at augers. Bulls at rose garden cucumbers mice sunflower wheat in pig. Chainsaw foal hay hook, herbs at combine harvester, children is mallet. Goat goose hen horse. Pick up truck livestock, pets and storage shed, troughs feed bale manure, is garden wheat oats at augers. Lamb. </TextBox> <Banner variants={BannerVariants} animate={animation}> <BannerIllustration /> </Banner> </HeroSection> </Container> </div>
Am Ende übergeben wir die Varianten an ihre jeweiligen Komponenten und sehen zu, wie die Magie geschieht. Damit schiebt sich das Banner
zunächst von rechts ein, basierend auf den Animationen, die wir in animationOne
definiert haben, und nach 2 Sekunden wird cycleAnimation
aufgerufen, was animationTwo
auslöst.
Wie ein weises Schwein einmal sagte: „Das ist alles, Leute.“
Fazit
Wir haben die Grundlagen von Framer Motion durchgearbeitet und einige Demoprojekte gesehen, die uns einen Einblick in die Bandbreite der Animationen geben, die wir erstellen können. Sie können jedoch noch viel mehr damit machen. Ich ermutige Sie, in die Dokumente einzutauchen und wild zu werden.
Ressourcen
- Framer Motion API-Dokumente, Framer Motion
- Reaktionskreuzungsbeobachter, npm
- Framer Motion für React, NetNinja