Animazione di componenti React con GreenSock
Pubblicato: 2022-03-10Durante i primi giorni del World Wide Web, le cose erano piuttosto statiche e noiose. Le pagine Web erano per lo più basate sulla progettazione grafica e sui layout del mondo della stampa fino all'introduzione delle animazioni. L'animazione può coinvolgere e mantenere l'attenzione delle persone più a lungo di una pagina Web statica e comunica un'idea o un concetto in modo più chiaro ed efficace.
Tuttavia, se non eseguite correttamente, le animazioni possono ostacolare le interazioni dell'utente con il tuo prodotto e influire negativamente sulla trazione. La GreenSock Animation Platform AKA (GSAP) è una potente libreria JavaScript che consente a sviluppatori, animatori e designer front-end di creare animazioni performanti basate su timeline. Consente agli amanti dell'animazione di assumere un controllo preciso delle sequenze di animazione piuttosto che dei keyframe
e delle proprietà di animation
talvolta vincolanti offerti dai CSS.
In questo articolo, ti presenterò alcune funzionalità di GSAP come scrollTriggers
, Timelines
, Easing
ecc, alla fine costruiremo un'interfaccia utente intuitiva animando un'app React con queste funzionalità. Scopri il progetto finito su codesandbox.
Questo articolo ti sarà utile se:
- Hai creato animazioni su applicazioni web con HTML, CSS e JavaScript.
- Stai già costruendo pagine Web animate in un'app React con pacchetti come animate.css, React-motion, Framer-motion e React-Spring, inoltre vuoi controllare le alternative.
- Sei un appassionato di React e vorresti creare animazioni complesse su applicazioni web basate su React.
Vedremo come creare una varietà di animazioni da un progetto web esistente. Andiamo a farlo!
Nota : questo articolo presuppone che tu abbia dimestichezza con HTML, CSS, JavaScript e React.js.
Cos'è GSAP?
GreenSock Animation Platform, nota anche come GSAP, è un'animazione ad altissime prestazioni di livello professionale per il Web moderno che consente agli sviluppatori di animare le proprie app in modo modulare, dichiarativo e riutilizzabile. È indipendente dal framework e può essere utilizzato in qualsiasi progetto basato su JavaScript, ha una dimensione del pacchetto molto minima e non rigonfia la tua app.
GSAP può eseguire animazioni su tela, utilizzate per creare esperienze WebGL e creare animazioni SVG dinamiche e un ottimo supporto per il browser.
Perché usare GSAP?
Forse non sei ancora pronto a tradire altri framework, o non sei stato convinto ad abbracciare le chicche che vengono con GSAP. Consentitemi di darvi alcuni motivi per cui potreste prendere in considerazione GSAP.
Puoi creare animazioni complesse
La libreria JavaScript GSAP consente agli sviluppatori di creare animazioni basate sulla fisica da semplici a molto complesse come nel caso di questi siti, consente a sviluppatori e designer di sequenziare il movimento e controllare l'animazione in modo dinamico. Ha molti plugin come DrawSVGPlugin, MorphSVGPlugin e altri, che rendono la creazione di animazioni basate su SVG e animazioni 2D/3D una realtà. Oltre a integrare GSAP su elementi DOM, puoi usarli all'interno di animazioni basate sul contesto WebGL/Canvas/Tre.js.
Inoltre, la capacità di allentamento di GSAP è piuttosto sofisticata, rendendo quindi possibile creare effetti avanzati con più bezier rispetto alla normale animazione CSS.
Prestazione
GSAP ha prestazioni elevate impressionanti su diversi browser.
Secondo il team di GSAP, nel loro sito web, “GSAP è 20 volte più veloce di jQuery, inoltre GSAP è lo strumento di animazione con script completo più veloce del pianeta. In molti casi è persino più veloce delle animazioni e delle transizioni CSS3". Conferma il confronto della velocità per te stesso.
Inoltre, le animazioni GSAP funzionano senza sforzo su computer desktop, tablet e smartphone. Non è necessario aggiungere un lungo elenco di prefissi, tutto questo viene gestito da GSAP.
Puoi controllare più vantaggi su GSAP o vedere cosa ne dice Sarah Drasner qui.
Contro di GSAP
Stai dicendo che dovrei usare sempre GSAP per ogni progetto? Ovviamente no! Sento che c'è solo un motivo per cui potresti non voler usare GSAP. Scopriamolo!
- GSAP è esclusivamente una libreria di animazione basata su JavaScript, quindi richiede una certa conoscenza della manipolazione di JavaScript e DOM per utilizzare efficacemente i suoi metodi e API. Questo aspetto negativo della curva di apprendimento lascia ancora più spazio a complicazioni per un principiante che inizia con JavaScript.
- GSAP non soddisfa le animazioni basate su CSS, quindi se stai cercando una libreria per tali, potresti anche usare i
keyframes
nell'animazione CSS.
Se hai qualche altro motivo, sentiti libero di condividerlo nella sezione commenti.
Bene, ora che i tuoi dubbi sono stati chiariti, passiamo al nocciolo della questione in GSAP.
Nozioni di base GSAP
Prima di creare la nostra animazione utilizzando React, acquisiamo familiarità con alcuni metodi e blocchi costitutivi di GSAP.
Se conosci già i fondamenti di GSAP, puoi saltare questa sezione e passare direttamente alla sezione del progetto, dove faremo un'inclinazione della pagina di destinazione durante lo scorrimento.
Gemello
Un'interpolazione è un singolo movimento in un'animazione. In GSAP, un'interpolazione ha la seguente sintassi:
TweenMax.method(element, duration, vars)
Diamo un'occhiata a cosa rappresenta questa sintassi;
-
method
si riferisce al metodo GSAP con cui ti piacerebbe eseguire l'interpolazione. -
element
è l'elemento che vuoi animare. Se desideri creare interpolazioni per più elementi contemporaneamente, puoi passare una matrice di elementi aelement
. - la
duration
è la durata dell'interpolazione. È un numero intero in secondi (senza il suffissos
!). -
vars
è un oggetto delle proprietà che vuoi animare. Ne parleremo più avanti.
metodi GSAP
GSAP fornisce numerosi metodi per creare animazioni. In questo articolo ne citeremo solo alcuni come gsap.to
, gsap.from
, gsap.fromTo
. Puoi controllare altri metodi interessanti nella loro documentazione. I metodi discussi in questa sezione verranno utilizzati nella creazione del nostro progetto più avanti in questo tutorial.
-
gsap.to()
i valori a cui deve essere animato un oggetto, ovvero i valori delle proprietà finali di un oggetto animato, come mostrato di seguito:gsap.to('.ball', {x:250, duration: 5})
Per dimostrare il metodo to
, la demo codepen di seguito mostra che un elemento con una classe di ball 250px
si sposterà x-axis
in cinque secondi quando i componenti vengono montati. Se non viene fornita una durata, verrà utilizzato un valore predefinito di 500 millisecondi.
Nota : gli assi x
e y-axis
rappresentano rispettivamente l'asse orizzontale e verticale, anche nelle proprietà di trasformazione CSS come translateX
e translateY
sono rappresentati come x
per le trasformazioni pixel-measured
e xPercent
e y
per le yPercent
in percentuale.
Per visualizzare lo snippet completo del codice, controlla il playground del codepen.
-
gsap.from()
— Definisce i valori da cui deve essere animato un oggetto — ovvero i valori iniziali di un'animazione:gsap.from('.square', {duration:3, scale: 4})
La demo codepen mostra come un elemento con una classe di square
viene ridimensionato da una scala di 4 in 3 3seconds
quando i componenti vengono montati. Verifica lo snippet di codice completo su questo codepen.
-
gsap.fromTo()
: consente di definire i valori iniziali e finali per un'animazione. È una combinazione del metodofrom()
eto()
.
Ecco come appare;
gsap.fromTo('.ball',{opacity:0 }, {opacity: 1 , x: 200 , duration: 3 }); gsap.fromTo('.square', {opacity:0, x:200}, { opacity:1, x: 1 , duration: 3 });
Questo codice anima l'elemento con una classe di ball
da un'opacità di 0 a un'opacità di 1
x-axis
in 3 seconds
e la classe del square
viene animata da un'opacità di 0
a 1
in 3 seconds
su x-axis
solo quando il componente è montato. Per vedere come funziona il metodo fromTo
e lo snippet di codice completo, controlla la demo su CodePen di seguito.
Nota : ogni volta che stiamo animando proprietà posizionali, come left
e top
, dobbiamo assicurarci che gli elementi interessati debbano avere una proprietà di posizione CSS relative
, absolute
o fixed
.
allentamento
La documentazione ufficiale di GSAP ha definito l'allentamento come il modo principale per modificare i tempi dei tuoi Tween. Determina come un oggetto cambia posizione in diversi punti. Facilità controlla la velocità di modifica dell'animazione in GSAP e viene utilizzato per impostare lo stile dell'animazione di un oggetto.
GSAP offre diversi tipi di facilità e opzioni per darti un maggiore controllo su come dovrebbe comportarsi la tua animazione. Fornisce inoltre un visualizzatore di facilità per aiutarti a scegliere le impostazioni di facilità preferite.
Esistono tre tipi di facilità e variano nelle loro operazioni.
-
in()
— Il movimento inizia lentamente, quindi aumenta il ritmo verso la fine dell'animazione. -
out()
— L'animazione si avvia velocemente e poi rallenta alla fine dell'animazione. -
inOut()
— L'animazione inizia lentamente, aumenta il ritmo a metà e termina lentamente.
In questo esempio di andamento, abbiamo concatenato le interpolazioni che mostravano i tre tipi di bounce.in
, bounce.out
e bounce.inOut
, e abbiamo impostato un ritardo del numero di secondi necessari per completare l'animazione prima di iniziare quella successiva solo quando il componente è monta. Questo schema è ripetitivo, nella prossima sezione vedremmo come potremmo usare una sequenza temporale per farlo meglio.
Linea del tempo
Una linea temporale funge da contenitore per più interpolazioni. Anima le interpolazioni in ordine sequenziale e non dipende dalla durata dell'interpolazione precedente. La sequenza temporale semplifica il controllo delle interpolazioni nel loro insieme e la gestione precisa dei loro tempi.
Le timeline possono essere scritte creando un'istanza di una timeline in questo modo:
gsap.timeline();
Puoi anche concatenare più interpolazioni a una sequenza temporale in due modi diversi, nel codice seguente:
##Method 1 const tl = gsap.timeline(); // create an instance and assign it a variable tl.add(); // add tween to timeline tl.to('element', {}); tl.from('element', {}); ##Method 2 gsap.timeline() .add() // add tween to timeline .to('element', {}) .from('element', {})
Ricreiamo l'esempio precedente con una sequenza temporale:
const { useRef, useEffect } = React; const Balls = () => { useEffect(() => { const tl = gsap.timeline(); tl.to('#ball1', {x:1000, ease:"bounce.in", duration: 3}) tl.to('#ball2', {x:1000, ease:"bounce.out", duration: 3, delay:3 }) tl.to('#ball3', {x:1000, ease:"bounce.inOut", duration: 3, delay:6 }) }, []); } ReactDOM.render( , document.getElementById('app'));
const { useRef, useEffect } = React; const Balls = () => { useEffect(() => { const tl = gsap.timeline(); tl.to('#ball1', {x:1000, ease:"bounce.in", duration: 3}) tl.to('#ball2', {x:1000, ease:"bounce.out", duration: 3, delay:3 }) tl.to('#ball3', {x:1000, ease:"bounce.inOut", duration: 3, delay:6 }) }, []); } ReactDOM.render( , document.getElementById('app'));
All'interno di un hook useEffect
, abbiamo creato una variabile (tl)
che contiene un'istanza di una timeline, quindi abbiamo usato la variabile tl
per animare la nostra interpolazione in sequenza senza dipendere dall'interpolazione precedente da animare, passando le stesse proprietà che erano nella esempio precedente. Per lo snippet di codice completo di questa demo, controlla il playground del codepen di seguito.
Ora che abbiamo avuto un'idea di alcuni elementi costitutivi di base di GSAP, vediamo come potremmo creare un'animazione completa in una tipica app React nella prossima sezione. Iniziamo il volo!
Costruire una pagina di destinazione animata con React e GSAP
Andiamo ad animare un'app React. Assicurati di clonare il repository prima di iniziare ed eseguire npm install
per installare le dipendenze.
Cosa stiamo costruendo?
Attualmente, la nostra pagina di destinazione contiene alcuni testi su sfondo bianco, un menu che non scende, senza alcuna animazione. I seguenti sono ciò che aggiungeremo alla pagina di destinazione;
- Anima il testo e il logo sulla home page, in modo che risulti più facile quando il componente è montato.
- Anima il menu, in modo che scenda quando si fa clic sul menu.
- Rendi le immagini nella pagina della galleria inclinate
20deg
quando la pagina scorre.
Guarda la demo su codesandbox.
Spezzeremo il processo della nostra pagina di destinazione in componenti, quindi sarà facile da capire. Ecco il processo;
- Definire i metodi di animazione,
- Anima testo e logo,
- Attiva/disattiva menu,
- Rendi le immagini inclinate di
20deg
sullo scorrimento della pagina.
componenti
-
Animate.js
: definiti tutti i metodi di animazione, -
Image.js
— importa le immagini della cambusa, -
Menu.js
— Contiene la funzionalità di commutazione del menu, -
Header.js
: contiene i collegamenti di navigazione.
Definisci i metodi di animazione
Crea una cartella dei component
all'interno della directory src
e crea un file animate.js
. Copia e incolla il codice seguente al suo interno.
import gsap from "gsap" import { ScrollTrigger } from "gsap/ScrollTrigger"; //Animate text export const textIntro = elem => { gsap.from(elem, { xPercent: -20, opacity: 0, stagger: 0.2, duration: 2, scale: -1, ease: "back", }); };
Qui, abbiamo importato gsap
. Abbiamo scritto una funzione freccia esportata che anima il testo sulla pagina di destinazione. Ricorda che il metodo gsap.from()
definisce i valori da cui un oggetto dovrebbe essere animato. La funzione ha un parametro elem
che rappresenta la classe che deve essere animata. Richiede alcune proprietà e assegna valori come xPercent: -20
(trasforma l'oggetto di -20%), non conferisce opacità all'oggetto, scale
l'oggetto di -1
, fa ease
l'oggetto in 2sec
.
Per vedere se funziona, vai su App.js
e includi il codice seguente.
... //import textIntro import {textIntro} from "./components/Animate" ... //using useRef hook to access the textIntro DOM let intro = useRef(null) useEffect(() => { textIntro(intro) }, []) function Home() { return ( <div className='container'> <div className='wrapper'> <h5 className="intro" ref={(el) => (intro = el)}></h5> The <b>SHOPPER</b>, is a worldclass, innovative, global online ecommerce platform, that meets your everyday daily needs. </h5> </div> </div> ); }
Qui importiamo il metodo textIntro
dal componente Aminate
. Per accedere al DOM useRef
Hook. Abbiamo creato una variabile intro
il cui valore è impostato su null
. Successivamente, all'interno useEffect
, abbiamo chiamato il metodo textIntro
e la variabile intro
. All'interno del nostro componente home, nel tag h5
, abbiamo definito il ref
prop e passato la variabile intro
.
Successivamente, abbiamo un menu, ma non scende quando viene cliccato. Facciamolo funzionare! All'interno del componente Header.js
, aggiungi il codice di seguito.
import React, { useState, useEffect, useRef } from "react"; import { withRouter, Link, useHistory } from "react-router-dom"; import Menu from "./Menu"; const Header = () => { const history = useHistory() let logo = useRef(null); //State of our Menu const [state, setState] = useState({ initial: false, clicked: null, menuName: "Menu", }); // State of our button const [disabled, setDisabled] = useState(false); //When the component mounts useEffect(() => { textIntro(logo); //Listening for page changes. history.listen(() => { setState({ clicked: false, menuName: "Menu" }); }); }, [history]); //toggle menu const toggleMenu = () => { disableMenu(); if (state.initial === false) { setState({ initial: null, clicked: true, menuName: "Close", }); } else if (state.clicked === true) { setState({ clicked: !state.clicked, menuName: "Menu", }); } else if (state.clicked === false) { setState({ clicked: !state.clicked, menuName: "Close", }); } }; // check if out button is disabled const disableMenu = () => { setDisabled(!disabled); setTimeout(() => { setDisabled(false); }, 1200); }; return ( <header> <div className="container"> <div className="wrapper"> <div className="inner-header"> <div className="logo" ref={(el) => (logo = el)}> <Link to="/">SHOPPER.</Link> </div> <div className="menu"> <button disabled={disabled} onClick={toggleMenu}> {state.menuName} </button> </div> </div> </div> </div> <Menu state={state} /> </header> ); }; export default withRouter(Header);
In questo componente, abbiamo definito il nostro menu e lo stato del pulsante, all'interno useEffect
, abbiamo ascoltato i cambiamenti di pagina usando l'hook useHistory
, se la pagina cambia abbiamo impostato i valori dello stato clicked
e menuName
rispettivamente su false
e Menu
.
Per gestire il nostro menu, abbiamo verificato se il valore del nostro stato iniziale è false, se true, cambiamo il valore di initial
, clicked
e menuName
in null
, true
e Close
. Altrimenti controlliamo se il pulsante è stato cliccato, se true cambieremmo il menuName
in Menu
. Successivamente, abbiamo una funzione disabledMenu
che disabilita il nostro pulsante per 1 secondo quando viene 1sec
.
Infine, nel nostro button
, abbiamo assegnato disabled
a disabled
che è un valore booleano che disabiliterà il pulsante quando il suo valore è true
. E il gestore onClick
del pulsante è legato alla funzione toggleMenu
. Tutto ciò che abbiamo fatto qui è stato alternare il testo del menu
e passare lo stato a un componente Menu
, che avremmo creato al più presto. Scriviamo i metodi che renderanno il nostro menu a tendina prima di creare il componente Menu
vero e proprio. Vai su Animate.js
e incollaci questo codice.
.... //Open menu export const menuShow = (elem1, elem2) => { gsap.from([elem1, elem2], { duration: 0.7, height: 0, transformOrigin: "right top", skewY: 2, ease: "power4.inOut", stagger: { amount: 0.2, }, }); }; //Close menu export const menuHide = (elem1, elem2) => { gsap.to([elem1, elem2], { duration: 0.8, height: 0, ease: "power4.inOut", stagger: { amount: 0.07, }, }); };
Qui abbiamo una funzione chiamata menuShow
, che inclina il menu orizzontalmente di 2degrees
, semplifica il menu, sposta l'animazione utilizzando la proprietà stagger
e trasforma il menu da right to top
in 0.7sec
, le stesse proprietà vanno per la funzione menuHide
. Per utilizzare queste funzioni, crea il file Menu.js
all'interno dei components
e incolla questo codice al suo interno.
import React, {useEffect, useRef} from 'react' import { gsap } from "gsap" import { Link } from "react-router-dom" import { menuShow, menuHide, textIntro, } from './Animate' const Menu = ({ state }) => { //create refs for our DOM elements let menuWrapper = useRef(null) let show1 = useRef(null) let show2 = useRef(null) let info = useRef(null) useEffect(() => { // If the menu is open and we click the menu button to close it. if (state.clicked === false) { // If menu is closed and we want to open it. menuHide(show2, show1); // Set menu to display none gsap.to(menuWrapper, { duration: 1, css: { display: "none" } }); } else if ( state.clicked === true || (state.clicked === true && state.initial === null) ) { // Set menu to display block gsap.to(menuWrapper, { duration: 0, css: { display: "block" } }); //Allow menu to have height of 100% gsap.to([show1, show2], { duration: 0, opacity: 1, height: "100%" }); menuShow(show1, show2); textIntro(info); } }, [state]) return ( <div ref={(el) => (menuWrapper = el)} className="hamburger-menu"> <div ref={(el) => (show1 = el)} className="menu-secondary-background-color" ></div> <div ref={(el) => (show2 = el)} className="menu-layer"> <div className="container"> <div className="wrapper"> <div className="menu-links"> <nav> <ul> <li> <Link ref={(el) => (line1 = el)} to="/about-us" > About </Link> </li> <li> <Link ref={(el) => (line2 = el)} to="/gallery" > Gallery </Link> </li> <li> <Link ref={(el) => (line3 = el)} to="/contact-us" > Contact us </Link> </li> </ul> </nav> <div ref={(el) => (info = el)} className="info"> <h3>Our Vision</h3> <p> Lorem ipsum dolor sit amet consectetur adipisicing elit.... </p> </div> </div> </div> </div> </div> </div> ); } export default Menu
Quello che abbiamo fatto nel componente Menu
è stato importare le funzioni animate, che sono menuShow
, menuHide
e textIntro
. Successivamente, abbiamo assegnato variabili per ogni riferimento creato per i nostri elementi DOM
utilizzando l' useRef
refs
passato null
come valori. All'interno useEffect
, controlliamo lo stato del menu
, se clicked
è false
, chiamiamo la funzione menuHide
, altrimenti, se lo stato clicked
è true chiamiamo la funzione menuShow
. Infine, ci siamo assicurati che gli elementi DOM
interessati menuWrapper
refs
show1
, show2
. Con ciò, abbiamo animato il nostro menu.
Vediamo come appare.
L'ultima animazione che implementeremo è rendere le nostre immagini nella nostra galleria skew
quando scorre. Vediamo ora lo stato della nostra galleria.
Per implementare l'animazione skew nella nostra galleria, andiamo su Animate.js
e aggiungiamo alcuni codici.
.... //Skew gallery Images export const skewGallery = elem1 => { //register ScrollTrigger gsap.registerPlugin(ScrollTrigger); // make the right edge "stick" to the scroll bar. force3D: true improves performance gsap.set(elem1, { transformOrigin: "right center", force3D: true }); let clamp = gsap.utils.clamp(-20, 20) // don't let the skew go beyond 20 degrees. ScrollTrigger.create({ trigger: elem1, onUpdate: (self) => { const velocity = clamp(Math.round(self.getVelocity() / 300)); gsap.to(elem1, { skew: 0, skewY: velocity, ease: "power3", duration: 0.8, }); }, }); }
Abbiamo creato una funzione chiamata skewGallery
, passato elem1
come parametro e registrato ScrollTrigger
.
ScrollTrigger è un plug-in in GSAP che ci consente di attivare animazioni basate sullo scorrimento, come in questo caso l'inclinazione delle immagini mentre la pagina scorre.
Per fare in modo che il bordo destro si attacchi alla barra di scorrimento, abbiamo passato il valore del right center
alla proprietà transformOrigin
, abbiamo anche impostato la proprietà force3D
su true in other per migliorare le prestazioni.
Abbiamo dichiarato una variabile clamp
che calcola la nostra inclinazione e garantisce che non superi i 20degs
. All'interno dell'oggetto ScrollTrigger
, abbiamo assegnato la proprietà trigger
al parametro elem1
, che sarebbe l'elemento che deve essere attivato quando chiamiamo questa funzione. Abbiamo una funzione di callback onUpdate
, al suo interno c'è una variabile di velocity
che calcola la velocità attuale e la divide per 300
.
Infine, animiamo l'elemento dai loro valori correnti impostando altri valori. skew
in modo che inizialmente sia a 0
e skewY
come variabile di velocity
a 0.8
.
Successivamente, dobbiamo chiamare questa funzione nel nostro file App.js
.... import { skewGallery } from "./components/Animate" function Gallery() { let skewImage = useRef(null); useEffect(() => { skewGallery(skewImage) }, []); return ( <div ref={(el) => (skewImage = el)}> <Image/> </div> ) } ....
Qui, abbiamo importato skewGalley
da ./components/Animate
, creato un skewImage
che ha come target l'elemento dell'immagine. All'interno useEffect
, abbiamo chiamato la funzione skewGallery
e passato skewImage
ref come parametro. Infine, abbiamo passato skewImage
ref
to.
Saresti d'accordo con me che è stato un viaggio così bello finora. Ecco l'anteprima su CodeSanbox
Il repository di supporto per questo articolo è disponibile su Github.
Conclusione
Abbiamo esplorato la potenza di GSAP in un progetto React, abbiamo solo scalfito la superficie in questo articolo, non c'è limite a ciò che puoi fare con GSAP per quanto riguarda l'animazione. Il sito Web ufficiale di GSAP offre suggerimenti aggiuntivi per aiutarti a comprendere a fondo i metodi e i plug-in. Ci sono molte demo che ti lascerebbero a bocca aperta su ciò che le persone hanno fatto con GSAP. Mi piacerebbe sentire la tua esperienza con GSAP nella sezione commenti.
Risorse
- Documentazione GSAP, GreenSock
- "Guida per principianti alla piattaforma di animazione GreenSock", Nicholas Kramer, freeCodeCamp
- "Un'introduzione alle animazioni con Greensock Animation API (GSAP)", Zell Liew