Animación web
El buen movimiento web es una decisión de diseño primero y una llamada a biblioteca después. Sigue el flujo de trabajo a continuación en orden; carga los archivos de reglas solo cuando un paso los necesite.
Flujo de trabajo
1. Lee el proyecto antes de elegir nada
- Revisa
package.json. Si ya hay una biblioteca de animación instalada, úsala. No agregues una segunda para un único efecto. - Anota el framework (React / Next.js App Router / Vue / Svelte / HTML plano) y si los componentes se renderizan en el servidor (se necesita
"use client"para animación del lado del cliente en Next.js). - Busca tokens de movimiento existentes (duraciones, easing) en variables CSS o un archivo de tema, y reutilízalos.
2. Decide para qué es el movimiento
Cada animación necesita un trabajo. Elige uno por elemento:
| Trabajo | Ejemplo | Presupuesto típico |
|---|---|---|
| Retroalimentación — confirmar una acción | pulsar botón, alternar, copiar al portapapeles | 100–200ms |
| Orientación — mostrar de dónde vino o hacia dónde fue algo | modal que se abre desde su activador, reordenar elementos de lista, cambio de ruta | 200–400ms |
| Atención — señalar una cosa | una única secuencia de héroe, una nueva notificación | 400–1200ms, una vez |
| Narrativa — contar una historia vinculada al scroll | recorrido de producto fijado, línea de tiempo depurada | vinculado a la distancia de scroll |
Si un elemento no tiene trabajo, no lo animes. Un momento orquestado supera a un fundido de aparición en cada sección. Lee rules/motion-design.md para duraciones, easing, coreografía y la lista de verificación de "sensación premium".
3. Elige la herramienta más liviana que haga el trabajo
Plataforma primero, luego bibliotecas:
| Necesidad | Recurre a | Archivo de reglas |
|---|---|---|
| Hover, foco, pulsación, bucles simples | Transiciones CSS / @keyframes | css-keyframes.md |
| Diálogo, popover, menú desplegable entrada y salida | CSS @starting-style + transition-behavior: allow-discrete | css-keyframes.md |
| Transiciones de página / ruta, morph de elemento compartido entre estados | View Transitions API | view-transitions.md |
| Revelación simple vinculada a scroll, barra de progreso, parallax | CSS animation-timeline: view() / scroll() | scroll-animations.md |
| Secciones fijadas, líneas de tiempo depuradas de varios pasos, scroll horizontal | GSAP + ScrollTrigger | gsap.md |
| Revelaciones de titulares con texto dividido / máscara de línea | GSAP SplitText | gsap.md |
| Animación de entrada/salida en React, diseño y diseño compartido, gestos | Motion para React (motion/react) | framer-motion.md |
| Animación imperativa sin framework, muelles | Motion (motion) | motion-one.md |
| Dibujo de líneas SVG y morphing sin GSAP | Anime.js v4 (svg.createDrawable, svg.morphTo) | animejs.md |
| Morphing de rutas complejas entre formas no relacionadas | Flubber (o GSAP MorphSVG) | flubber.md |
| Reproducción de animaciones de After Effects | Lottie (dotLottie) | lottie.md |
| Animación ilustrada interactiva y dirigida por estado | Rive | rive.md |
| Gráficos animados | Transiciones D3 | d3.md |
| Muchas formas 2D interactivas (editores, diagramas) | Konva | konva.md |
| 3D, partículas, shaders | Three.js / React Three Fiber | three-js.md |
Cuando la elección no sea obvia, lee rules/choosing-library.md.
4. Constrúyelo con los no negociables
Estos se aplican a cada animación, en cada biblioteca:
- Anima
transformyopacity. Evita animarwidth,height,top,left,margin; usascale/translate, FLIP (GSAP Flip, Motionlayout) o View Transitions para cambios de diseño. Consulta rules/performance.md. - Respeta
prefers-reduced-motion. Reemplaza el movimiento con un fundido de opacidad o un cambio instantáneo; nunca elimines la entrada del contenido para que permanezca invisible. Los efectos de scroll depurado deben volverse estáticos. - Nunca ocultes contenido hasta que JS se ejecute. Establece el estado inicial oculto en JS (o detrás de una clase
.js), para que el contenido sea visible si el script falla. Con SSR, evita un destello de contenido visible- luego-oculto aplicando estilos iniciales antes de la primera pintura. - Limpia. Revierte o elimina animaciones al desmontar (
useGSAP,createScope().revert(),gsap.context), y elimina ScrollTriggers al cambiar de ruta. - Mantén la interacción receptiva. Animaciones interrumpibles para cualquier cosa iniciada por el usuario: un segundo clic no debe esperar detrás del primero. Nunca bloquees la entrada esperando que termine una animación.
- No enfoques la animación fuera de lugar. Los elementos que se mueven deben mantener un estado
:focus-visiblevisible, y los diálogos animados aún deben mover el foco correctamente.
5. Verifica en un navegador real
No informes una animación como terminada solo por leer el código. Ejecútala y verifica:
- Se reproduce y el estado final coincide con el diseño (captura de pantalla a mitad de la animación y en reposo).
- Sin errores en la consola, sin cambio de diseño al iniciar.
- Emula
prefers-reduced-motion: reducey confirma que la versión reducida funciona y el contenido es visible. - Redimensiona a un ancho móvil: las secciones fijadas y el texto dividido aún funcionan (vuelve a dividir al redimensionar).
- Los efectos de scroll funcionan al desplazarse hacia arriba y hacia abajo, y después de una recarga a mitad de página.
- Panel de rendimiento: sin fotogramas largos durante la animación, sin picos de layout/pintura de propiedades animadas.
Puntos de partida mínimos
// GSAP + ScrollTrigger (npm i gsap)
import gsap from "gsap";
import { ScrollTrigger } from "gsap/ScrollTrigger";
gsap.registerPlugin(ScrollTrigger);
gsap.from(".card", {
y: 40, opacity: 0, duration: 0.8, ease: "expo.out", stagger: 0.08,
scrollTrigger: { trigger: ".cards", start: "top 80%" },
});
// Motion para React (npm i motion)
import { motion, AnimatePresence } from "motion/react";
<AnimatePresence>
{open && (
<motion.div
key="panel"
initial={{ opacity: 0, y: 8 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: 8 }}
transition={{ type: "spring", duration: 0.35, bounce: 0 }}
/>
)}
</AnimatePresence>
/* Revelación de scroll solo con plataforma: Chrome 115+, Safari 26+, Firefox 159+ */
@media (prefers-reduced-motion: no-preference) {
@supports (animation-timeline: view()) {
.reveal {
animation: reveal linear both;
animation-timeline: view();
animation-range: entry 10% cover 30%;
}
}
}
@keyframes reveal { from { opacity: 0; translate: 0 2rem; } }
Archivos de reglas
- rules/motion-design.md — duraciones, easing, coreografía, qué hace que el movimiento se sienta premium
- rules/choosing-library.md — flujo de decisión, matriz de capacidades, tamaños de paquete medidos
- rules/performance.md — propiedades solo de compositor, perfilado,
will-change, movimiento reducido - rules/scroll-animations.md — líneas de tiempo de scroll CSS, patrones de ScrollTrigger, scroll suave
- rules/view-transitions.md — transiciones de página dentro del mismo documento y entre documentos
- rules/css-keyframes.md — transiciones,
@keyframes,@starting-style, easinglinear() - rules/gsap.md — líneas de tiempo, ScrollTrigger, SplitText, Flip,
useGSAP - rules/framer-motion.md — Motion para React: variantes,
AnimatePresence, diseño, gestos - rules/motion-one.md — Motion vanilla JS:
animate, secuencias,scroll,inView - rules/animejs.md — Anime.js v4: líneas de tiempo, escalonado, dibujo y morphing SVG
- rules/lottie.md — reproductor dotLottie, controles, eventos
- rules/rive.md — máquinas de estado y entradas
- rules/d3.md — transiciones de unión de datos
- rules/flubber.md — morphing de rutas
- rules/konva.md — formas 2D en canvas y tweens
- rules/three-js.md — escenas, R3F, rendimiento
Ejemplos funcionales de estas técnicas viven en demos/.