Presentazione di Framer Motion

Pubblicato: 2022-03-10
Riepilogo rapido ↬ Le animazioni, se eseguite correttamente, sono potenti. Tuttavia, creare animazioni accattivanti con CSS può essere complicato. Arriva Framer Motion. Con Framer Motion, non è necessario essere un esperto di CSS per creare bellissime animazioni. Framer Motion ci fornisce animazioni pronte per la produzione e un'API di basso livello con cui possiamo interagire per integrare queste animazioni nelle nostre applicazioni.

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

Altro dopo il salto! Continua a leggere sotto ↓

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 stato showModal 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 componente Box e lo renderizziamo in App
  • 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