wandres.dev
MOTION Y LAS ALTERNATIVAS · El resto del ecosistema

Anime.js v4 y auto-animate: dos filosofías opuestas

La reescritura modular de Anime.js y su API de v4, auto-animate como la librería que no te pide escribir animaciones, y qué caso resuelve cada una mejor que las grandes.

⏱ 17 min

Entre el motor completo y la API nativa hay dos proyectos que llegan al mismo sitio por caminos contrarios. Anime.js v4 es una reescritura modular de una librería veterana que te da timelines, muelles y arrastre con una fracción del peso de las grandes. auto-animate va al extremo opuesto: no te deja escribir animaciones en absoluto, observa el DOM y anima los cambios por su cuenta. Las dos son elecciones legítimas, y las dos fallan de formas que conviene conocer antes.

🎯 Al terminar esta lección sabrás
  • Escribir animaciones y timelines con la API de módulos de Anime.js v4.
  • Identificar los cambios de v3 a v4 que rompen todo el código antiguo que encuentres en tutoriales.
  • Aplicar auto-animate y saber exactamente qué tres transiciones cubre y cuáles no.
  • Decidir cuándo una de estas dos es mejor elección que un motor completo o que WAAPI a pelo.

Anime.js v4

La versión 4, publicada en 2025, es una reescritura completa. Desapareció el objeto global anime y en su lugar hay exportaciones nombradas que se pueden sacudir del árbol. La librería sigue siendo MIT y sigue sin dependencias.

import { animate, stagger, utils, createTimeline } from "animejs";

animate(".caja", {
  x: 240,
  rotate: "1turn",
  duration: 800,
  ease: "outQuad",
});

Los cambios que rompen el código de v3 son cuatro, y como la mayoría de tutoriales que encontrarás son de v3, conviene tenerlos claros:

v3 v4 Nota
anime({ targets: ".caja", ... }) animate(".caja", { ... }) El objetivo sale del objeto de parámetros
easing: "easeOutQuad" ease: "outQuad" Nombre de la propiedad y del valor
direction: "alternate" alternate: true Booleanos en lugar de cadenas
anime.timeline() createTimeline() Igual con stagger, que vive en la raíz o en utils

Las duraciones van en milisegundos. Cada propiedad puede llevar sus propios parámetros, lo que evita tener que partir la animación en dos cuando una propiedad necesita otra curva:

animate(".tarjeta", {
  y: { to: [40, 0], duration: 520, ease: "outCubic" },
  opacity: { to: [0, 1], duration: 260, ease: "linear" },
  delay: stagger(70),
});

La timeline se construye con createTimeline y admite posiciones absolutas en milisegundos o relativas al final del bloque anterior. Los valores por defecto se declaran una vez:

const tl = createTimeline({ defaults: { duration: 600, ease: "outExpo" } });

tl.add(".titulo", { opacity: [0, 1], y: [24, 0] })
  .add(".subtitulo", { opacity: [0, 1] }, "-=400")
  .add(".cta", { scale: [0.9, 1] }, "-=300");

tl.pause();
document.querySelector("#lanzar").addEventListener("click", () => tl.restart());

Además del motor clásico, v4 trae piezas que antes exigían otra dependencia: createDraggable para arrastre con inercia, createSpring para curvas de muelle, onScroll para enganchar una animación al desplazamiento, svg con utilidades de trazado y morfismo, y waapi.animate como envoltura directa sobre element.animate() cuando quieres el compositor y no necesitas el motor.

import { waapi } from "animejs";

waapi.animate(".barra", { scaleX: [0, 1], duration: 900, ease: "linear" });

Y createScope, que resuelve el problema de la limpieza en componentes: agrupa todo lo creado dentro y lo revierte de una vez, incluidos los estilos en línea que la librería escribió.

import { createScope, animate } from "animejs";

const alcance = createScope({ root: contenedor }).add(() => {
  animate(".item", { opacity: [0, 1], delay: stagger(50) });
});

// Al desmontar el componente:
alcance.revert();

Dónde está su hueco real. Anime.js v4 ocupa el espacio de “necesito timelines y arrastre pero no quiero un motor completo”. Es notablemente más ligera que GSAP con sus plugins y cubre el ochenta por ciento de lo mismo. Lo que no tiene es el ecosistema: no hay equivalente de ScrollTrigger con pin y snap, no hay división de texto con reflow responsive, y no hay MorphSVG resolviendo el emparejamiento de puntos entre formas arbitrarias. Si tu proyecto necesita alguna de esas tres, la comparación de peso no viene al caso porque no estás comparando lo mismo.

auto-animate

@formkit/auto-animate no te deja describir el movimiento. Le das un elemento contenedor y la librería instala un MutationObserver sobre él. Cuando un hijo se añade, se quita o cambia de posición, mide y anima con WAAPI.

import autoAnimate from "@formkit/auto-animate";

autoAnimate(document.querySelector("#lista"), {
  duration: 250,
  easing: "ease-in-out",
});

En React hay un hook que devuelve la referencia al contenedor, y en Vue una directiva:

import { useAutoAnimate } from "@formkit/auto-animate/react";

function Tareas({ tareas }) {
  const [padre] = useAutoAnimate({ duration: 200 });
  return <ul ref={padre}>{tareas.map((t) => <li key={t.id}>{t.texto}</li>)}</ul>;
}

Cubre exactamente tres transiciones y ninguna más: aparición de un hijo, desaparición de un hijo y cambio de posición de un hijo dentro del contenedor. Con eso resuelve el caso más común y más agradecido de toda la interfaz —una lista que cambia sin que los elementos salten— con una línea de código y sin ninguna decisión de diseño por tu parte.

Lo que hay que saber antes de meterlo en producción:

  • Solo mira a los hijos directos. Un cambio dos niveles por debajo no dispara nada.
  • Anima posición y opacidad, no tamaño interno. Un hijo que cambia de altura por su contenido no se anima; se anima su desplazamiento si eso mueve a los hermanos.
  • Respeta prefers-reduced-motion por defecto, y eso está muy bien. La opción disrespectUserMotionPreference existe para desactivarlo, y no hay ningún caso legítimo en el que quieras usarla.
  • Un contenedor con muchos hijos mide todos los hijos en cada mutación. En una lista virtualizada de miles de filas, esto es exactamente el trabajo que la virtualización intentaba evitar.
  • El aspecto es genérico por construcción. Si tu sistema de movimiento tiene curvas y duraciones propias, auto-animate solo te deja ajustar dos números. Es coherencia por defecto a cambio de renunciar al control.
// Personalización más allá de duration y easing: una función que devuelve
// el KeyframeEffect para cada tipo de cambio.
autoAnimate(contenedor, (elemento, accion, coordenadas) => {
  let keyframes;
  if (accion === "add") {
    keyframes = [
      { transform: "scale(0.96)", opacity: 0 },
      { transform: "scale(1)", opacity: 1 },
    ];
  }
  if (accion === "remove") {
    keyframes = [
      { transform: "scale(1)", opacity: 1 },
      { transform: "scale(0.98)", opacity: 0 },
    ];
  }
  if (accion === "remain") {
    const dx = coordenadas.oldCoords.left - coordenadas.newCoords.left;
    const dy = coordenadas.oldCoords.top - coordenadas.newCoords.top;
    keyframes = [
      { transform: `translate(${dx}px, ${dy}px)` },
      { transform: "translate(0, 0)" },
    ];
  }
  return new KeyframeEffect(elemento, keyframes, { duration: 240, easing: "ease-out" });
});

Ese tercer caso, remain, es literalmente FLIP escrito a mano: la diferencia entre la caja vieja y la nueva se aplica como transform inverso y se anima hasta cero. Si lo has escrito antes con getBoundingClientRect, reconocerás la forma.

💡
El mejor uso de auto-animate es como suelo, no como sistema

Ponerlo en los contenedores de listas de toda la aplicación elimina de golpe la clase entera de “los elementos saltan al filtrar o al ordenar”, que es el defecto de movimiento más frecuente y el que más se percibe como falta de acabado. Y no te impide animar a mano donde sí importa. Como base es excelente; como respuesta a todo, se queda corto en cuanto alguien pide algo específico.

Cuándo cada una gana

Anime.js gana cuando necesitas secuenciación real y el proyecto no justifica un motor completo: una landing con una intro coreografiada, un fragmento interactivo dentro de un artículo, una visualización con transiciones encadenadas. También cuando quieres arrastre y muelles sin sumar dos dependencias más. Y en cualquier sitio donde el peso sea una restricción de verdad y no una preferencia.

auto-animate gana cuando el movimiento que falta es de reordenación y el equipo no va a mantener código de animación. Es la única librería de esta lista cuyo coste de mantenimiento es literalmente cero: no hay nada que actualizar cuando cambia el diseño, porque no hay nada escrito.

Ninguna de las dos gana cuando el movimiento es parte de la identidad del producto y hay una persona dedicada a ello. Ahí el techo importa más que el suelo, y el techo de un motor completo está mucho más alto.

auto-animate demuestra que el movimiento correcto por defecto vale más que el movimiento excelente opcional

Hay una asimetría que se ve poco y que auto-animate explota mejor que nadie: el movimiento que falta se percibe como un defecto y el movimiento que sobra se percibe como un adorno. Cuando una lista se reordena de golpe, el usuario pierde el rastro del elemento que estaba mirando y tiene que volver a buscarlo; eso es una carga cognitiva real y medible, y no aparece en ningún ticket porque nadie sabe articularla. Cuando una lista se reordena con una transición mediocre de doscientos cincuenta milisegundos y una curva genérica, el problema desaparece por completo, y el hecho de que la curva no sea la que habría elegido tu diseñador es invisible para todo el mundo menos para tu diseñador. La consecuencia práctica es incómoda para quien disfruta afinando curvas: la diferencia entre cero y aceptable es enorme, y la diferencia entre aceptable y excelente es pequeña. Un equipo que instala auto-animate en sus veinte contenedores de lista en una tarde consigue más mejora percibida que uno que dedica un trimestre a coreografiar tres pantallas con precisión de milisegundo, y esto es cierto aunque el segundo equipo tenga razón en todo lo que dice sobre la curva. La conclusión no es que afinar no valga la pena; es que el orden importa. Primero elimina el movimiento ausente de toda la aplicación con la herramienta más barata que exista, y solo después invierte en las dos o tres pantallas donde el movimiento es parte de lo que el producto quiere comunicar. Al revés —afinar tres pantallas mientras las otras cincuenta siguen saltando— es el patrón más común y el peor reparto de esfuerzo posible.

⚔️ La misma lista, tres veces
  1. Monta una lista filtrable de treinta elementos sin ninguna animación y observa cuánto cuesta seguir un elemento concreto al filtrar.
  2. Añade autoAnimate al contenedor y compáralo. Mide el tiempo del bloque de mutación en el panel de rendimiento.
  3. Reescribe lo mismo con Anime.js: mide las cajas, aplica el transform inverso y anima a cero.
  4. Sube la lista a mil elementos y compara el coste de las dos soluciones.
  5. Activa el movimiento reducido del sistema y comprueba cuál de las dos versiones lo respeta sin que hayas escrito nada.