Ir al contenido
charmotion

Cómo agregar animación a un sitio web (HTML, CSS, JS)

Por el equipo de Charmotion · Actualizado el

Ilustración de portada de la guía: Cómo agregar animaciones a un sitio web (HTML, CSS, JS), con personajes de demostración de Charmotion

Para agregar animación a un sitio web, usa transiciones de CSS y @keyframes para el movimiento de la interfaz, la Web Animations API cuando JavaScript deba controlar la reproducción, un <video> silenciado con reproducción automática para clips, y un reproductor de Lottie o Rive para ilustraciones y personajes. Anima transform y opacity, y respeta prefers-reduced-motion.

Puntos clave

  • CSS cubre la mayor parte del movimiento de la interfaz; envuélvelo o desactívalo dentro de @media (prefers-reduced-motion: reduce).
  • element.animate() devuelve un objeto Animation que puedes pausar, invertir y esperar con await.
  • El video solo se reproduce automáticamente si está silenciado; en iPhone además necesita playsinline.
  • El video con fondo transparente necesita dos archivos: primero un .mov HEVC con alfa para Safari y un WebM VP9 con alfa para todos los demás.
  • Para un personaje que reacciona a eventos, una máquina de estados (state machine) de Rive es un solo archivo pequeño en lugar de un video por estado.

¿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.

¿Conviene usar CSS, JavaScript o un archivo de animación?

Elige según la tarea, no según la herramienta. Cada opción tiene un uso claro:

TrabajoIdeal para
Estados hover, menús, fundidos, respuesta de botonesTransiciones de CSS y @keyframes
Movimiento que inicias, pausas o encadenas desde el códigoWeb Animations API (element.animate())
Un clip de marketing o una demo de producto<video> (MP4, WebM)
Una ilustración diseñada o una secuencia de íconosLottie o dotLottie
Una mascota de marca que cambia de estado con eventosMáquina de estados (state machine) de Rive
Tabla de decisión: APIs integradas para el feedback de la interfaz, efectos de íconos para íconos, una máquina de estados de Rive para personajes y video para clips de marketing
Elige el método de animación según la tarea.

CSS y la Web Animations API no tienen costo de carga porque el navegador ya los incluye. Video, Lottie y Rive agregan cada uno un archivo y, en el caso de Lottie y Rive, un script de reproductor. Ese costo vale la pena cuando el movimiento lo dibujó un diseñador en lugar de describirse en código. Si solo necesitas un spinner o un fundido, quédate con CSS.

¿Cómo funcionan las transiciones de CSS y @keyframes?

Una transición anima una propiedad entre dos valores cuando cambia, por ejemplo con :hover. Una animación por keyframes define pasos con nombre mediante @keyframes y los aplica con la forma abreviada animation, así que puede ejecutarse al cargar, en loop o una sola vez.

.badge {
  animation: pop-in 400ms ease-out both;
}

@keyframes pop-in {
  from { opacity: 0; transform: scale(0.8); }
  to   { opacity: 1; transform: scale(1); }
}

@media (prefers-reduced-motion: reduce) {
  .badge { animation: none; }
}

El modo de relleno both mantiene aplicado el primer fotograma antes de que empiece la animación y el último después de que termine, lo que evita un destello del estado sin estilos.

La media feature prefers-reduced-motion indica si el visitante activó una opción para reducir el movimiento en iOS, macOS, Windows o Android. Tiene dos valores, no-preference y reduce, y MDN la considera ampliamente disponible desde enero de 2020. Eliminar los movimientos grandes con reduce es lo mínimo; un fundido corto de opacidad suele estar bien. La guía de movimiento reducido explica qué conservar y qué quitar.

¿Cuándo es mejor la Web Animations API que CSS?

Usa la Web Animations API cuando la animación dependa de datos en tiempo de ejecución o necesites controlarla después de que empiece. element.animate(keyframes, options) recibe los mismos keyframes que usa CSS, escritos como objetos de JavaScript, y devuelve un objeto Animation.

const reduce = matchMedia("(prefers-reduced-motion: reduce)").matches;
const card = document.querySelector(".card");

if (!reduce) {
  const anim = card.animate(
    [
      { transform: "translateY(0)" },
      { transform: "translateY(-8px)" },
      { transform: "translateY(0)" },
    ],
    { duration: 600, easing: "ease-out", iterations: 1 }
  );
  anim.finished.then(() => card.classList.add("settled"));
}

El objeto devuelto tiene play(), pause(), reverse() y cancel(), además de una promesa finished. Así es fácil secuenciar pasos, desplazarte por la animación con currentTime o detenerla cuando el usuario sale de la página. CSS no puede hacer nada de eso sin alternar clases y escuchar animationend.

Las media queries no se aplican a los scripts, así que comprueba prefers-reduced-motion tú mismo con matchMedia, como arriba.

¿Cómo se reproduce automáticamente una animación en video sin sonido?

Agrega autoplay muted loop playsinline al elemento de video. Los navegadores solo permiten la reproducción automática si el video está silenciado o no tiene pista de audio. En iPhone, WebKit reproduce el video dentro de la página solo con playsinline; sin él, el video espera un toque y se abre en pantalla completa.

Para un personaje o logo sobre el fondo de tu página necesitas transparencia, y ningún formato es transparente en todas partes. Safari reproduce WebM pero descarta su canal alfa, así que pon primero un .mov HEVC con alfa y después un WebM VP9 con alfa:

<video autoplay muted loop playsinline preload="metadata"
       width="400" height="400" poster="/mascot-poster.png">
  <!-- Safari: HEVC with alpha must come first -->
  <source src="/mascot.mov" type="video/quicktime; codecs=hvc1.1.6.H120.b0">
  <!-- Chrome, Edge, Firefox: VP9 with alpha -->
  <source src="/mascot.webm" type="video/webm; codecs=vp09.00.41.08">
</video>
Comparación ampliada de un borde suave con canal alfa y un borde dentado de GIF de un bit sobre un degradado

Los navegadores eligen la primera fuente que pueden reproducir. Si el WebM fuera primero, Safari lo elegiría y mostraría un recuadro negro. La guía de video con fondo transparente explica cómo codificar ambos archivos. Para el movimiento reducido, pausa por script el video con reproducción automática cuando matchMedia("(prefers-reduced-motion: reduce)") coincida, y mantén visible el póster.

¿Cómo se inserta una animación de Lottie o dotLottie?

Lottie es una exportación JSON de una animación de After Effects; dotLottie (.lottie) es un paquete comprimido que puede contener varias animaciones, temas y máquinas de estados. El proyecto dotlottie-web de LottieFiles ofrece reproductores para JavaScript puro, React, Vue, Svelte, Solid y un web component. Todos reproducen archivos .json y .lottie.

El web component no necesita paso de compilación, así que es la opción más sencilla en una página estática:

<dotlottie-wc src="/animations/confetti.lottie" autoplay loop
  style="width: 320px; height: 320px"></dotlottie-wc>
<script type="module"
  src="https://unpkg.com/@lottiefiles/dotlottie-wc@0.10.0/dist/dotlottie-wc.js"></script>

Fija la versión que probaste en lugar de @latest, para que una actualización no cambie tu página sin aviso. Con un bundler, instala @lottiefiles/dotlottie-wc y agrega import "@lottiefiles/dotlottie-wc"; en lugar de la etiqueta script. La biblioteca lottie-web de Airbnb, más antigua, sigue funcionando con archivos .json si ya la usas.

¿Cómo se inserta una animación de Rive y se cambia su estado?

Carga el runtime web de Rive, apúntalo a un canvas y a un archivo .riv, e indica el nombre de la máquina de estados. Rive recomienda @rive-app/webgl2 para la mayoría de los sitios; @rive-app/canvas ofrece la misma API y evita el límite de contextos WebGL por página del navegador si muestras muchas animaciones a la vez. Llama a resizeDrawingSurfaceToCanvas() en onLoad para que el dibujo se vea nítido en pantallas de alta densidad.

<canvas id="mascot" width="400" height="400"
        role="img" aria-label="Animated mascot"></canvas>
<script src="https://unpkg.com/@rive-app/webgl2@2.44.2"></script>
<script>
  const reduce = matchMedia("(prefers-reduced-motion: reduce)").matches;
  const r = new rive.Rive({
    src: "/mascot.riv",
    canvas: document.getElementById("mascot"),
    stateMachine: "CharacterViewModelMachine",
    autoplay: !reduce,
    autoBind: true,
    onLoad: () => r.resizeDrawingSurfaceToCanvas(),
  });
</script>

Con autoBind: true, el runtime vincula el view model predeterminado del archivo, y cambias de estado escribiendo en él. En un archivo con un número mood y un trigger success, se ve así:

const vm = () => r.viewModelInstance;

form.addEventListener("submit", () => { vm().number("mood").value = 5; });  // loading
api.onDone = () => { vm().trigger("success").trigger(); vm().number("mood").value = 0; };

Los archivos .riv de Charmotion están hechos así: sube un personaje, una mascota de marca o un logo y obtienes CharacterViewModelMachine con un número mood de 0 a 9 (idle, happy, wave, thinking, sad, loading, listening, talking, sleeping, attention) y triggers para éxito, error, sí, no, entrada y salida, además del código para web, React y SwiftUI. Los archivos que usan el sistema de inputs anterior también funcionan; consulta la guía de máquinas de estados.

¿Cómo mantienes rápidas las animaciones de tu sitio web?

Tres hábitos evitan la mayoría de los tirones y las cargas lentas:

  • Anima transform y opacity. Los navegadores suelen poder componerlas sin recalcular el diseño. Animar width, top o margin obliga a recalcular el diseño en cada fotograma.
  • Carga reproductores y archivos solo cuando hagan falta. Rive indica que su WASM de WebGL2 pesa 648 KB tras la compresión brotli (enero de 2026); el runtime canvas-lite, más ligero, pesa unos 373 KB con brotli. Cárgalo en las páginas que muestran un personaje, no en todo el sitio, e inícialo cuando el canvas entre en pantalla con un IntersectionObserver.
  • Define el tamaño de cada elemento multimedia. Establece width y height en canvas y videos para que la página no tenga saltos de diseño al cargarlos, y usa preload="metadata" o "none" para los videos que quedan fuera de la primera pantalla.
new IntersectionObserver(([entry], obs) => {
  if (entry.isIntersecting) { startMascot(); obs.disconnect(); }
}).observe(document.getElementById("mascot"));

Por último, dale a las animaciones en loop una forma de apagarlas. Quien lee una página larga junto a una mascota que rebota sin parar agradecerá un botón de pausa o un loop en reposo (idle) tranquilo.

Cómo agregar un personaje animado a una página web

  1. Elige el método. Usa CSS para el movimiento de la interfaz, video para clips fijos y un archivo de Rive o Lottie para personajes diseñados.
  2. Agrega los archivos. Coloca los archivos .riv, .lottie o de video en tu carpeta estática, con una imagen de póster para el video.
  3. Agrega el marcado. Agrega un elemento <canvas>, <dotlottie-wc> o <video autoplay muted loop playsinline> con tamaño definido.
  4. Carga el reproductor. Incluye el script del reproductor con versión fija, o importa el paquete con tu bundler, solo en las páginas que lo necesiten.
  5. Conecta tus eventos. Asigna el número de mood o dispara triggers desde handlers de formularios, fetch o clics.
  6. Respeta el movimiento reducido. Comprueba prefers-reduced-motion en CSS y con matchMedia en el script, y muestra un fotograma estático cuando esté activado.

Preguntas frecuentes

¿Cuál es la forma más fácil de agregar animación a un sitio web?

CSS. Una transición en :hover o una animación corta con @keyframes no necesita ninguna librería ni descargas adicionales. Pasa a la Web Animations API cuando necesites controlar la reproducción desde JavaScript, y a Lottie, Rive o video solo cuando un diseñador haya dibujado el movimiento.

¿Por qué mi video de fondo no se reproduce automáticamente en iPhone?

Probablemente le falta muted o playsinline. Safari en iOS solo reproduce automáticamente videos sin sonido o silenciados, y solo los reproduce dentro de la página con playsinline. El modo de bajo consumo también puede bloquear la reproducción automática, así que define siempre una imagen de póster que se vea bien por sí sola.

¿Sirve un GIF para una animación en un sitio web?

Normalmente no. Los GIF pesan mucho más que archivos MP4 o WebM equivalentes y solo admiten un color totalmente transparente, así que los bordes suaves se ven dentados. Usa un video silenciado en loop para los clips, y un archivo de Rive o Lottie para personajes e íconos vectoriales.

¿Necesito un framework para usar Rive o Lottie?

No. Ambos tienen reproductores en JavaScript puro. El runtime web de Rive y el componente web de dotLottie se pueden cargar con una sola etiqueta script en una página HTML estática, y ambos también ofrecen paquetes de npm y wrappers para React si usas un bundler.

Respuestas rápidas de esta guía

Fuentes

Guías relacionadas