Vorstellung von Framer Motion

Veröffentlicht: 2022-03-10
Kurze Zusammenfassung ↬ Animationen sind mächtig, wenn sie richtig gemacht werden. Das Erstellen auffälliger Animationen mit CSS kann jedoch schwierig sein. Hier kommt Framer Motion. Mit Framer Motion müssen Sie kein CSS-Experte sein, um schöne Animationen zu erstellen. Framer Motion stellt uns produktionsreife Animationen und eine Low-Level-API zur Verfügung, mit der wir interagieren können, um diese Animationen in unsere Anwendungen zu integrieren.

In 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"}}> .

Mehr nach dem Sprung! Lesen Sie unten weiter ↓

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 den showModal -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 die Box -Komponente ein und rendern sie in App
  • 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