Saltar al contenido

CSS, de a poco

Curso de CSS

Animaciones

Lección 14 de 37 · 4 min

Una transición necesita que algo cambie para moverse. Una animación no: arranca sola apenas se aplica, puede tener todos los pasos que quieras y repetirse.

@keyframes y animation

Una animación tiene dos partes. Los fotogramas clave (@keyframes) llevan un nombre y describen estados en el tiempo: from equivale a 0% y to a 100%, y en el medio podés poner los porcentajes que quieras. Si falta el 0% o el 100%, el navegador usa el valor que el elemento ya tiene.

beat.css
@keyframes beat {
0%, 100% { transform: scale(1); opacity: 1; }
50% { transform: scale(1.4); opacity: 0.6; }
}

La otra parte es la propiedad animation, que reproduce esos fotogramas en un elemento. Junta ocho propiedades:

  1. Nombreanimation-nameEl nombre de los fotogramas clave.
  2. Duraciónanimation-durationCuánto dura un ciclo.
  3. Curvaanimation-timing-functionSe aplica a cada tramo entre dos fotogramas, no a la animación entera.
  4. Retardoanimation-delayCuánto espera antes de arrancar.
  5. Repeticionesanimation-iteration-countCuántas veces se repite; infinite no para nunca.
  6. Direcciónanimation-directionnormal va de 0% a 100%; alternate va y vuelve.
  7. Rellenoanimation-fill-modeQué se ve fuera de la animación. Con none vuelve a su estilo, forwards deja el último fotograma, backwards aplica el primero durante el delay y both hace las dos cosas.
  8. Estadoanimation-play-staterunning o paused.

Mové los controles, usá Pausa y Repetir, y mirá cómo cambia la versión corta. La regla de abajo sigue el progreso por los @keyframes: su marca va de 0% a 100% con la misma curva, delay, repeticiones y dirección que el disco.

animation-play-state: runningslide 1s ease-in-out infinite alternate
@keyframes
iteration-count
direction
disco.css
@keyframes slide {
from { transform: translateX(-80px); }
to { transform: translateX(80px); }
}
.disco {
animation: slide 1s ease-in-out infinite alternate;
}
disco.html
<!-- los @keyframes van en el CSS, como en disco.css -->
<img src="disco.png" alt="" class="animate-[slide_1s_ease-in-out_infinite_alternate] motion-reduce:animate-none" />

La versión corta suele escribirse en este orden: nombre, duración, curva, delay, repeticiones, dirección, fill-mode y play-state. Lo que el navegador exige es que el primer tiempo sea la duración y el segundo, el delay.

Transición o animación

Usá una transición cuando algo pasa de un estado a otro por una causa: hover, foco, una clase nueva. Usá una animación cuando el movimiento tiene que arrancar solo, tener más de dos pasos o repetirse, como un indicador de carga.

styles.css
.boton { transition: transform 200ms ease; }
.boton:hover { transform: scale(1.05); }
.cargando { animation: turn 1s linear infinite; }

En los dos casos, lo más liviano de animar es transform y opacity: el navegador puede resolverlas al componer la página, sin recalcular el layout ni volver a pintar. Animar width, top o left recalcula el layout en cada fotograma, y en un celular se nota.

La curva steps(4) cambia el movimiento continuo por cuatro saltos en cada tramo. Sirve para animar cuadro por cuadro, como un sprite. Con turn y steps(4), el disco pasa por 0, 90, 180 y 270 grados, y vuelve a empezar.

Una tira de película con cuatro cuadros: el disco a 0, 90, 180 y 270 grados. Con steps(4), la vuelta completa se hace en esos cuatro saltos.

Movimiento accesible

A algunas personas el movimiento en pantalla les da mareo o náuseas, por ejemplo con trastornos vestibulares. Los sistemas operativos tienen una opción para reducirlo, y CSS la lee con la media query prefers-reduced-motion.

Si vale reduce, sacá el movimiento o cambialo por algo quieto. No alcanza con acortar la duración: un loop infinito más corto gira más rápido, que es peor. Por eso la demo, si tu sistema lo pide, arranca en pausa.

disco.css
@media (prefers-reduced-motion: reduce) {
.disco { animation: none; }
}
  1. DestellosWCAG 2.3.1 · nivel ANada puede destellar más de tres veces en un segundo, salvo que el destello quede por debajo de los umbrales de tamaño y contraste.
  2. PausaWCAG 2.2.2 · nivel ASi algo se mueve solo, dura más de cinco segundos y se ve junto a otro contenido, tiene que haber una forma de pausarlo, detenerlo u ocultarlo. Un infinite entra en esta regla.

El botón Pausa de la demo cumple esa regla: es un button con aria-pressed que cambia animation-play-state.

En Tailwind

Tailwind trae cuatro animaciones listas: animate-spin, animate-ping, animate-pulse y animate-bounce. Para las tuyas, los @keyframes siguen en CSS y la animación va entre corchetes, con guiones bajos en vez de espacios. Si la vas a reusar, definila en @theme: --animate-slide crea la clase animate-slide.

CSSTailwind
animation: slide 1s infiniteanimate-[slide_1s_infinite]
animation: noneanimate-none
--animate-slide en @themeanimate-slide
prefers-reduced-motion: reducemotion-reduce:*
prefers-reduced-motion: no-preferencemotion-safe:*
animation-play-state: paused[animation-play-state:paused]
disco.html
<!-- en el CSS:
@theme {
--animate-slide: slide 1s ease-in-out infinite alternate;
@keyframes slide {
from { transform: translateX(-80px); }
to { transform: translateX(80px); }
}
}
-->
<div class="animate-slide motion-reduce:animate-none">…</div>
<!-- solo gira si el sistema no pide menos movimiento -->
<div class="motion-safe:animate-spin">…</div>