Pular para o conteúdo
charmotion

Como adicionar animação a um app React Native

Por equipe Charmotion · Atualizado em

Ilustração de capa do guia: Como adicionar animação a um app React Native, com os personagens de demonstração do Charmotion

Para adicionar animação a um app React Native, use a API nativa Animated com useNativeDriver: true para movimentos simples de UI, e o Reanimated para movimentos complexos ou guiados por gestos. Adicione lottie-react-native para loops vetoriais feitos por designers, @rive-app/react-native para personagens interativos e react-native-video para clipes.

Principais pontos

  • useNativeDriver: true move uma animação Animated para a thread de UI.
  • O Reanimated 4 funciona apenas na New Architecture e precisa de react-native-worklets.
  • O runtime Nitro do Rive exige React Native 0.78+, Expo SDK 53+, iOS 15.1+ e Android SDK 24+.
  • As mudanças de estado no Rive passam por hooks de Data Binding como useRiveNumber e useRiveTrigger.
  • Verifique AccessibilityInfo.isReduceMotionEnabled() antes de iniciar loops.

Já tem um personagem ou logo? Veja seu personagem em telas reais de app: telas de splash, onboarding, carregamento e estado vazio. É grátis, roda no navegador e não envia nada.

API Animated ou Reanimated: qual usar?

O React Native já vem com a API Animated. Ela dá conta de fades, deslizes e springs simples, sem nenhuma dependência extra. O Reanimated, da Software Mansion, é a escolha comum para movimento guiado por gestos, animações de layout e tudo o que precisa continuar fluido enquanto o JavaScript está ocupado.

Veja o resto das ferramentas:

Você quer animarUso
Feedback simples de UIAnimated com useNativeDriver: true
Gestos, sequências complexasReanimated
Um loop vetorial feito por designerlottie-react-native
Um personagem que reage a eventos do app@rive-app/react-native
Um clipe fixo e pré-renderizadoreact-native-video

Um spring do Reanimated fica assim:

import Animated, { useSharedValue, useAnimatedStyle, withSpring } from "react-native-reanimated";

function Bouncy() {
  const scale = useSharedValue(1);
  const style = useAnimatedStyle(() => ({ transform: [{ scale: scale.value }] }));
  return (
    <Pressable onPressIn={() => (scale.value = withSpring(0.92))}
               onPressOut={() => (scale.value = withSpring(1))}>
      <Animated.View style={[styles.card, style]} />
    </Pressable>
  );
}

O Reanimated 4 funciona apenas com a New Architecture (Fabric) e exige react-native-worklets. Apps mais antigos na arquitetura legada precisam migrar primeiro ou continuar em uma versão anterior do Reanimated.

O que o useNativeDriver faz de fato?

Sem o native driver, o JavaScript calcula cada frame e o envia para o código nativo. Se a thread JS estiver ocupada, por exemplo processando uma resposta grande, frames são perdidos. Com useNativeDriver: true, o React Native envia a animação inteira para o código nativo uma única vez, e ela roda na thread de UI sem passar pelo JavaScript a cada frame.

const opacity = useRef(new Animated.Value(0)).current;

useEffect(() => {
  Animated.timing(opacity, {
    toValue: 1,
    duration: 300,
    useNativeDriver: true,
  }).start();
}, []);

return <Animated.View style={{ opacity }}>{children}</Animated.View>;

O native driver é compatível com propriedades que não afetam o layout, como opacity e transform. Animar width, height ou margens exige o driver JS, o que é um bom motivo para expressar o movimento como escala e translação.

Essa também é a ideia central do Reanimated: os worklets rodam a lógica da animação na thread de UI, então gestos e springs continuam fluidos mesmo quando o seu JavaScript não está.

Dez estados em loop de um personagem, cada um selecionado por um número de mood de 0 a 9
Os dez estados em loop em um arquivo Rive do Charmotion, selecionados com o input numérico mood.

Como adicionar uma animação Lottie no Expo ou no React Native puro?

Instale o lottie-react-native. Em um projeto puro, rode pod install na pasta ios em seguida; no Expo, use npx expo install lottie-react-native para que a versão corresponda ao seu SDK. Confira antes os requisitos no README: a versão major atual é voltada a versões recentes do React Native na New Architecture, e a linha 7.3.x existe para versões mais antigas.

import LottieView from "lottie-react-native";

export function Loading() {
  return (
    <LottieView
      source={require("./assets/loading.json")}
      style={{ width: 160, height: 160 }}
      autoPlay
      loop
    />
  );
}

Defina dimensões explícitas para a view, como no exemplo do README. Para React Native Web, o pacote também precisa de @lottiefiles/dotlottie-react.

O Lottie funciona bem para spinners, checks de sucesso e ilustrações de onboarding. Se a animação precisa mudar de comportamento conforme o estado do app, uma state machine do Rive é mais simples do que trocar arquivos Lottie.

Como adicionar um personagem Rive com o runtime Nitro?

O runtime atual do Rive para React Native é construído sobre Nitro Modules. Instale-o junto com a peer dependency; no Expo, use npx expo install e uma development build, já que ele contém código nativo:

npm install @rive-app/react-native react-native-nitro-modules

Ele exige React Native 0.78+ (0.79+ recomendado), Expo SDK 53+, iOS 15.1+ e Android SDK 24+. O pacote antigo rive-react-native é legado.

Carregue o arquivo com useRiveFile, crie uma instância do view model e vincule as propriedades com hooks:

import { RiveView, useRiveFile, useViewModelInstance, useRiveNumber, useRiveTrigger } from "@rive-app/react-native";

export function Mascot({ loading }: { loading: boolean }) {
  const { riveFile } = useRiveFile(require("./assets/character.riv"));
  const { instance } = useViewModelInstance(riveFile, { async: true, viewModelName: "Character" });
  const { setValue: setMood } = useRiveNumber("mood", instance);
  const { trigger: success } = useRiveTrigger("success", instance);

  useEffect(() => { setMood(loading ? 5 : 0); }, [loading]);

  if (!riveFile) return null;
  return (
    <RiveView
      file={riveFile}
      stateMachineName="CharacterViewModelMachine"
      dataBind={instance}
      autoPlay={true}
      style={{ width: 220, height: 220 }}
    />
  );
}

Chame success() quando uma ação for concluída. A documentação do Rive recomenda async: true e observa que pode ser necessário chamar playIfNeeded() na ref da view depois de alterar propriedades, caso a state machine tenha se estabilizado. Os métodos de input da state machine estão obsoletos neste runtime; veja Rive no React Native para o cronograma de migração.

Como reproduzir um vídeo sem som e em loop?

Para um clipe fixo, use react-native-video. Confira no README a versão major atual e a versão mínima de iOS. Um loop sem som precisa de três props:

import Video from "react-native-video";

<Video
  source={require("./assets/logo-reveal.mp4")}
  style={{ width: 240, height: 240 }}
  resizeMode="contain"
  repeat
  muted
/>

Vídeo é a ferramenta certa para clipes fotográficos ou 3D, como uma revelação de lançamento ou um loop de marketing. Ele não reage ao estado do app, e cada variação adiciona mais um arquivo ao seu bundle. Para um personagem que precisa ficar em idle, esperar e comemorar, um único arquivo Rive com state machine costuma ser menor e muito mais flexível.

Pause o vídeo quando a tela perder o foco, por exemplo com os eventos de foco do React Navigation, para que ele não continue sendo decodificado em segundo plano.

Lottie, Rive ou vídeo: qual é o mais leve no React Native?

Cada opção tem dois custos: a biblioteca de runtime, que você adiciona uma vez, e os arquivos que você inclui para cada animação.

  • Vídeo exige react-native-video, e cada clipe é um arquivo separado. O tamanho cresce com a resolução, a duração e o número de variações.
  • Lottie adiciona as bibliotecas nativas do Lottie. Os arquivos são JSON, então um loop vetorial simples é pequeno, mas exportações complexas do After Effects com muitas camadas podem crescer rápido.
  • Rive adiciona seu runtime nativo. O Rive informa cerca de 1,67 MB de download para o runtime de iOS e 2,40 MB de download para o runtime Android ARM-v8a (janeiro de 2026). Depois disso, cada .riv é um binário compacto que pode conter todos os estados de um personagem.

A balança se inverte conforme você adiciona estados. Um loop decorativo em uma única tela costuma ser mais leve como vídeo curto ou arquivo Lottie. Um mascote com os estados idle, loading, success, error e talking precisaria de cinco vídeos ou cinco arquivos Lottie com cortes secos entre eles, enquanto um único arquivo Rive cobre todos com transições suaves.

Seja qual for a sua escolha, meça os números reais: verifique o tamanho do bundle de release antes e depois de adicionar a biblioteca e compare o tamanho dos arquivos com a sua própria arte, em vez de confiar nos exemplos dos fornecedores.

Como adicionar um mascote ou personagem de onboarding?

Defina os estados do mascote a partir dos eventos do seu app, não da arte. Um mapeamento típico:

  • Um passo do onboarding aparece: dispare enter e depois idle.
  • Esperando a rede: um loop de loading (mood 5).
  • Permissão concedida ou tarefa concluída: dispare success. Falha: dispare error.
  • Entrada de voz: listening; resposta tocando: talking.

Mantenha esse mapeamento em um único hook, como useMascot(state), que define o humor e dispara os triggers. As telas passam o próprio estado; a state machine do Rive cuida das transições para que as reações terminem de forma limpa.

success: um trigger que toca uma vez e depois volta ao humor atual.

O Charmotion gera esse tipo de arquivo a partir de um mascote ou logo enviado: um .riv com CharacterViewModelMachine e um view model Character (um número mood de 0 a 9 e triggers como success, error, enter e exit), que corresponde ao código Nitro acima. Veja estados de animação de mascote para o planejamento.

Como manter as animações no React Native fluidas e acessíveis?

Mantenha o trabalho de animação fora da thread do JavaScript:

  • Use useNativeDriver: true ou worklets do Reanimated e anime transform e opacity.
  • Desmonte ou pause loops, views Lottie e vídeos em telas que não estão em foco.
  • Inclua os arquivos de animação no app em vez de buscá-los na primeira exibição.
  • Teste builds de release em um aparelho Android básico de verdade; o modo dev é mais lento.

Respeite o movimento reduzido. AccessibilityInfo.isReduceMotionEnabled() retorna true quando a configuração está ativada, e o evento reduceMotionChanged é disparado quando ela muda. No Android, também retorna true quando a escala de animação de transição está desativada.

const [reduce, setReduce] = useState(false);

useEffect(() => {
  AccessibilityInfo.isReduceMotionEnabled().then(setReduce);
  const sub = AccessibilityInfo.addEventListener("reduceMotionChanged", setReduce);
  return () => sub.remove();
}, []);

return reduce ? <Image source={require("./assets/mascot-still.png")} /> : <Mascot loading={loading} />;

Mantenha o significado em texto quando remover o movimento. Veja o guia de movimento reduzido para detalhes sobre WCAG.

Adicione animação a um app React Native

  1. Comece pelo que já vem pronto. Anime opacity e transform com Animated e useNativeDriver: true.
  2. Adicione Reanimated para gestos. Instale o Reanimated e o react-native-worklets; a versão 4 exige a New Architecture.
  3. Adicione um runtime para a arte. Instale lottie-react-native ou @rive-app/react-native com react-native-nitro-modules (uma development build do Expo).
  4. Renderize o arquivo. Use <LottieView source autoPlay loop /> ou useRiveFile com <RiveView file={riveFile} />.
  5. Vincule o estado do app. Crie uma instância do view model e controle-a com useRiveNumber e useRiveTrigger.
  6. Respeite o movimento reduzido. Verifique AccessibilityInfo.isReduceMotionEnabled() e mostre uma imagem estática quando for true.

Perguntas frequentes

O Reanimated 4 exige a New Architecture?

Sim. A documentação da Software Mansion afirma que o Reanimated 4.x funciona apenas com a New Architecture (Fabric) do React Native e exige o pacote react-native-worklets. Se o seu app ainda roda na arquitetura legada, migre primeiro ou continue em uma versão major anterior do Reanimated até poder migrar.

Posso usar o Rive no Expo Go?

Não. O runtime do Rive para React Native inclui código nativo por meio de Nitro Modules, então não roda dentro do app Expo Go padrão. Use uma development build do Expo (Expo SDK 53 ou posterior) ou um projeto React Native puro e depois instale @rive-app/react-native e react-native-nitro-modules.

O rive-react-native está obsoleto?

O Rive agora trata o rive-react-native como runtime legado e orienta novos projetos a usar o @rive-app/react-native, baseado em Nitro. Apps existentes podem continuar no pacote antigo por enquanto, mas os novos recursos e o trabalho de Data Binding são voltados ao novo runtime, então planeje uma migração.

Devo usar Animated ou Reanimated para um fade simples?

A API Animated nativa já resolve. Use Animated.timing com useNativeDriver: true para que o fade rode na thread de UI. Recorra ao Reanimated quando o movimento acompanha um gesto, encadeia várias etapas ou precisa continuar fluido enquanto a thread de JavaScript está ocupada com outras tarefas.

Respostas rápidas deste guia

Fontes

Guias relacionados