Animações em React Native

O mundo em que a gente vive é dinâmico. Mas, em muitos websites, várias interações são instantâneas e elementos somem e aparecem ou mudam de uma forma completamente artificial e nada convincente. Para melhorar essa experiência, nós, como desenvolvedores, precisamos usar animações, e, neste post, vamos ver algumas opções de como fazer isso em React Native.
Murilo Campos Pereira | 21 de outubro de 2021

O mundo em que a gente vive é dinâmico. Nele, tudo está constantemente em movimento. Os ônibus não se teletransportam de ponto a ponto, tempestades não surgem do nada e o café não aparece magicamente após alguém apertar o botão de uma máquina. Mas, em muitos websites, várias interações são instantâneas. Nestes casos, quando clicamos em um botão ou em um link, elementos somem e aparecem ou mudam de uma forma completamente artificial e nada convincente. Para melhorar essa experiência, nós, como desenvolvedores, precisamos usar animações, e, neste post, vamos ver algumas opções de como fazer essas animações em React Native.

O projeto

Vamos utilizar um projeto básico que simplesmente lista alguns repositórios do GitHub de uma linguagem escolhida. Vou adicionar um link com o código de tudo no final. O nosso foco será dado somente à implementação das animações.

Lottie

O primeiro tipo de implementação de animação que vamos ver é o mais simples, ou seja, vamos usar alguma já pronta (desenvolvida por alguém do seu time de design ou da internet). Existem várias animações incríveis (gratuitas e pagas) no site do lottie, e designers podem exportar animações do AfterEffects utilizando a extensão Bodymovin. Para executar o processo no seu projeto, basta instalar a lib wrapper lottie-react-native. Tudo o que precisamos fazer é importar a animação em JSON e passar para o componente.

BlogPost_21.10.2021_React Native animation_1.png

Temos uma ref para poder controlar programaticamente a animação, a fonte (o json) e absoluteFill para cobrir o fundo da tela inteiro; por fim, queremos que a animação esteja pausada enquanto o usuário não tiver escolhido alguma linguagem.

Animated API

A segunda opção é usar a API de animações que já vem com o React Native. O downside dela é que as animações são executadas na thread do JavaScript, e em dispositivos de baixa performance podem ocorrer quedas de frames e lentidão. Vamos animar nossa lista utilizando a Animated API e deixá-la com uma cara de Star Wars.

Agora, vamos pensar nos passos que precisamos seguir para alcançarmos este resultado. Nós precisamos:

  • Pegar o valor atual do scroll vertical da tela;

  • A partir deste valor, atuar na escala e opacidade do item que estiver chegando ao topo da tela.

Então, vamos para o código. Por partes, porque temos muito trabalho pela frente. Primeiro, vamos dar um jeito de pegar o valor do scroll vertical e guardar este valor em algum lugar. Se eu quero manter um valor sem precisar alterá-lo, o indicado é utilizar o hook useRef; para pegar o evento de scroll, precisamos transformar nossa FlatList em uma Animated.FlatList.

BlogPost_21.10.2021_React Native animation_2.png

O próximo passo é determinar uma input range, parecido com o que podemos também fazer com transições em CSS. Para isso, vamos ter que calcular o tamanho de cada item da lista, então eu extraí algumas constantes para nos ajudar.

BlogPost_21.10.2021_React Native animation_3.png

Interpolando com este input range, estou dizendo: mantenha a escala até o primeiro item da lista chegar ao topo da tela (para evitar bugs com bounce, como quando você tenta subir mais na lista, mas já está no topo dela no iOS e a lista ainda vai um pouco para baixo) e interpole a escala até zero desse ponto até o segundo item da lista chegar ao topo. Vamos fazer o mesmo com a opacidade.

BlogPost_21.10.2021_React Native animation_4.png

Agora, resta transformarmos a View de cada item da lista em uma Animated.View e passarmos estas interpolações.

BlogPost_21.10.2021_React Native animation_5.png

Pronto! Utilizando a Animated API, temos a transição de escala e opacidade que queríamos. Agora, vamos ver como implementar a paginação arrastando para os lados (como visto no primeiro vídeo) utilizando uma biblioteca um pouco mais robusta.

Reanimated

Como dito anteriormente, uma grande limitação da Animated API é que as animações são executadas na JS thread, o que pode acarretar problemas de performance. Nesses casos, a react-native-reanimated é uma boa solução para implementar animações executadas na native thread. Este é um assunto bem complexo no qual não vamos nos aprofundar, mas sim fazer um overview da implementação da solução.

Os passos seguem parecidos com os do caso anterior. Ou seja, queremos saber o valor do scroll horizontal da View e fazer alguma ação baseada nesse valor. Para guardarmos esses valores e trabalharmos com eles, teremos que usar hooks exportados da própria biblioteca (lembre-se que tudo está sendo executado na thread nativa, fora do contexto do JavaScript).

BlogPost_21.10.2021_React Native animation_6.png

Para acompanharmos qualquer toque na tela (panning) com a biblioteca reanimated, precisamos de uma Animated.View dentro de um PanGestureHandler; logo, vamos colocar toda a lista que implementamos no passo anterior dentro desse Handler.

BlogPost_21.10.2021_React Native animation_7.png

O activeOffset configura o mínimo de swipe para considerar uma troca de página. Vamos ver a implementação da resposta do evento e do estilo declarado logo abaixo. Os dois também dependem de hooks exportados pela biblioteca.

BlogPost_21.10.2021_React Native animation_8.png

Primeiro, vamos definir uns “pontos de parada” da nossa animação, que, nesse caso da paginação, seria a largura da tela para direita e esquerda e zero para a transição parar no meio da tela. Agora, vamos fazer o handler da ação de deslizar.

BlogPost_21.10.2021_React Native animation_9.png

O hook exporta um contexto para todos os métodos. A partir desse contexto, conseguimos ver o valor atual do offset (quanto o usuário deslizou o dedo para a direita ou esquerda).

  • No começo do evento (usuário toca na tela): Atribuímos o valor da variável translateX que definimos anteriormente (inicia em zero) ao offset da animação.

  • Evento ativo (usuário deslizando o dedo na tela): Somamos o scroll do eixo X ao offset da animação (traslado da lista para a direita ou esquerda).

  • Final do evento (usuário solta o dedo da tela): Fazemos uma transição interpolando os snapPoints declarados anteriormente, igual fizemos com a opacidade e escala.

Agora, é só aplicar esta nossa transição a um transform da View. Este é o estilo que está aplicado na Animated.View que vimos.

BlogPost_21.10.2021_React Native animation_10.png

Conclusão

Animações em React Native é um tema fascinante e complexo. Muitas vezes ficamos com medo de tentar adicionar algumas por achar difícil e que não vai fazer tanta diferença, mas animações são essenciais para uma experiência de uso aprimorada. O que foi abordado aqui é apenas a ponta da ponta do iceberg, mas fique à vontade para clonar o repositório final no GitHub. Para quem quiser se aprofundar em animações com a biblioteca reanimated, recomendo muito o canal do YouTube e o curso do William Candillon.

Murilo Campos Pereira
Software Engineer | Entusiasta de functional reactive/observable programming e utility-first CSS, formado em ciência da computação. Nas horas livres jogo RPG e ando de bicicleta.