Presentazione di Framer Motion
Pubblicato: 2022-03-10In questo articolo, daremo un'occhiata più da vicino a come Framer Motion ci aiuta a creare fantastiche animazioni. Impareremo come funzionano i componenti di movimento e impareremo come concatenare le animazioni. Vedremo come creare animazioni attivate da gesti, temporizzate e di scorrimento con il movimento di Framer. Lungo la strada, useremo le cose che abbiamo imparato per creare cinque applicazioni demo che ho impostato per mostrarci come possiamo integrare Framer Motion nelle applicazioni del mondo reale.
Questo tutorial sarà utile per i lettori interessati all'integrazione delle animazioni nella loro applicazione React.
Nota: questo articolo richiede una conoscenza di base di React e CSS.
Che cos'è Framer Motion?
Framer Motion è una libreria di animazioni che semplifica la creazione di animazioni. La sua API semplificata ci aiuta ad astrarre le complessità dietro le animazioni e ci consente di creare animazioni con facilità.
Componenti di movimento
Questi sono gli elementi costitutivi del movimento di Framer. I componenti di movimento vengono creati anteponendo il motion
al tuo normale elemento HTML e SVG (ad esempio, motion.h1
). I componenti di movimento possono accettare diversi oggetti di scena, con quello di base che è l'elemento di scena animate
. Questo oggetto contiene un oggetto in cui definiamo le proprietà di quel componente che vogliamo animare. Le proprietà che definiamo verranno animate quando il componente viene montato nel DOM.
Animiamo un testo h1 usando Framer Motion. Innanzitutto, installiamo la libreria framer-motion e importiamo motion
.
npm i framer-motion import { motion } from 'framer-motion';
Quindi convertiamo h1 in una componente di movimento.
<motion.h1 animate={{x: 20, y: -20}}> This is a motion component </motion.h1>
Ciò farà scorrere h1
di 20 px a destra e si sposterà di 20 px in alto durante il caricamento. Quando le unità non vengono aggiunte, i calcoli vengono eseguiti utilizzando i pixel. Tuttavia, puoi impostare in modo esplicito le unità su cui desideri basare i calcoli, animate={{x: "20rem", y: "-20rem"}}>
.
Per impostazione predefinita, un componente di movimento verrà animato dallo stato definito dai suoi stili a quelli nell'elemento di animate
. Tuttavia, se volessimo, potremmo dirottare e definire lo stato di animazione iniziale del componente usando l'elica initial
. Mentre l'elica animate
viene utilizzata per definire il comportamento dei componenti quando montano, l'elica initial
definisce il loro comportamento prima che vengano montati.
Se vogliamo che il nostro h1 entri da sinistra, lo controlliamo usando l'elica iniziale.
<motion.h1 initial={{x: -1000}} animate={{x: 20}}> This is a motion component </motion.h1>
Ora, quando l' h1
si monta, scivola da sinistra.
Non ci limitiamo a una singola animazione. Possiamo definire una serie di animazioni chiamate keyframes
in un array di valori. Ogni valore verrà animato in sequenza.
<motion.h1 initial={{x: -1000}} animate={{x: [20, 50, 0, -70, 40] }}> This is a motion component </motion.h1>
Il prop di transition
ci consente di definire come si verificano le animazioni. Con esso, definiamo come i valori si animano da uno stato all'altro. Tra le altre cose, possiamo definire la duration
, il delay
e il type
di animazione usando questo prop.
<motion.h1 initial={{ x: -1000 }} animate={{ x: 0 }} transition={{ type: "tween", duration: "2", delay: "1" }}> This is a motion component </motion.h1>
Supponiamo di dover animare diversi componenti di movimento contemporaneamente, come nel frammento di codice di seguito.
<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>
Anche se funziona, il prop delle variants
in Framer Motion ci consente di estrarre le nostre definizioni di animazione in un oggetto varianti. variants
non solo rendono il nostro codice più pulito, ma ci consentono di creare animazioni ancora più potenti e complesse.
Estraendo le nostre definizioni di animazione in oggetti varianti, abbiamo questo:
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" } }
Invece di passare direttamente le definizioni dell'animazione negli oggetti di scena initial
e animate
di un componente, estraiamo queste definizioni in oggetti variant autonomi. Negli oggetti varianti, definiamo i nomi delle varianti che descrivono il nome di ciascuna animazione come varianti.
<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>
Nelle variants
prop, si passa il nome degli oggetti varianti per ogni componente di movimento e poi si passa nelle animazioni agli oggetti di scena initial
e animate
.
Possiamo portare ulteriormente la nostra configurazione attuale con varianti per ridurre le ripetizioni. Utilizzando le varianti, possiamo propagare gli attributi di animazione verso il basso attraverso il DOM da un componente di movimento padre. Affinché ciò funzioni, creiamo varianti per il genitore motion.div
con nomi di animazione simili nel suo oggetto variante come i suoi figli. In questo modo, non dovremo passare i nomi dell'animazione a ciascun componente figlio. Dietro le quinte, l'elemento genitore lo gestisce per noi.
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>
Ora abbiamo un codice più pulito senza ripetizioni. Abbiamo trasformato il contenitore div in un componente di movimento in modo da poter passare l'oggetto ContainerVariants
che abbiamo definito. Poiché non definiamo alcuna animazione sul contenitore, passiamo oggetti vuoti a initial
e animate
. I nomi dell'animazione devono essere gli stessi in ogni oggetto variante affinché la propagazione funzioni.
Ora comprendiamo le basi di Framer Motion. Iniziamo a creare il nostro pugno di 5 applicazioni demo.
Negozio di icone
Possiamo creare animazioni interattive basate sui gesti. I componenti di movimento sono attualmente in grado di ascoltare il rilevamento del passaggio del mouse, del tocco, della panoramica e del trascinamento. Costruiremo questa app Icon Shop usando il supporto whileHover
.
Componenti
-
App.js
: contiene i testi di intestazione. -
Card.jsx
: qui definiamo le animazioni per le schede icona. -
CardContainer.jsx
: importiamo e scorre le icone. -
styles.js
: crea, modella ed esporta i componenti di movimento. Ho usato styled-components per lo styling dei componenti.
Iniziamo con 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> );
Importiamo i componenti di movimento H1
e H2
che abbiamo creato nel file Styles.js
. Dal momento che sono componenti di movimento, utilizziamo gli oggetti di scena initial
e animate
per definire il loro comportamento prima e quando montano. Qui importiamo e visualizziamo anche il componente CardContiner
.
Ora, 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> ); };
Qui importiamo gli SVG, il componente Container
motion e il componente Card
.
Simile a H1
e H2
in App.js
, definiamo le animazioni del Container
usando gli oggetti di scena initial
e animate
. Quando viene caricato, creerà un fantastico effetto di scorrimento da sinistra del browser.
Ora, 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> ); };
Qui creiamo due oggetti varianti con le animazioni beforeHover
e onHover
. CardVariants
, inizialmente non vogliamo fare nulla, quindi beforeHover
è un oggetto vuoto. onHover
aumentiamo la scala della scatola delle carte.

IconVariants
, definiamo lo stato iniziale di IconBox
nel suo beforeHover
. Impostiamo la sua opacità su 0 e la spingiamo verso l'alto di 50px. Quindi, in onHover
, reimpostare l'opacità su 1, riportarla alla posizione predefinita e modificare il tipo di transizione in tween
. Quindi passiamo nelle varianti alle rispettive componenti di movimento. Utilizziamo la propagazione, quindi non è necessario impostare esplicitamente gli oggetti di scena initial
e animate
sul componente IconBox
.
Barra di navigazione animata
Costruiremo un semplice componente di navigazione e vedremo come creare relazioni temporali tra i componenti di movimento genitore e figlio.
Componenti
-
App.js
: contiene i testi di intestazione. -
Styles.js
: crea, modella ed esporta i componenti di movimento. I componenti sono stilizzati utilizzando componenti di stile.
Diamo un'occhiata al file App.js
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 } };
Creiamo uno stato isOpen
che verrà utilizzato per verificare se la Navbar è aperta o meno. Creiamo 3 varianti di oggetti, iconVariants
, menuVariants
e linkVariants
dove definiamo le animazioni rispettivamente per i componenti di movimento SvgBox
, Nav
e Link
. Il iconVariants
viene utilizzato per ruotare SvgBox
di 135 gradi quando viene posizionato sopra. Non è necessario aggiungere "gradi" al valore. Nel menuVariants
controlliamo la posizione in alto del Nav
come faresti usando la proprietà position
nei CSS. Commutamo la posizione superiore del Nav
in base allo stato isOpen
.
Con le varianti, possiamo creare relazioni temporali tra i componenti di movimento padre e figlio. Definiamo la relazione tra genitore Nav
e suo figlio, Link
usando la proprietà when
nell'oggetto di transizione. Qui, impostalo su beforeChildren
, in modo che le animazioni del componente padre finiscano prima dell'inizio dell'animazione del figlio.
Usando la proprietà staggerChildren
, impostiamo un ordine di tempo per ogni collegamento. Ogni collegamento impiegherà 0,5 secondi per apparire uno dopo l'altro. Questo crea un bel segnale visivo quando il Nav
è aperto. In linkVariants
l'opacità e la posizione verticale di ogni link.
<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>
Qui passiamo nelle varianti ai rispettivi componenti. In SvgBox
, modifichiamo lo stato di isOpen
ogni volta che viene cliccato, quindi lo animiamo in modo condizionale in base allo stato. Come SvgBox
, animiamo condizionatamente Nav
e Link
in base allo stato di isOpen
.
Modale animato
Costruiremo un componente modale e impareremo a conoscere AnimatePresence
di Framer Motion e come ci consente di animare gli elementi quando lasciano il DOM.
Componenti:
-
App.js
: abbiamo impostato lo statoshowModal
qui. -
Modal.jsx
: il vero lavoro di animazione si svolge qui. -
Styles.js
: crea, modella ed esporta i componenti di movimento. I componenti sono stilizzati utilizzando componenti di stile.
Esaminiamo 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> ); }
Creiamo uno stato showModal
che verrà utilizzato per eseguire il rendering condizionale del modale. La funzione toggleModal
lo stato ogni volta che si fa clic su ToggleButton
. ToggleButton
è un componente di movimento, quindi possiamo definire le animazioni per esso. Quando si monta, scorre da sinistra. Questa animazione viene eseguita per 0,5 secondi. Passiamo anche nello stato showModal
al Modal
attraverso oggetti di scena.
Ora, 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>
Importiamo AnimatePresence
da framer-motion
. Ci consente di impostare le animazioni di uscita per i componenti quando lasciano il DOM. Rendiamo condizionalmente il Modal
in base allo stato showModal
. Definiamo le animazioni per ModalBox
e ModalContent
attraverso i loro prop initial
e animate
. C'è anche un nuovo oggetto di scena qui, exit
. Avere AnimatePresence
come wrapper ci consente di aggiungere animazioni di uscita a ModalBox
nel prop di exit
.
Scorri l'animazione
Useremo una combinazione di useAnimation
hook e react-intersection-observer
per creare animazioni attivate dallo scorrimento.
Componenti
-
App.js
: impostiamo le animazioni per il componenteBox
e lo renderizziamo inApp
-
Styles.js
: crea, modella ed esporta i componenti di movimento. I componenti sono stilizzati utilizzando componenti di stile.
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} /> ); };
L'hook useAnimation
ci permette di controllare le sequenze in cui si verificano le nostre animazioni. Abbiamo accesso ai metodi controls.start
e controls.stop
che possiamo utilizzare per avviare e interrompere manualmente le nostre animazioni. Passiamo l'animazione hidden
iniziale a StyledBox
. Passiamo i controlli che abbiamo definito con il metodo start
a StyledBox
animate prop.
L' hook useInView
react-intersection-observer
ci consente di tenere traccia di quando un componente è visibile nel viewport. L'hook useInView
ci dà accesso a ref
, che passiamo al componente che vogliamo guardare, e al booleano inView
, che ci dice se quell'elemento è inView
o meno. Usiamo useEffect
per chiamare controls.start
ogni volta che l'elemento che stiamo guardando, StyledBox
è in vista. Passiamo controls
e inView
come dipendenze di useEffect
. Inoltre, passiamo le varianti che abbiamo definito, BoxVariants
a StyledBox
.
Animazione dell'eroe
Costruiremo una fantastica animazione per il banner dell'eroe usando il gancio useCycle
. Capiremo come useCycle
ci permette di scorrere le animazioni.
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" }, }, };
Definiamo 3 varianti, H1Variants
, TextVariants
e BannerVariants
. Tuttavia, il nostro obiettivo è BannerVariants
. Definiamo 2 animazioni, animationOne
e animationTwo
in BannerVariants
. Queste sono le animazioni che passiamo in useCycle
per scorrere.
const [animation, cycleAnimation] = useCycle("animationOne", "animationTwo"); useEffect(() => { setTimeout(() => { cycleAnimation(); }, 2000); }, []);
useCycle
funziona in modo simile useState
. Nell'array destrutturato, l' animation
rappresenta l'animazione attiva, sia animationOne
che animationTwo
. La funzione cylceAnimation
che scorre tra l'animazione che abbiamo definito. Passiamo le animazioni che vogliamo scorrere in useCycle
e chiamiamo cylceAnimation
dopo 2 secondi in useEffect
.
<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>
Alla fine di tutto, passiamo nelle varianti ai rispettivi componenti e guardiamo accadere la magia. Con questo, il Banner
scorrerà inizialmente da destra in base alle animazioni che abbiamo definito in animationOne
, e dopo 2 secondi verrà chiamato cycleAnimation
che attiverà animationTwo
.
Come disse una volta un saggio maiale, "questo è tutto gente".
Conclusione
Abbiamo esaminato le basi di Framer Motion e visto alcuni progetti demo che ci danno un'idea della gamma di animazioni che possiamo creare. Tuttavia, puoi fare molto di più con esso. Ti incoraggio a tuffarti nei documenti e scatenarti.
Risorse
- Framer Motion Api Docs, Framer Motion
- osservatore-intersezione-reazione, npm
- Framer Motion per React, NetNinja