Animare la tua app nativa React

L'animazione è una parte importante della progettazione dell'esperienza utente. Serve come feedback sulle azioni dell'utente, informa gli utenti sullo stato del sistema e li guida su come interagire con l'interfaccia. 

Uno degli strumenti che sto usando per creare app mobili multipiattaforma è React Native, quindi in questo tutorial ti illustrerò come implementare le animazioni in questa piattaforma. L'output finale di questo tutorial sarà un'app di cucina che implementa diversi tipi di animazioni. Ecco come apparirà:

Partirò dal presupposto che tu conosca già le basi per lavorare con React Native, quindi non approfondirò troppo il codice che non ha nulla a che fare con le animazioni. Per ulteriori informazioni su React Native, consulta alcuni dei miei altri tutorial.

Lavoreremo specificamente sulla piattaforma Android, ma il codice utilizzato in questo tutorial dovrebbe funzionare anche su iOS. Infatti, se non vuoi affrontare il problema di creare un nuovo progetto React Native, ti consiglio di consultare React Native Web Starter. Ciò consente di creare un nuovo progetto React Native che è possibile visualizzare in anteprima nel browser. Ciò si presenta con il vantaggio di non dover configurare un dispositivo e un ricaricamento rapido più veloce, in modo da poter visualizzare in anteprima le modifiche più rapidamente.

La tua prima app di animazione

Se non lo hai già fatto, crea un nuovo progetto React Native:

RNAnimation init native-reactive

Se stai usando React Native Web Starter, ecco come si crea un nuovo progetto:

git clone https://github.com/grabcode/react-native-web-starter.git RNAnimation cd RNAnimation rm -rf .git npm install

Apri il index.android.js (o index.web.js), Rimuovere il codice predefinito e aggiungere quanto segue:

import React, Component da 'react'; importare App da './app/components/App'; import AppRegistry, View da 'react-native'; la classe RNAnimation estende Component render () return (    );  AppRegistry.registerComponent ('RNAnimation', () => RNAnimation);

Se si utilizza React Native per Web, è possibile saltare il passaggio precedente poiché il codice predefinito è già impostato per utilizzare il App componente.

Creare un app / componenti cartella e dentro creare un App.js file. Questo sarà il file principale con cui lavoreremo. Una volta creato il file, puoi procedere e importare i pacchetti necessari per l'intero progetto.

import React, Component da 'react'; import Platform, StyleSheet, Text, ScrollView, View, Image, TouchableHighlight, Switch, Dimensions, Animated, Easing, LayoutAnimation, UIManager da 'react-native';

Se hai già fatto qualche tipo di sviluppo React Native, dovresti già avere molta familiarità con i seguenti componenti. In caso contrario, dai un'occhiata ai documenti React Native API.

Platform, StyleSheet, Text, ScrollView, View, Image, TouchableHighlight, Switch, Dimensions,

Questi sono i pacchetti che sono usati specificamente per implementare le animazioni:

Animato, Easing, LayoutAnimation, UIManager

Ecco una breve panoramica di ciascuno:

  • Animato: ci consente di creare componenti animati. React Native ha una netta separazione tra componenti animati e statici. In particolare, puoi creare viste animate (), testo () e immagini ().
  • Facilitare: un contenitore generale di valori costanti per facilitare le animazioni. 
  • LayoutAnimation: per eseguire diversi tipi di animazioni ogni volta che il layout cambia (ad es. quando lo stato viene aggiornato).
  • UIManager: attualmente, LayoutAnimation è ancora una funzionalità sperimentale su Android. L'importazione di UIManager ci consente di abilitarlo. Per iOS, LayoutAnimation funziona per impostazione predefinita, quindi non è necessario importare UIManager.

Ruota animazione

Il primo passo nella creazione di un'animazione consiste nel definire un valore animato. Questo è comunemente fatto all'interno del costruttore di componenti. Nel codice qui sotto, stiamo definendo un nuovo valore animato per il App costruttore di componenti. Si noti che il nome di questo valore può essere qualsiasi cosa purché descriva l'animazione che si desidera creare. 

In React Native, puoi creare un nuovo valore animato chiamando il Valore() metodo nel Animato classe. Quindi fornire il valore animato iniziale come argomento. 

costruttore (oggetti di scena) super (oggetti di scena); this.spinValue = new Animated.Value (0); 

Quindi, crea la funzione che eseguirà l'animazione di rotazione. 

Sulla prima riga, dobbiamo impostare il valore iniziale del valore animato con cui vogliamo lavorare. In questo caso, lo stiamo impostando su 0. 

Successivamente, creiamo una nuova animazione temporale chiamando il Animated.timing () funzione. Questo accetta il valore animato corrente come primo argomento e un oggetto contenente la configurazione dell'animazione come secondo. L'oggetto dovrebbe contenere il valore finale per il valore animato, la durata (in millisecondi) e il tipo di animazione di andamento. 

Infine, chiama il inizio() metodo per avviare l'animazione.

spin () this.spinValue.setValue (0); Animated.timing (this.spinValue, toValue: 1, duration: 1500, easing: Easing.linear) .start (); 

Il passo finale è implementare effettivamente l'animazione. Dentro il tuo render () metodo, definire come verrà modificato il valore di rotazione. Questo può essere fatto chiamando il interpolare() funzione. Accetta un oggetto contenente un inputRange e outputRange. inputRange è una matrice contenente il valore di rotazione iniziale e finale. outputRange è una matrice contenente i valori di rotazione effettivi. 

Quindi inizialmente l'oggetto da animare sarà a 0 gradi di rotazione e il valore finale sarà 360 gradi. Questa rotazione viene eseguita nel corso di 1.500 millisecondi, come definito in precedenza nella configurazione dell'animazione.

const spin = this.spinValue.interpolate (inputRange: [0, 1], outputRange: ['0deg', '360deg']); 

Quando si esegue il rendering del componente, il valore di rotazione viene aggiunto come trasformazione negli stili. Quindi, se hai familiarità con le animazioni CSS, questa è l'implementazione equivalente in React Native.

ritorno (  );

Ora che conosci le basi della creazione di animazioni, creiamo alcune altre in modo da sapere come implementare diversi tipi. Dentro il tuo costruttore(), crea un oggetto contenente le animazioni che implementeremo:

var animations = [animation: 'spin', enabled: falso, animazione: 'scala', abilitato: falso, animazione: 'opacità', abilitato: falso, animazione: 'colorChange', abilitato: falso , animazione: 'parallelTranslateX', abilitato: false];

Non ti preoccupare se non sai quello che ognuno fa - ti farò camminare attraverso tutti loro. Tutto quello che devi sapere per ora è che questa configurazione indica se un'animazione è attualmente abilitata o meno. Una volta inizializzato, aggiungi il animazioni array allo stato:

this.state = animations: animations;

Nel tuo render () funzione, aggiungi i componenti che animeremo e l'elenco delle animazioni.

ritorno (     This.renderAnimationsList ()     );

Il renderAnimationsList () funzione esegue il rendering dell'elenco di animazioni utilizzando Interruttore e Testo componenti. 

renderAnimationsList () return this.state.animations.map ((item) => return (   this.toggleAnimation (item.animation, value) style = styles.switch value = item.enabled />  Item.animation  ); ); 

Interruttore consente all'utente di attivare e disattivare le animazioni. Ogni volta che l'utente capovolge l'interruttore, il toggleAnimation () la funzione viene eseguita. Tutto ciò che fa è trovare l'animazione in questione e aggiornare il valore del abilitato proprietà al valore selezionato. Quindi aggiorna lo stato con i valori aggiornati e scorre attraverso tutte le animazioni, eseguendo solo quelle abilitate.

toggleAnimation (animation, value) var animations = this.state.animations; var index = animations.findIndex ((obj) => return obj.animation == animation;); animazioni [indice] .enabled = valore; this.setState (animazioni: animazioni); animations.forEach ((item) => if (item.enabled) this [item.animation] ();); 

Aggiungi anche gli stili che verranno utilizzati in tutta l'app.

const styles = StyleSheet.create (container: flex: 1, alignItems: 'center', flexDirection: 'column', button: height: 40, backgroundColor: '#eee', justifyContent: 'center', alignItems: 'center', marginTop: 10, item: flex: 2, flexDirection: 'row', height: 50,, opzione: marginBottom: 10, animation_type: marginLeft: 10, spinner: marginTop: 20 , alignSelf: 'center', width: 50, height: 50, box: width: 50, height: 50, zIndex: 100, red_box: backgroundColor: 'red', marginBottom: 20, blue_box: alignSelf : 'flex-start', backgroundColor: 'blue', green_box: alignSelf: 'flex-end', backgroundColor: 'green', squares_container: flexDirection: 'row', flex: 1, flexWrap: 'wrap' , square: width: 35, height: 35, backgroundColor: 'lightblue', margin: 10, number: fontSize: 20, fontWeight: 'bold');

Animazione in scala

L'animazione in scala è dove si realizza un oggetto più grande o più piccolo della sua dimensione originale. Inizia creando un nuovo valore animato all'interno del costruttore:

this.scaleValue = new Animated.Value (0);

Crea la funzione per animare la scala. Questo sembra simile al rotazione() funzione; l'unica differenza è la funzione di andamento che stiamo usando. Qui stiamo usando easeOutBack per rendere il ridimensionamento più fluido. Questo è utile specialmente se questa animazione è eseguita ripetutamente. Se vuoi sapere quali altre funzioni di easing puoi usare, consulta easings.net. Tutte le espansioni elencate qui possono essere utilizzate in React Native.

scale () this.scaleValue.setValue (0); Animated.timing (this.scaleValue, toValue: 1, duration: 1500, easing: Easing.easeOutBack) .start (() => if (this.state.animations [0] .enabled) this.scale ( );); 

L'altra cosa che è nuova nella funzione sopra è che stiamo passando una funzione come argomento per il inizio() funzione. Questa funzione viene eseguita quando l'animazione è terminata. Qui stiamo controllando se l'animazione è abilitata, e se lo è, chiamiamo di nuovo la stessa funzione. Questo ci permette di eseguire l'animazione ripetutamente fintanto che è abilitata.

() => if (this.state.animations [0] .enabled) this.scale (); 

Quindi, nel tuo render () funzione, configurare l'interpolazione di ridimensionamento. Questa volta, abbiamo tre valori per l'intervallo di input e output per creare un effetto pulsante, come un battito cardiaco. Questo ci consente di creare un'animazione di scala che non rende improvvisamente un oggetto più grande o più piccolo. Il range di output più alto è 7, quindi l'oggetto sarà sette volte più grande della sua dimensione originale.

const nearFar = this.scaleValue.interpolate (inputRange: [0, 0.5, 1], outputRange: [1, 7, 1]);

Per risparmiare spazio, basta aggiungere il scala trasformare sullo stesso componente che abbiamo usato in precedenza:

Con queste due trasformazioni aggiunte, ora puoi abilitare sia l'animazione di spin che quella di scala per eseguirle allo stesso tempo.

Ormai dovresti aver notato gli schemi che ci permettono di creare animazioni. Un sacco di codice viene ripetuto durante le animazioni. La migliore pratica sarebbe quella di creare funzioni che incapsulino codice ripetuto, ma per mantenere le cose semplici e facili da capire, restiamo con il codice raw per il resto delle animazioni.

Animazione opacità

Ora proviamo ad animare l'opacità di un componente. A questo punto dovresti avere una certa familiarità con il punto in cui ogni pezzo di codice va, quindi non ho più intenzione di menzionare dove collocherai ognuno di essi. Ma nel caso in cui ti confondi, puoi semplicemente guardare il codice su GitHub:

this.opacityValue = new Animated.Value (0);

Crea una funzione per cambiare l'opacità. Quando si modifica l'opacità, una funzione di andamento lineare è la migliore in quanto è la più semplice.

opacity () this.opacityValue.setValue (0); Animated.timing (this.opacityValue, toValue: 1, duration: 3000, easing: Easing.linear) .start (() => if (this.state.animations [2] .enabled) this.opacity ( );); 

Cambia l'opacità da visibile a trasparente e poi di nuovo visibile nel corso di tre secondi.

const opacity = this.opacityValue.interpolate (inputRange: [0, 0.5, 1], outputRange: [1, 0, 1]);

Crea un nuovo componente la cui opacità sarà controllata:

Valore del colore

Successivamente, proviamo ad animare il colore di sfondo di un componente:

this.colorValue = new Animated.Value (0);

Questa volta, ci stiamo animando nel corso di cinque secondi:

colorChange () this.colorValue.setValue (0); Animated.timing (this.colorValue, toValue: 100, duration: 5000). Start (() => if (this.state.animations [3] .enabled) this.colorChange ();); 

Abbiamo tre colori con cui lavorare. Il colore iniziale è giallo, quindi dopo pochi secondi diventerà completamente arancione e quindi rosso. Si noti che i colori non cambieranno bruscamente; verranno visualizzati anche tutti i colori tra i colori che hai specificato. React Native calcola automaticamente i valori del colore tra quelli specificati. Puoi allungare la durata se vuoi vedere come cambia il colore nel tempo. 

const colorAnimation = this.colorValue.interpolate (inputRange: [0, 50, 100], outputRange: ['yellow', 'orange', 'red']);

Proprio come l'opacità, il valore interpolato viene aggiunto come stile:

Animazioni parallele

Puoi dire che abbiamo già eseguito animazioni in parallelo. Ma questo è solo un effetto collaterale di avere diverse trasformazioni collegate a un singolo componente. Se si desidera eseguire più animazioni contemporaneamente, è necessario utilizzare parallelo() funzione dall'API animata. Questo accetta una serie di funzioni di animazione da eseguire. Nell'esempio seguente, abbiamo due valori animati, uno per ciascun componente che vogliamo animare.

this.blue_box_X = new Animated.Value (0); this.green_box_X = new Animated.Value (0); 

Nella funzione di animazione, impostiamo i valori animati iniziali come al solito. Ma sotto, stiamo usando Animated.parallel () per raggruppare tutte le animazioni che vogliamo eseguire. In questo caso, abbiamo solo due animazioni temporali, che vengono eseguite per due secondi. Notare anche che non stiamo chiamando il inizio() metodo su ogni animazione. Invece, lo stiamo usando dopo aver dichiarato l'animazione parallela. Questo ci permette di avviare le animazioni contemporaneamente. 

parallelTranslateX () this.blue_box_X.setValue (0); this.green_box_X.setValue (0); Animated.parallel ([Animated.timing (this.blue_box_X, toValue: 1, durata: 2000, easing: Easing.linear), Animated.timing (this.green_box_X, toValue: 1, durata: 2000, easing: Easing .linear)]). start (() => if (this.state.animations [4] .enabled) this.parallelTranslateX ();); 

Perché l'interpolazione abbia senso, per prima cosa controlla lo stile che abbiamo aggiunto per i due riquadri in precedenza:

blue_box: alignSelf: 'flex-start', backgroundColor: 'blue', green_box: alignSelf: 'flex-end', backgroundColor: 'green',

La scatola blu è allineata usando flex-start, il che significa che è allineato a sinistra. La scatola verde è flex-end, che è allineato a destra. (Almeno, questo è come funzionano se il contenitore ha un flexDirection di colonna. Altrimenti, è una storia diversa.) 

Con questa conoscenza, ora possiamo spostare le scatole ovunque vogliamo. Ma per questo tutorial, tutto ciò che vogliamo fare è spostare le caselle al contrario delle loro posizioni iniziali. Quindi la casella blu si sposta a destra e la casella verde si sposta a sinistra. Qui è dove arrivano i dati sulle dimensioni del dispositivo. Usiamo il larghezza del dispositivo per calcolare il valore di interpolazione finale in modo che la casella non vada fuori limite. 

var width = Dimensions.get ('window');

In questo caso, stiamo semplicemente sottraendo 50 dalla larghezza del dispositivo per rendere la casella blu andare a destra. E per la scatola verde, stiamo convertendo la larghezza del dispositivo al suo equivalente negativo, quindi si sposta a sinistra. Ti starai chiedendo, perché 50? Questo perché la dimensione di ogni scatola è 50. La scatola andrà ancora fuori dai limiti se non sottraiamo la sua dimensione dalla larghezza del dispositivo. 

const blue_box_translateX = this.blue_box_X.interpolate (inputRange: [0, 1], outputRange: [0, width - 50],); const green_box_translateX = this.green_box_X.interpolate (inputRange: [0, 1], outputRange: [0, -width + 50],);

Infine, aggiungi i componenti da animare. La trasformazione in questione è translateX, che ci consente di modificare la posizione di un oggetto sull'asse X per spostarlo orizzontalmente.

 

A parte le animazioni parallele, ci sono anche le animazioni di sequenza e scaglionate. 

L'implementazione di questi è simile alle animazioni parallele nel senso che accettano tutte una serie di animazioni da eseguire. Ma il fattore determinante per le animazioni di sequenza è che le animazioni fornite nell'array verranno eseguite in sequenza. Puoi anche aggiungere ritardi facoltativi a ciascuna animazione, se lo desideri. 

D'altra parte, un'animazione scaglionata è una combinazione di animazioni parallele e in sequenza. Questo perché ti permette di eseguire animazioni sia in parallelo che in sequenza. Ecco una penna che dimostra animazioni barcollanti.

Animazione del layout

È un altro strumento che React Native fornisce per l'implementazione delle animazioni LayoutAnimation. Ciò consente di animare le visualizzazioni nelle loro nuove posizioni quando si verifica il layout successivo. Le modifiche al layout di solito si verificano quando si aggiorna lo stato. Ciò comporta l'aggiunta, l'aggiornamento o la rimozione di uno specifico componente dell'interfaccia utente dallo schermo. 

Quando accadono questi eventi, LayoutAnimation si occupa di animare il componente interessato. Ad esempio, in un'app Elenco cose da fare, quando aggiungi un nuovo elemento to-do, verrà automaticamente aggiunta un'animazione primaverile per far nascere il nuovo oggetto.

Aggiungiamo a LayoutAnimation nell'app del lavello della cucina. Come accennato in precedenza, dovrai importare LayoutAnimationpiattaforma, e UIManager nell'app. Quindi, nel tuo costruttore(), aggiungi il codice per l'abilitazione LayoutAnimation su Android:

if (Platform.OS === 'android') UIManager.setLayoutAnimationEnabledExperimental (true); 

(Su iOS, LayoutAnimation dovrebbe funzionare di default Se utilizzi React Native per il Web, LayoutAnimation non è supportato, quindi dovrai esportare l'app su Android o iOS e provarla da lì.)

Avanti, proprio sotto il ScrollView che contiene l'elenco delle animazioni, aggiungi un pulsante per generare i quadrati che verranno mostrati sullo schermo:

  Aggiungi quadrati  

Fondamentalmente ciò che farà sarà generare tre piccoli quadrati ogni volta che l'utente tocca Aggiungi quadrati pulsante. 

Ecco la funzione per aggiungere quadrati:

addSquares () LayoutAnimation.configureNext (LayoutAnimation.Presets.spring); var squares = this.state.squares; this.setState (squares: squares + 3); 

L'idea è di chiamare il LayoutAnimation.configureNext () prima di aggiornare lo stato. Questo accetta l'animazione che si desidera utilizzare. Fuori dalla scatola, LayoutAnimation viene fornito con tre preset: lineareprimavera, e easeInEaseOut. Questi dovrebbero funzionare per la maggior parte dei casi, ma se hai bisogno di personalizzare le animazioni, puoi leggere la documentazione su LayoutAnimation per imparare come creare il tuo.

Dentro il render () funzione, crea a per loop che renderà i quadrati. Il numero di quadrati da generare dipende dal valore corrente di piazze nello stato.

var squares = []; per (var i = 0; i < this.state.squares; i++) squares.push(this.renderSquare(i)); 

Il renderSquare () la funzione è quella che sta effettivamente rendendo i quadrati:

renderSquare (chiave) ritorno (  ); 

Librerie di terze parti

L'API animata di React Native è molto robusta e personalizzabile, ma come hai visto finora, questo ha lo svantaggio di dover scrivere molto codice solo per implementare animazioni molto semplici. Quindi in questa sezione finale, ti presenterò due librerie di terze parti che ti permetteranno di implementare animazioni comuni con meno codice.

Numeri animati

Se stai creando un'app che ha bisogno di animare i numeri (ad esempio un cronometro o un'app contatore), puoi usare il built-in setInterval () funzione per aggiornare lo stato su un intervallo impostato e quindi implementare autonomamente le animazioni. 

O se vuoi, puoi usare la libreria dei numeri animati. Ciò consente di implementare facilmente animazioni numeriche, ad esempio la personalizzazione della transizione ogni volta che il numero viene aggiornato. Puoi installarlo con il seguente comando:

npm installa react-native-animate-number --save

Una volta installato, importalo nella tua app:

import AnimateNumber da 'react-native-animate-number';

Quindi utilizzalo come componente:

Che cosa fa il codice precedente conta fino a 100 a partire da 0. 

Animazioni generiche

Se si desidera implementare animazioni generiche come quelle offerte dalla libreria animate.css, esiste una libreria equivalente per React Native chiamata Animatable. Puoi installarlo con il seguente comando:

npm install react-native-animatable --save

Una volta installato, importalo con il seguente codice:

import * come Animable da 'react-native-animatable';

Ecco un esempio che utilizza il codice che abbiamo aggiunto in precedenza per la nostra animazione di layout. Tutto quello che devi fare è usare  invece di  e quindi aggiungere un arbitro quindi possiamo fare riferimento a questo componente utilizzando il codice JavaScript.

 piazze 

Quindi, crea un resetSquares () metodo. Questo rimuoverà tutti i quadrati che sono attualmente sullo schermo. Uso this.refs.squares fare riferimento al contenitore dei quadrati e quindi chiamare il zoomOutUp () funzione per animarlo fuori dalla vista usando un'animazione con zoom indietro con la direzione verso l'alto. E non dimenticare di aggiornare lo stato dopo che l'animazione è stata completata. Questo è un modello comune quando si implementano le animazioni. Fai l'animazione prima di aggiornare lo stato.

resetSquares () this.refs.squares.zoomOutUp (1500) .then (() => this.setState (squares: 0);); 

Lo stesso vale per il addSquares () metodo. Ma questa volta, stiamo animando il contenitore dei riquadri. E invece di eseguire prima l'animazione, lo facciamo subito dopo che lo stato è stato aggiornato. Questo perché il contenitore dei quadrati non viene realmente visualizzato a meno che non abbia un figlio. Quindi qui stiamo infrangendo la regola che l'animazione dovrebbe essere eseguita per prima.

addSquares () LayoutAnimation.configureNext (LayoutAnimation.Presets.spring); var squares = this.state.squares; if (squares == 0) this.setState (squares: squares + 3, () => this.refs.squares.zoomInDown (1););  else this.setState (squares: squares + 3); 

Conclusione

Questo è tutto! In questo articolo, hai imparato le basi della creazione di animazioni in React Native. Le animazioni possono essere implementate utilizzando l'API animata, LayoutAnimations, e librerie di terze parti. 

Come avete visto, la creazione di animazioni può richiedere una notevole quantità di codice, anche per quelli semplici come un'animazione di ridimensionamento. Questo ha il vantaggio di permetterti di personalizzare le animazioni come preferisci. 

Tuttavia, se non vuoi gestire troppi codici, puoi sempre utilizzare librerie React Native di terze parti create appositamente per implementare facilmente le animazioni. Puoi trovare il codice sorgente completo utilizzato in questo tutorial su GitHub.

Ulteriori letture

  • Reagire alle animazioni native usando l'API animata: una guida per principianti sull'implementazione di diversi tipi di animazioni in React Native. Questo tutorial copre le animazioni di sequenze e scaglioni se vuoi saperne di più su di loro.
  • Reagire libro di animazione nativo: ancora un work in progress ma comunque una risorsa preziosa. Ha quasi tutto ciò che vuoi sapere sulle animazioni in React Native, ad esempio, se vuoi animare qualcosa sullo scroll dell'utente, o se vuoi trascinare oggetti in giro.
  • Reagire documenti nativi - animazioni: se vuoi conoscere i dettagli specifici su come implementare le animazioni in React Native.
  • Animazione in Mobile UX Design: non esattamente correlato a React Native, ma all'animazione app mobile in generale. Questa è una buona lettura per i progettisti e gli sviluppatori di UX, per avere un'idea generale su come mostrare animazioni significative agli utenti.

Infine, se vuoi saperne di più sull'animazione CSS, dai un'occhiata ad alcuni dei nostri corsi video.