Ir al contenido
charmotion

Cómo agregar animación a una app de React Native

Por el equipo de Charmotion · Actualizado el

Ilustración de portada de la guía: Cómo agregar animación a una app de React Native, con personajes de demostración de Charmotion

Para agregar animación a una app de React Native, usa la API integrada Animated con useNativeDriver: true para movimientos simples de UI, y Reanimated para movimientos complejos o controlados por gestos. Agrega lottie-react-native para loops vectoriales creados por diseñadores, @rive-app/react-native para personajes interactivos y react-native-video para clips.

Puntos clave

  • useNativeDriver: true mueve una animación de Animated al hilo de UI.
  • Reanimated 4 funciona solo con la New Architecture y necesita react-native-worklets.
  • El runtime Nitro de Rive necesita React Native 0.78+, Expo SDK 53+, iOS 15.1+ y Android SDK 24+.
  • Los cambios de estado de Rive pasan por hooks de Data Binding como useRiveNumber y useRiveTrigger.
  • Comprueba AccessibilityInfo.isReduceMotionEnabled() antes de iniciar loops.

¿Ya tienes un personaje o logo? Mira tu personaje en pantallas reales de una app: pantallas de splash, onboarding, carga y estado vacío. Es gratis, funciona en tu navegador y no sube nada.

API Animated o Reanimated: ¿cuál deberías usar?

React Native incluye la API Animated. Basta para fundidos, deslizamientos y resortes simples, y no necesita ninguna dependencia adicional. Reanimated, de Software Mansion, es la opción habitual para el movimiento controlado por gestos, las animaciones de layout y todo lo que deba seguir fluido mientras JavaScript está ocupado.

Este es el resto de la caja de herramientas:

Quieres animarUso
Feedback simple de la interfazAnimated con useNativeDriver: true
Gestos, secuencias complejasReanimated
Un loop vectorial hecho por un diseñadorlottie-react-native
Un personaje que reacciona a eventos de la app@rive-app/react-native
Un clip fijo y prerrenderizadoreact-native-video

Un resorte con Reanimated se ve así:

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>
  );
}

Reanimated 4 funciona solo con la Nueva Arquitectura (Fabric) y requiere react-native-worklets. Las apps más antiguas que usan la arquitectura heredada deben migrar primero o quedarse en una versión anterior de Reanimated.

¿Qué hace realmente useNativeDriver?

Sin el driver nativo, JavaScript calcula cada fotograma y lo envía al código nativo. Si el hilo de JS está ocupado, por ejemplo procesando una respuesta grande, se pierden fotogramas. Con useNativeDriver: true, React Native envía toda la animación al código nativo una sola vez, y se ejecuta en el hilo de UI sin pasar por JavaScript en cada fotograma.

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>;

El driver nativo admite propiedades que no afectan al layout, como opacity y transform. Animar width, height o los márgenes requiere el driver de JS, lo cual es una buena razón para expresar el movimiento como escala y traslación.

Esta es también la idea central de Reanimated: los worklets ejecutan la lógica de animación en el hilo de UI, así que los gestos y resortes siguen fluidos aunque tu JavaScript no lo esté.

Diez estados en loop de un mismo personaje, cada uno seleccionado con un número de mood del 0 al 9
Los diez estados en loop de un archivo Rive de Charmotion, seleccionados con el input numérico mood.

¿Cómo agregar una animación Lottie en Expo o en React Native puro?

Instala lottie-react-native. En un proyecto puro, ejecuta después pod install en la carpeta ios; en Expo, usa npx expo install lottie-react-native para que la versión coincida con tu SDK. Revisa primero los requisitos del README: la versión mayor actual está pensada para versiones recientes de React Native con la Nueva Arquitectura, y la línea 7.3.x existe para versiones anteriores.

import LottieView from "lottie-react-native";

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

Dale a la vista dimensiones explícitas, como hace el ejemplo del README. Para React Native Web, el paquete también necesita @lottiefiles/dotlottie-react.

Lottie encaja bien con spinners, marcas de éxito e ilustraciones de onboarding. Si la animación debe cambiar su comportamiento según el estado de la app, una máquina de estados (state machine) de Rive es más sencilla que ir cambiando archivos Lottie.

¿Cómo agregar un personaje de Rive con el runtime de Nitro?

El runtime actual de Rive para React Native está construido sobre Nitro Modules. Instálalo junto con su dependencia peer; con Expo, usa npx expo install y un development build, ya que contiene código nativo:

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

Requiere React Native 0.78+ (se recomienda 0.79+), Expo SDK 53+, iOS 15.1+ y Android SDK 24+. El paquete anterior rive-react-native es heredado.

Carga el archivo con useRiveFile, crea una instancia del view model y vincula las propiedades con 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 }}
    />
  );
}

Llama a success() cuando termine una acción. La documentación de Rive recomienda async: true e indica que quizá necesites playIfNeeded() en la ref de la vista después de cambiar propiedades si la máquina de estados se ha detenido. Los métodos de inputs de la máquina de estados están obsoletos en este runtime; consulta Rive en React Native para ver el calendario de migración.

¿Cómo reproducir un video silenciado y en loop?

Para un clip fijo, usa react-native-video. Revisa el README para conocer la versión mayor actual y su versión mínima de iOS. Un loop silenciado necesita tres props:

import Video from "react-native-video";

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

El video es la herramienta adecuada para clips fotográficos o en 3D, como una intro de inicio o un loop de marketing. No puede reaccionar al estado de la app, y cada variación agrega otro archivo a tu bundle. Para un personaje que tiene que estar en reposo, esperar y celebrar, un solo archivo de Rive con una máquina de estados suele pesar menos y es mucho más flexible.

Pausa el video cuando la pantalla pierda el foco, por ejemplo con los eventos de foco de React Navigation, para que no siga decodificando en segundo plano.

Lottie, Rive o video: ¿cuál es más ligero en React Native?

Cada opción tiene dos costos: la biblioteca de runtime que agregas una sola vez y los archivos que incluyes por cada animación.

  • Video necesita react-native-video, y cada clip es un archivo independiente. El peso crece con la resolución, la duración y el número de variaciones.
  • Lottie agrega las bibliotecas nativas de Lottie. Los archivos son JSON, así que un loop vectorial simple pesa poco, pero las exportaciones complejas de After Effects con muchas capas pueden crecer rápidamente.
  • Rive agrega su runtime nativo. Rive indica que el runtime de iOS ocupa unos 1.67 MB de descarga y el runtime de Android ARM-v8a, 2.40 MB de descarga (enero de 2026). A partir de ahí, cada .riv es un binario compacto que puede contener todos los estados de un personaje.

La balanza se invierte a medida que agregas estados. Un loop decorativo en una sola pantalla suele ser más ligero como video corto o archivo Lottie. Una mascota de marca con estados de reposo, carga, éxito, error y habla necesitaría cinco videos o cinco archivos Lottie con cortes bruscos entre ellos, mientras que un solo archivo de Rive los cubre todos con transiciones suaves.

Elijas lo que elijas, mide los números reales: revisa el tamaño de tu bundle de release antes y después de agregar la biblioteca, y compara el peso de los archivos con tu propio arte en lugar de fiarte de los ejemplos del proveedor.

¿Cómo agregar una mascota de marca o un personaje al onboarding?

Define los estados de la mascota a partir de los eventos de tu app, no del arte. Una correspondencia típica:

  • Aparece un paso del onboarding: dispara enter y luego pasa a reposo.
  • Esperando a la red: un loop de carga (mood 5).
  • Permiso concedido o tarea terminada: dispara success. Fallo: dispara error.
  • Entrada de voz: escuchando (listening); respuesta en reproducción: hablando (talking).

Mantén esta correspondencia en un solo hook, como useMascot(state), que establezca el estado de ánimo y dispare los triggers. Las pantallas pasan su estado; la máquina de estados de Rive gestiona las transiciones para que las reacciones terminen limpiamente.

success: un trigger que se reproduce una vez y luego vuelve al estado de ánimo actual.

Charmotion genera este tipo de archivo a partir de una mascota o un logo que subas: un .riv con CharacterViewModelMachine y un view model Character (un número mood de 0 a 9 y triggers como success, error, enter y exit), que coincide con el código de Nitro anterior. Consulta estados de animación de una mascota para planificarlo.

¿Cómo mantener las animaciones de React Native fluidas y accesibles?

Saca el trabajo de animación del hilo de JavaScript:

  • Usa useNativeDriver: true o worklets de Reanimated, y anima transform y opacity.
  • Desmonta o pausa los loops, las vistas de Lottie y los videos en las pantallas que no tienen el foco.
  • Incluye los archivos de animación en la app en lugar de descargarlos la primera vez que se muestran.
  • Prueba builds de release en un dispositivo Android real de gama baja; el modo de desarrollo es más lento.

Respeta la opción de reducir movimiento. AccessibilityInfo.isReduceMotionEnabled() se resuelve como true cuando el ajuste está activado, y el evento reduceMotionChanged se dispara cuando cambia. En Android también devuelve true cuando la escala de animación de transiciones está desactivada.

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} />;

Conserva el significado en texto cuando quites el movimiento. Consulta la guía de movimiento reducido para ver los detalles de WCAG.

Agrega animación a una app de React Native

  1. Usa primero lo integrado. Anima opacity y transform con Animated y useNativeDriver: true.
  2. Agrega Reanimated para gestos. Instala Reanimated y react-native-worklets; la versión 4 requiere la Nueva Arquitectura.
  3. Agrega un runtime para el arte. Instala lottie-react-native, o @rive-app/react-native con react-native-nitro-modules (un development build de Expo).
  4. Renderiza el archivo. Usa <LottieView source autoPlay loop /> o useRiveFile con <RiveView file={riveFile} />.
  5. Vincula el estado de la app. Crea una instancia del view model y contrólala con useRiveNumber y useRiveTrigger.
  6. Respeta la opción de reducir movimiento. Comprueba AccessibilityInfo.isReduceMotionEnabled() y muestra una imagen fija cuando devuelva true.

Preguntas frecuentes

¿Reanimated 4 requiere la Nueva Arquitectura?

Sí. La documentación de Software Mansion indica que Reanimated 4.x funciona solo con la Nueva Arquitectura de React Native (Fabric) y requiere el paquete react-native-worklets. Si tu app todavía usa la arquitectura heredada, migra primero o quédate en una versión mayor anterior de Reanimated hasta que puedas hacerlo.

¿Puedo usar Rive en Expo Go?

No. El runtime de Rive para React Native incluye código nativo a través de Nitro Modules, así que no puede ejecutarse dentro de la app estándar de Expo Go. Usa un development build de Expo (Expo SDK 53 o posterior) o un proyecto de React Native puro y luego instala @rive-app/react-native y react-native-nitro-modules.

¿rive-react-native está obsoleto?

Rive ahora considera rive-react-native como el runtime heredado y dirige los proyectos nuevos al paquete @rive-app/react-native, basado en Nitro. Las apps existentes pueden seguir funcionando con el paquete antiguo por ahora, pero las nuevas funciones y el trabajo de Data Binding se centran en el nuevo runtime, así que planifica una migración.

¿Uso Animated o Reanimated para un fundido simple?

La API Animated integrada es suficiente. Usa Animated.timing con useNativeDriver: true para que el fundido se ejecute en el hilo de la interfaz. Recurre a Reanimated cuando el movimiento siga un gesto, encadene varios pasos o deba mantenerse fluido mientras el hilo de JavaScript está ocupado con otras tareas.

Respuestas rápidas de esta guía

Fuentes

Guías relacionadas