ToolHub
View All Posts

Guía de animación CSS: Transiciones, keyframes y rendimiento

Las animaciones CSS dan vida a las interfaces, proporcionando retroalimentación visual, guiando la atención del usuario y creando experiencias memorables. Las animaciones bien hechas se sienten naturales e intuitivas; las mal hechas causan tartamudeo, distraen a los usuarios e incluso provocan malestar físico. Esta guía cubre todo, desde los fundamentos de transiciones y keyframes hasta optimización avanzada de rendimiento y accesibilidad, brindándote el conocimiento para crear animaciones fluidas, con propósito e inclusivas.

Transiciones y animaciones CSS

CSS ofrece dos mecanismos distintos para crear movimiento: transiciones y animaciones. Comprender cuándo usar cada uno es fundamental para trabajar eficazmente con animaciones CSS.

Transiciones CSS

Las transiciones animan los cambios entre dos estados de una propiedad CSS. Defines qué propiedad animar, la duración y la curva de easing, y el navegador maneja automáticamente la interpolación entre los valores inicial y final. Las transiciones se activan mediante cambios de estado como hover, focus, adición de clases o cambios de propiedad desde JavaScript.

.button {
  background: #3b82f6;
  transform: scale(1);
  transition: background 0.3s ease, transform 0.2s ease;
}

.button:hover {
  background: #2563eb;
  transform: scale(1.05);
}

Las transiciones son perfectas para animaciones simples de dos estados. Requieren un código mínimo, son fáciles de entender y funcionan de forma fiable en todos los navegadores. La limitación es que solo animan entre dos valores: no puedes definir pasos intermedios.

Animaciones CSS (keyframes)

Las animaciones usan la regla @keyframes para definir una secuencia de cambios de estilo en cualquier número de pasos. A diferencia de las transiciones, las animaciones pueden ejecutarse independientemente de los cambios de estado, repetirse infinitamente, reproducirse en reversa y pausarse. Te dan control preciso sobre cada fotograma de la animación.

@keyframes pulse {
  0% {
    transform: scale(1);
    opacity: 1;
  }
  50% {
    transform: scale(1.05);
    opacity: 0.8;
  }
  100% {
    transform: scale(1);
    opacity: 1;
  }
}

.pulse-element {
  animation: pulse 2s ease-in-out infinite;
}

Cuándo usar cada una

EscenarioUsar transiciónUsar animación
Efecto hoverNo
Estado focusNo
Alternar visibilidadPosiblemente
Secuencia de múltiples pasosNo
Animación en bucleNo
Independiente del estadoNo
Animación de entradaPosiblemente

Sintaxis de keyframes en profundidad

La regla @keyframes es el corazón de las animaciones CSS. Comprender completamente su sintaxis desbloquea todo el poder de las secuencias de animación de múltiples pasos.

Keyframes basados en porcentaje

Los keyframes se definen usando valores porcentuales de 0% a 100%, que representan el progreso en la línea de tiempo de la animación. Puedes definir tantos keyframes como necesites, y el navegador interpola entre ellos:

@keyframes bounce {
  0%, 100% {
    transform: translateY(0);
  }
  25% {
    transform: translateY(-20px);
  }
  50% {
    transform: translateY(0);
  }
  75% {
    transform: translateY(-10px);
  }
}

Múltiples keyframes pueden compartir el mismo bloque de estilo separando los valores porcentuales con comas, como 0%, 100% en el ejemplo anterior. Esto es útil para animaciones que regresan al estado inicial.

Propiedad abreviada animation

La propiedad abreviada animation combina ocho subpropiedades en una sola declaración. El orden es: name, duration, timing-function, delay, iteration-count, direction, fill-mode y play-state.

.element {
  animation: slideIn 0.5s ease-out 0.2s 1 forwards running;
}

/* Equivalent longhand */
.element {
  animation-name: slideIn;
  animation-duration: 0.5s;
  animation-timing-function: ease-out;
  animation-delay: 0.2s;
  animation-iteration-count: 1;
  animation-direction: normal;
  animation-fill-mode: forwards;
  animation-play-state: running;
}

Modos de relleno de animación

La propiedad animation-fill-mode controla qué estilos se aplican antes y después de que se ejecute la animación. Es una de las propiedades de animación más malinterpretadas:

Funciones de easing

Las funciones de easing (también llamadas funciones de temporización) definen la curva de aceleración de una animación. Determinan si la animación comienza lentamente y luego acelera, comienza rápido y luego desacelera, o sigue un patrón más complejo. La función de easing correcta hace que la animación se sienta natural; la incorrecta la hace sentir mecánica o abrupta.

Funciones de easing integradas

Curvas cubic-bezier

La función cubic-bezier() te permite definir curvas de easing personalizadas con control preciso. Acepta cuatro parámetros que representan los puntos de control de una curva de Bézier cúbica: cubic-bezier(x1, y1, x2, y2). Los valores x representan tiempo (0 a 1), los valores y representan progreso (pueden exceder 0-1 para efectos de sobreimpulso).

.element {
  /* Smooth deceleration - great for entrances */
  transition: transform 0.3s cubic-bezier(0.0, 0.0, 0.2, 1.0);

  /* Smooth acceleration - great for exits */
  transition: transform 0.2s cubic-bezier(0.4, 0.0, 1, 1);

  /* Material Design standard easing */
  transition: transform 0.3s cubic-bezier(0.4, 0.0, 0.2, 1);
}

Funciones de pasos (steps)

La función steps() crea animaciones discretas fotograma a fotograma en lugar de interpolación suave. Es útil para animaciones de sprites o para crear un efecto de 'escritura':

@keyframes typing {
  from { width: 0; }
  to { width: 100%; }
}

.typing-effect {
  animation: typing 3s steps(40, end);
  overflow: hidden;
  white-space: nowrap;
}

Optimización de rendimiento

Las animaciones fluidas requieren 60 fotogramas por segundo (fps), lo que significa que cada fotograma debe completarse en 16.67 milisegundos. No cumplir este presupuesto provoca tartamudeo visible: vibración de la pantalla, pérdida de fotogramas y degradación de la experiencia del usuario. Comprender el pipeline de renderizado del navegador es clave para escribir animaciones de alto rendimiento.

Pipeline de renderizado

Cuando una propiedad CSS cambia, el navegador pasa por hasta tres etapas para actualizar la pantalla:

  1. Estilo: Determina qué reglas CSS se aplican a qué elementos.
  2. Layout: Calcula el tamaño y la posición de cada elemento. Activado por cambios en width, height, margin, padding, top, left, font-size y otras propiedades geométricas.
  3. Paint: Rellena los píxeles de cada elemento. Activado por cambios en color, background, box-shadow, border-radius y otras propiedades visuales.
  4. Composición: Combina las capas pintadas en el orden correcto. Solo este paso se ejecuta en la GPU. Activado por cambios en transform y opacity.

Las animaciones que solo activan el paso de composición tienen el mejor rendimiento porque evitan completamente el layout y el paint. Por eso transform y opacity son el estándar de oro para animaciones CSS de alto rendimiento.

Propiedades solo de compositor

Propiedad¿Activa layout?¿Activa paint?¿Composición GPU?
transformNoNo
opacityNoNo
filterNoA veces
top / leftNo
width / heightNo
margin / paddingNo
background-colorNoNo
box-shadowNoNo

Uso de will-change

La propiedad will-change le dice al navegador qué propiedades se espera que cambien, permitiéndole optimizar por adelantado promoviendo el elemento a su propia capa del compositor. Sin embargo, debe usarse con moderación:

/* Apply will-change just before the animation */
.element:hover {
  will-change: transform;
}

/* Remove it after the animation completes */
.element {
  transition: transform 0.3s ease;
}

Los errores comunes con will-change incluyen aplicarlo a demasiados elementos (cada capa del compositor consume memoria GPU) y mantenerlo permanentemente (desperdiciando recursos). Una buena práctica es añadir will-change mediante JavaScript justo antes de que comience la animación y eliminarlo cuando termine.

Usar Transform en lugar de propiedades de layout

La optimización de rendimiento más efectiva es reemplazar las propiedades que activan el layout con transform:

Regla de rendimiento: Si puedes lograr el mismo efecto visual con transform u opacity, priorízalos siempre sobre las propiedades que activan layout o paint. Este principio elimina la mayoría de los problemas de rendimiento de animación.

Patrones comunes de animación

Ciertos patrones de animación aparecen repetidamente en las interfaces web. Dominar estos patrones te proporciona un kit de herramientas para la mayoría de las necesidades comunes de animación UI.

1. Fade in

La animación de entrada más simple y versátil. El elemento pasa de transparente a opaco:

@keyframes fadeIn {
  from { opacity: 0; }
  to { opacity: 1; }
}

.fade-in {
  animation: fadeIn 0.4s ease-out forwards;
}

2. Deslizar hacia arriba con fade

Un efecto de entrada más dinámico que combina movimiento vertical y cambio de opacidad. Esto crea la sensación de que el elemento llega desde abajo:

@keyframes slideUpFade {
  from {
    opacity: 0;
    transform: translateY(20px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

.slide-up {
  animation: slideUpFade 0.5s cubic-bezier(0.4, 0, 0.2, 1) forwards;
}

3. Escala al hover

Un sutil aumento de escala al pasar el cursor proporciona una retroalimentación interactiva satisfactoria:

.card {
  transition: transform 0.2s ease, box-shadow 0.2s ease;
}

.card:hover {
  transform: translateY(-4px) scale(1.02);
  box-shadow: 0 12px 24px rgba(0, 0, 0, 0.15);
}

4. Animación de lista escalonada

Cuando aparece una lista de elementos, establecer retrasos de animación escalonados para cada elemento crea un efecto de cascada que guía la mirada:

.list-item {
  opacity: 0;
  transform: translateY(10px);
  animation: slideUpFade 0.3s ease-out forwards;
}

.list-item:nth-child(1) { animation-delay: 0.0s; }
.list-item:nth-child(2) { animation-delay: 0.05s; }
.list-item:nth-child(3) { animation-delay: 0.1s; }
.list-item:nth-child(4) { animation-delay: 0.15s; }
.list-item:nth-child(5) { animation-delay: 0.2s; }

Para listas dinámicas, usa JavaScript para establecer animation-delay basado en el índice de cada elemento, o usa propiedades personalizadas CSS: style="animation-delay: calc(var(--i) * 0.05s)".

5. Spinner de carga

La rotación continua y suave es el clásico indicador de carga:

@keyframes spin {
  to { transform: rotate(360deg); }
}

.spinner {
  width: 24px;
  height: 24px;
  border: 3px solid rgba(255, 255, 255, 0.2);
  border-top-color: #3b82f6;
  border-radius: 50%;
  animation: spin 0.8s linear infinite;
}

Accesibilidad en animaciones: prefers-reduced-motion

No todos los usuarios se benefician de las animaciones. Para personas con trastornos vestibulares, sensibilidad al movimiento o ciertas condiciones cognitivas, las animaciones pueden causar mareos, náuseas o convulsiones. La media query prefers-reduced-motion te permite respetar la preferencia de reducción de animación a nivel de sistema del usuario.

Implementar reducción de animación

El enfoque recomendado es habilitar las animaciones para usuarios que no tienen establecida la preferencia de reducción de animación:

/* Default: no animation for users who prefer reduced motion */
.element {
  opacity: 1;
  transform: none;
}

/* Only animate for users who have no motion preference */
@media (prefers-reduced-motion: no-preference) {
  .element {
    animation: slideUpFade 0.5s ease-out forwards;
  }
}

Alternativamente, puedes deshabilitar animaciones específicas para usuarios que prefieren animación reducida:

.element {
  animation: slideUpFade 0.5s ease-out forwards;
}

@media (prefers-reduced-motion: reduce) {
  .element {
    animation: none;
    opacity: 1;
    transform: none;
  }

  /* Keep functional transitions but shorten duration */
  * {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
  }
}

Qué conservar y qué eliminar

Cuando los usuarios prefieren animación reducida, no todas las animaciones deben eliminarse. Las animaciones funcionales que transmiten significado o estado deben conservarse, solo simplificadas:

Bibliotecas de animación

Aunque las animaciones CSS manejan la mayoría de las necesidades UI, las bibliotecas de animación JavaScript ofrecen características avanzadas como movimiento basado en física, animaciones vinculadas al scroll y orquestación de secuencias complejas.

Bibliotecas CSS-first

Bibliotecas de animación JavaScript

Cuándo usar JavaScript vs CSS

CSS para animaciones declarativas simples que responden a cambios de estado. JavaScript para: movimiento basado en física (resortes, momento), animaciones vinculadas al scroll, secuencias complejas con control de tiempo preciso, parámetros de animación dinámicos calculados en tiempo de ejecución, o animaciones que necesitan pausarse, invertirse o arrastrarse programáticamente.

Depuración de animaciones

Los bugs de animación pueden ser sutiles y difíciles de diagnosticar. Las herramientas y técnicas de depuración adecuadas hacen el proceso más eficiente.

Panel de animaciones de Chrome DevTools

Chrome DevTools incluye un panel de animaciones dedicado que proporciona potentes capacidades de depuración:

Análisis de rendimiento

Usa el panel Performance en Chrome DevTools para identificar el tartamudeo de animaciones:

Escenarios comunes de depuración

¿Trabajas con CSS? Prueba nuestras herramientas CSS gratuitas para formatear, minificar y optimizar tus hojas de estilo para producción.

Formateador CSSMinificador CSS

Preguntas frecuentes

¿Debo usar transiciones CSS o animaciones CSS?

Las transiciones CSS son para cambios de estado simples de punto A a punto B, como efectos hover, estados de focus y alternar visibilidad. Las animaciones CSS (keyframes) son para secuencias complejas de múltiples pasos, animaciones en bucle o animaciones que necesitan ejecutarse independientemente de los cambios de estado. Las transiciones son más simples; las animaciones ofrecen más control.

¿Qué propiedades CSS son seguras para animar con buen rendimiento?

Las propiedades con mejor rendimiento para animar son transform y opacity, porque pueden ser manejadas completamente por el compositor GPU sin necesidad de activar layout o paint. Otras propiedades como width, height, margin y top activan el recálculo del layout, lo cual es costoso. Prioriza siempre transform: translateX() sobre left, y transform: scale() sobre cambios de width/height.

¿Cómo hacer accesibles las animaciones para usuarios sensibles?

Usa la media query prefers-reduced-motion para deshabilitar o simplificar animaciones para usuarios que tienen habilitada la configuración de 'reducir animación' en su sistema operativo. Envuelve las animaciones no esenciales en @media (prefers-reduced-motion: no-preference) y proporciona cambios de estado instantáneos como alternativa para usuarios que prefieren animación reducida.

¿Qué es will-change y cuándo debo usarlo?

will-change es una propiedad CSS que indica al navegador qué propiedades cambiarán en el futuro, permitiéndole optimizar por adelantado. Úsalo con moderación, solo cuando observes problemas de rendimiento. Aplícalo antes de que comience la animación y elimínalo cuando termine. El uso excesivo de will-change desperdicia memoria GPU y puede perjudicar el rendimiento.

¿Cómo depurar animaciones CSS?

Chrome DevTools proporciona potentes herramientas de depuración de animaciones. Abre el panel de animaciones (en el menú Más herramientas) para ver todas las animaciones activas, arrastrar la línea de tiempo, modificar duraciones y retrasos en tiempo real, y ralentizar las animaciones a 1/4 de velocidad. También puedes usar el panel Performance para identificar tartamudeo de animaciones y layout thrashing.