Choose your fighter: React-Spring vs. GSAP
26.04.2023
Due librerie per le animazioni a confronto: chi avrà la meglio?
Chiunque abbia avuto a che fare con un designer sa che una caratteristica fondamentale di questi professionisti è quella di partorire costantemente idee creative, grandiose, bellissime… E che faranno piangere il developer che deve realizzarle.
Nel mio caso, le designer mi hanno duramente messa alla prova con la realizzazione di svariate animazioni. Questo mi ha portata a interrogarmi su quale sia la migliore libreria per realizzare le loro fantasiose invenzioni usando React.
Ho provato molte librerie diverse, più o meno efficienti e intuitive, ma due sono quelle che ritengo più versatili: React-Spring e GreenSock (GSAP). Perciò ho deciso di confrontarle, uno step alla volta, per stabilire una volta per tutte quale delle due sia migliore.
Considerazioni generali
Ci sono alcune differenze sostanziali tra React-Spring e GSAP. La prima fra tutte, ovvia anche dal nome, è che React-Spring nasce specificamente per React. Viceversa, come cita la documentazione, GSAP è framework-agnostic, sebbene fornisca una guida abbastanza completa sull’utilizzo associato con React.
Cosa significa a livello pratico? React-Spring fornisce una serie di hook che interagiscono fluidamente con React. Viceversa, tutte le animazioni create con GSAP devono essere dichiarate all’interno di una useEffect
o useLayoutEffect
; ciò rende particolarmente importante occuparsi del clean-up degli effetti, per evitare che le animazioni mostrino comportamenti imprevisti.
Il comportamento imprevisto di uno scrolltrigger quando ci si dimentica di pulire gli effetti.
Per sperimentare le differenze tra React-Spring e GSAP ho deciso innanzitutto di costruire una semplicissima animazione in loop che si interrompesse al click sull’elemento in movimento e riprendesse con un secondo click. Da dove partire, quindi?
La documentazione
Ovviamente, il primo step è leggere la documentazione. React-Spring si presenta, a un primo sguardo, migliore da questo punto di vista; non solo la documentazione è esteticamente gradevole rispetto a quella fornita da GSAP, ma ogni sezione fornisce:
Spiegazione dell’hook
Esempio di utilizzo del codice
Esempio in TypeScript
Elenco di Codesandbox con varie animazioni da cui prendere esempio
In realtà, l’utilizzo di questa documentazione è però più complesso del previsto, per una serie di ragioni.
Innanzitutto, è difficile comprendere quale hook utilizzare se non li si conosce a priori, anche perché i nomi non sono particolarmente evocativi. Quindi, quando si ha in mente esattamente cosa si vorrebbe realizzare ma non come, la documentazione non aiuta granché. Poi, anche una volta individuato quale hook fa al caso nostro, mancano esempi basilari: i Codesandbox mostrano animazioni molto complesse, in cui spesso sono combinati hook diversi ed è difficile risalire all’utilizzo specifico dell’hook che ci interessa. Insomma, sembra che chi ha scritto la documentazione desiderasse più che altro mostrare i virtuosismi di React-Spring piuttosto che fornire un concreto aiuto agli utenti.
GSAP, dal canto suo, ha una documentazione discreta, anche se non eccellente. Anche in questo caso a volte risulta difficile trovare esattamente quello che si cerca, però quando finalmente si capita nella pagina giusta si possono trovare:
Video-tutorial per principianti, con spiegazioni dai casi base a quelli più complessi
Una spiegazione dettagliata di tutti gli attributi disponibili
L’incorporazione di Codepen con esempi dell’utilizzo
Inoltre, il vero punto forte di GSAP è la community. L’animazione non funziona come previsto? È necessario animare qualcosa in una maniera che non sembra prevista dalla documentazione? Qualcuno ha sicuramente già avuto gli stessi dubbi ed è molto probabile trovare una risposta senza dover cercare troppo, quasi sempre con annessi diversi Codepen che mostrano il problema e le soluzioni proposte dagli utenti esperti.
Qualità del risultato
Una volta letta la documentazione, procediamo nella realizzazione dell’animazione. A livello di codice, naturalmente React-Spring, nascendo specificatamente per React, rende molto facile realizzare in poche righe una buona animazione.
A questo punto è necessario fare una precisazione sul funzionamento di React-Spring. Uno dei pregi spesso decantati di React-Spring è la sua capacità di produrre animazioni molto belle con pochi semplici passi.
In questo caso, sono andata a modificare la configurazione default e, senza utilizzare i set predefiniti, ho modificato direttamente gli attributi dell’easing. Quest’ultimi possono sembrare un po’ strani: massa, tensione, frizione… Che vuol dire? I movimenti così naturali prodotti da React-Spring derivano proprio dall’utilizzo di principi della fisica per replicare un aspetto autentico.
Anche se questo permette di dover toccare poco o niente della configurazione di default, è facile accorgersi che questo presenta, purtroppo, anche dei lati negativi. Innanzitutto, che cosa vuol dire mass: 10
? Qual è la differenza tra 10, 100 o 1000? Qual è l’unità di misura? Non è chiaro, ma è in realtà probabilmente ininfluente, perché l’unica cosa che sembra contare, in realtà, è il rapporto tra i vari parametri.
Inoltre, un grandissimo punto a sfavore di questa configurazione è che assegnando all’animazione una durata si perdono gli attributi e l’easing diventa perfettamente lineare. Quindi, anche se da un lato si ha il vantaggio di costruire qualcosa di gradevole in pochissimi passi, dall’altro è complesso metterci mano e personalizzare l’aspetto finale.
Con GSAP, l’implementazione dell’animazione è meno leggibile, perché come anticipato è necessario farlo nella useEffect
. A differenza di React-Spring, dove dall’animazione si ottengono degli SpringValues da passare al componente animato, che peraltro deve essere dichiarato come tale, GSAP richiede di indicare quale componente animare tramite una ref oppure un id o un classname.
A parte la scelta discutibile dei nomi degli attributi (repeat: -1
indica un loop, mentre yoyo: true
è necessario per fare in modo che l’animazione si ripeta anche in direzione contraria), per poter avere il controllo sull’animazione, serve una reference all’animazione (o, in alternativa, all’interno di un contesto di GSAP è possibile darle un nome per controllarla).
In questo caso l’animazione a cui non è applicato alcun easing è piuttosto bruttina, al contrario di quella realizzata di default da React-Spring. È però possibile usufruire degli easing prestabiliti dalla libreria o anche definire un proprio easing custom. GSAP, infatti, all’interno della propria documentazione fornisce una piccola perla: un ease visualizer, facile da utilizzare, con cui costruire esattamente l’easing desiderato modificandone la curva a piacere, con la possibilità di copiarla poi nel proprio progetto. Inoltre, con questa libreria è possibile impostare una durata senza che questo intacchi l’aspetto dell’animazione.
Gli hook di React-Spring
Come già detto, React-Spring espone una serie di hook per diversi utilizzi. Il passaggio successivo della mia ricerca della libreria migliore è stato costruire una piccola animazione con ogni hook e poi replicarla con GSAP per osservare le differenze.
useSpring e useSprings
L’hook più utilizzato è sicuramente useSpring
, che la documentazione stessa definisce come il più adatto alla maggior parte dei casi d’uso. Se si vuole applicare una stessa spring a più elementi, invece, è più appropriato l’utilizzo di useSprings
.
In GSAP la stessa cosa può essere fatta mappando i componenti di interesse e assegnandoli a una ref (che quindi apparirà come un array di elementi) e poi per ciascun elemento, targettato tramite l’indice e la ref, creando una tween.
Sebbene lo stesso risultato sia ottenibile con entrambe le librerie, React-Spring vince per la semplicità di utilizzo, perché richiede di mappare gli elementi una sola volta, senza necessità di creare anche una ref.
useChain e useTrail
Questi due hook servono rispettivamente per concatenare animazioni diverse tra loro (ad esempio, una useSpring
e una useTransition
) e per eseguire delle animazioni su più elementi, in maniera simile a useSprings
ma automaticamente concatenate l’una dopo l’altra.
Perché parlare congiuntamente di questi due hook che apparentemente fanno due cose molto diverse? Perché GSAP, in questo caso, fornisce un unico strumento, gsap.timeline
, che consente di fare entrambe le cose. Nello specifico, la timeline di GSAP rende possibile gestire con la massima customizzazione la sequenza di animazioni sia su uno stesso elemento che su elementi diversi, specificando per ciascuna il momento nel quale si desidera farla iniziare.
In questo caso, dunque, GSAP spicca per la semplicità con cui porta a termine il compito e per la maggiore possibilità di personalizzare l’andamento delle sequenze animate.
useTransition
Questo hook è forse il più interessante (nonché il più utile) dopo useSpring
. La sua specialità è creare delle animazioni alla comparsa e alla scomparsa di un elemento. Purtroppo, la documentazione di React-Spring rende molto difficile comprendere esattamente come debba venir usato.
A partire da uno state che decreta la visibilità di un componente, l’hook restituisce un elemento style da applicare al componente in transizione. Anche se la sintassi non è tra le più comprensibili, è tutto sommato semplice creare questo tipo di animazione con React-Spring.
Per quanto invece riguarda GSAP, mentre non ci sono difficoltà nel creare un’animazione di ingresso di un componente, l’animazione in uscita può essere ottenuta creando un contesto per gestire l’animazione di scomparsa, che una volta completata (cioè onComplete
) imposta a falso lo state che gestisce la visibilità.
Take-home message fino a qua
Come abbiamo visto, gli hook di React-Spring forniscono strumenti specifici per certe animazioni, ad esempio con gruppi di elementi, in sequenza, per componenti che vengono aggiunti e rimossi dal DOM, ecc.
Questo approccio può essere apprezzabile perché più mirato, ma nel contempo rischia di rendere più complesso lo sviluppo delle animazioni, perché richiede di utilizzare sintassi e metodi diversi a seconda del tipo di animazione che si desidera. Inoltre, la possibilità di personalizzare l’animazione (ad esempio, nelle tempistiche) è piuttosto ridotta in molti degli hook.
Dall’altra parte, GSAP riesce a stare al passo con React-Spring, rendendo possibile la creazione delle stesse animazioni, ma utilizza una sintassi più generica e non direttamente legata al risultato desiderato. Una volta compresa la logica, è piuttosto facile adattarla a seconda dell’animazione che si desidera creare, ma richiede sicuramente più righe di codice per ottenere lo stesso risultato, riducendo anche la leggibilità. Inoltre, come già evidenziato per quanto riguarda le animazioni semplici, GSAP richiede qualche aggiustamento perché le animazioni appaiano fluide e naturali.
Le peculiarità di GSAP
Dopo aver esaminato nel dettaglio gli hook di React-Spring, ho pensato che per un confronto equo tra le due librerie fosse opportuno osservare anche le caratteristiche proprie di GSAP.
Questa libreria offre una gamma vastissima di funzioni, molte delle quali fornite tramite plugin. Alcuni plugin purtroppo sono riservati ai membri del Club GreenSock, ma quelli inclusi gratuitamente possono svolgere, tra le altre, le seguenti funzionalità:
Drag di elementi, su entrambi o su un solo asse, anche con snap a posizioni predefinite (ad esempio, su una cella di una griglia).
Spostamento di elementi lungo un path (ma per disegnarli gradualmente o per il morphing tra due SVG sono necessari plugin a pagamento).
Flip di elementi, ad esempio da un flex column a un flex row, o per ingrandire e spostare centralmente delle immagini quando cliccate da una griglia di immagini.
Animazioni sul testo, come scrittura lettera per lettera o comparsa di una parola alla volta. Anche in questo caso sono fornite ai membri premium dei tool per animazioni ancora più complesse, come la possibilità di mescolare e riordinare le lettere di parole, di far ondeggiare le righe del testo o di colorare una parola alla volta.
Gestione della transizione tra pagine diverse, con integrazione con react-router-dom.
Abbiamo poi già parlato di uno dei metodi più interessanti di GSAP, cioè la timeline, che consente di gestire perfettamente le tempistiche delle animazioni, non solo indicando una successione, ma anche quando un’animazione deve iniziare esattamente, ad esempio quando quella precedente è stata completata al 50% o anche impostando una label come marker nel tempo.
Infine, un’altra caratteristica interessante di GSAP è il context. L’utilizzo del contesto consente di tener traccia di tutte le animazioni, di racchiuderle in uno scope ed è disponibile anche un hook per memoizzarlo.
Grazie al context è possibile creare animazioni e chiamarle a piacere (ad esempio, al click), ma soprattutto semplifica il clean-up delle animazioni quando il componente viene smontato, perché è sufficiente chiamare il metodo .revert()
per cancellare tutte le animazioni.
La battaglia finale: animazioni complesse
A questo punto della mia ricerca non avevo ancora tratto alcuna conclusione su quale delle due librerie fosse migliore, perciò ho capito che il campo di battaglia doveva essere spostato ad un piano superiore. Ho quindi selezionato due prove complesse, da affrontare con entrambi gli strumenti per stabilire il vincitore.
L’interpolazione
Poter interpolare dei valori consente di fare praticamente qualsiasi cosa. Entrambe le librerie offrono la possibilità di farlo, chiaramente, ma è interessante vedere come.
In React-Spring l’interpolazione è semplicissima: i valori restituiti dalla useSpring
possono essere interpolati e passati al componente. La sintassi è abbastanza intuitiva e, sfruttando la possibilità di controllare la durata totale dell’animazione, è possibile gestire il cambiamento dei valori in ogni momento.
In questo caso il valore di value viene utilizzato per fare in modo che il componente Element compia una rotazione di 180° quando l’animazione è al 10% e raggiunga la rotazione completa al 100%, cioè dopo 3 secondi.
In GSAP, invece, interpolare i valori in maniera non lineare è complesso e per ottenere lo stesso effetto è preferibile utilizzare dei keyframe, ciascuno con la propria durata, perdendo però la possibilità di gestire la durata totale dell’animazione. Inoltre, questa soluzione rende difficile la gestione simultanea di più caratteristiche.
Per questa ragione, sebbene alla fine sia riuscita a ottenere la stessa animazione da entrambe le librerie, React-Spring vince di misura per quanto riguarda l’interpolazione dei valori, sia per la semplicità di utilizzo che per la facilità con cui può essere gestita la configurazione.
Animazioni in parallasse
Le animazioni triggerate dallo scroll, invece, sono il motivo per cui in primis ho scoperto dell’esistenza di GSAP. Questa libreria, infatti, ha reso semplicissimo realizzare un’animazione in parallasse, con ampia possibilità di personalizzazione e una serie di aspetti positivi, che includono:
Possibilità di muovere elementi in un momento specifico dello scroll (ad esempio, quando il centro di questi elementi è al centro dello schermo, oppure appena entrano in vista).
Possibilità di muovere un elemento A a seconda della posizione di un elemento B; il trigger può essere separato rispetto all’elemento animato.
Gestione di animazioni triggerate dallo scroll, ma anche che seguono l’andamento di quest’ultimo (e quindi proseguono man mano che si scrolla la pagina e si animano al contrario quando si torna all’inizio della pagina), grazie all’opzione di
scrub
. Inoltre, applicando lo scrub è anche possibile ottenere un effetto di movimento più naturale, leggermente ritardato rispetto al suo trigger.Opzione di
pin
, che consente di ancorare un elemento sullo schermo, muovendo invece tutto il resto (ad esempio, un elemento può essere fissato al centro dello schermo, mentre gli altri elementi continuano a scrollare).Possibilità di ottenere un indice del progresso dell’animazione, da sfruttare per ottenere effetti collaterali basati sullo scroll (ad esempio, modificare uno state quando l’animazione è a metà del suo compimento).
La parte migliore di tutte queste possibilità è che è semplicissimo comprendere come usarle, anche perché GSAP mantiene una certa coerenza nella sintassi, a prescindere dal tipo di animazione che si intende creare.
Quando l’elemento con classname “a” arriva in alto sullo schermo, il componente con classname “box” inizia a spostarsi orizzontalmente, finché la parte inferiore di “a” non raggiunge un’altezza di 10% dalla parte superiore della schermata. Se si scrolla verso l’alto, “box” ritorna alla sua posizione originaria.
Per quanto riguarda React-Spring, ho sperimentato svariate delle opzioni che offre per cercare di ricreare quanto avevo già realizzato con GSAP.
React-Spring mette a disposizione un hook dal nome di useInView
, che -come si intuisce dal nome- può essere usato per animare un componente nel momento in cui questo diviene visibile sullo schermo. Anche se l’utilizzo è molto semplice, il caso d’uso è ovviamente piuttosto limitato.
Un secondo hook offerto dalla libreria è useScroll
. Questo hook consente di ottenere un valore associato al progresso dello scroll e usarlo per animare tramite il controller di una semplice useSpring
. Il limite dell’hook, purtroppo, è che sembra che il progresso sia sempre riferito alla pagina nel suo complesso, rendendolo quindi difficilmente controllabile.
Esistono infine due componenti, Parallax
e ParallaxLayer
, che sono rispettivamente un container scrollabile e un wrapper per gli elementi che si vuole scrollino all’interno. Sebbene molto promettente, questa modalità per realizzare un’animazione in parallasse sembra interferire con lo scroll naturale della pagina. Purtroppo, la documentazione di React-Spring a riguardo è molto scarna e provvede solamente tre esempi. Inoltre, la possibilità di personalizzare lo scroll è molto scarsa.
In conclusione, per quanto riguarda le animazioni allo scroll, GSAP sembra offrire strumenti migliori rispetto a React-Spring, sia per la semplicità di utilizzo, sia per la versatilità del suo ScrollTrigger
.
Chi vince in conclusione
Arrivata alla fine, è comunque difficile trarre delle conclusioni su quale libreria sia migliore, perciò è impossibile dichiarare un vincitore assoluto. Si possono però stabilire dei casi nei quali una delle due librerie si rivela più semplice da utilizzare o dà dei risultati migliori rispetto all’altra.
In particolare, React-Spring è consigliabile quando si intendono realizzare:
Animazioni relativamente semplici (cioè che non contengano sequenze di movimenti), perché un punto di forza di React-Spring è la sua capacità di creare facilmente animazioni gradevoli.
Animazioni che richiedono di interpolare valori, anche non linearmente.
GSAP, d’altro canto, sembra più adatto per la creazione di:
Animazioni che richiedono più movimenti in sequenza o una gestione precisa del tempo, che può essere ottenuta grazie alla timeline.
Animazioni attivate dallo scroll, anche altamente personalizzate, grazie all’aiuto del plugin ScrollTrigger.
In ogni caso, ogni animazione può essere replicata con entrambe le librerie, pertanto non c’è una scelta giusta o sbagliata, ma al limite più o meno conveniente a seconda della casistica. È quindi possibile decidere sulla base delle proprie preferenze o, eventualmente, a seconda di quale delle due è più familiare.