requestAnimationFrame: Animaciones Fluidas y Optimizadas
Aprende a crear animaciones de alto rendimiento sincronizadas con el ciclo de repintado del navegador para lograr 60 FPS fluidos.
TL;DR - Resumen rápido
- requestAnimationFrame sincroniza animaciones con el ciclo de repintado del navegador
- Es más eficiente que setInterval para animaciones porque pausa en pestañas inactivas
- Devuelve un ID único que puedes usar para cancelar la animación con cancelAnimationFrame
- Recibe un timestamp como parámetro para calcular delta time y animaciones independientes de framerate
- Es la API estándar para animaciones web modernas y es compatible con todos los navegadores
Introducción a requestAnimationFrame
Las animaciones en el navegador siempre han sido un desafío de rendimiento. Antes de requestAnimationFrame, los desarrolladores usaban setInterval o setTimeout para crear animaciones, pero estos métodos tenían problemas fundamentales: no se sincronizaban con el ciclo de refresco del navegador y consumían recursos innecesarios cuando la pestaña no estaba activa. requestAnimationFrame resuelve estos problemas proporcionando una API optimizada específicamente para animaciones.
Esta API fue introducida en 2011 y rápidamente se convirtió en el estándar para animaciones web modernas. Su principal ventaja es que el navegador puede optimizar la animación pausándola cuando la pestaña no está visible, reduciendo el consumo de CPU y batería. Además, se ejecuta justo antes del siguiente repintado del navegador, garantizando que las animaciones se vean fluidas y sin tearing visual.
¿Qué es requestAnimationFrame?
requestAnimationFrame es un método del objeto window que te permite programar una función para que se ejecute antes del siguiente repintado del navegador. A diferencia de setInterval, que ejecuta código en intervalos fijos sin considerar el estado del navegador, requestAnimationFrame se adapta dinámicamente a la tasa de refresco del dispositivo (generalmente 60 Hz, pero puede ser 120 Hz o 144 Hz en monitores de alta frecuencia).
- <strong>Sincronización con el navegador:</strong> Se ejecuta justo antes de que el navegador repinte la pantalla
- <strong>Optimización automática:</strong> Se pausa cuando la pestaña está en segundo plano o minimizada
- <strong>Timestamp incluido:</strong> Recibe un timestamp en milisegundos para calcular delta time
- <strong>Retorno de ID:</strong> Devuelve un identificador único para cancelar la animación cuando sea necesario
- <strong>60 FPS objetivo:</strong> Optimizado para alcanzar 60 cuadros por segundo (o la frecuencia del monitor)
¿Por qué 60 FPS?
La mayoría de los monitores modernos se actualizan a 60 Hz, lo que significa 60 refrescos por segundo. Para lograr animaciones fluidas, el navegador debe completar cada cuadro en aproximadamente 16.67ms (1000ms ÷ 60). requestAnimationFrame está diseñado para cumplir con este objetivo, adaptándose automáticamente a monitores de mayor frecuencia (120Hz, 144Hz, etc.).
Uso Básico
El patrón básico de requestAnimationFrame consiste en llamar al método con una función de callback que contiene la lógica de animación. Dentro de este callback, actualizas el estado de la animación y luego vuelves a llamar a requestAnimationFrame para crear un bucle de animación continuo.
Animación Simple
Este ejemplo muestra cómo crear una animación básica que mueve un elemento horizontalmente. La función de animación actualiza la posición y solicita el siguiente cuadro.
En este ejemplo, creamos un bucle de animación que mueve un elemento de izquierda a derecha. La función animar se llama recursivamente usando requestAnimationFrame, lo que crea un ciclo continuo. Cada cuadro, incrementamos la posición en 2 píxeles y aplicamos el cambio mediante transform CSS, que es más eficiente que manipular left/top porque no causa reflow.
Mejor práctica: usa transform en lugar de left/top
Para animaciones de posición, usa transform: translateX() en lugar de modificar las propiedades left o top. Las transformaciones se ejecutan en el compositor del navegador y no causan reflow, lo que resulta en animaciones mucho más fluidas y con mejor rendimiento.
Animación con Timestamp
El callback de requestAnimationFrame recibe un timestamp que representa el tiempo en milisegundos desde que la página se cargó. Este valor es crucial para crear animaciones que sean consistentes independientemente del framerate del dispositivo.
Este ejemplo calcula el delta time (tiempo transcurrido desde el último cuadro) para hacer que la animación sea independiente del framerate. La velocidad se expresa en píxeles por segundo, y multiplicamos por el delta time en segundos para obtener el desplazamiento correcto para el cuadro actual. Esto garantiza que la animación se vea a la misma velocidad en monitores de 60Hz, 120Hz o 144Hz.
Cancelar Animaciones
requestAnimationFrame devuelve un identificador único (un número entero) que puedes usar para cancelar la animación en cualquier momento usando cancelAnimationFrame. Esto es esencial para evitar fugas de memoria y detener animaciones cuando ya no son necesarias.
Cancelación Básica
Este ejemplo muestra cómo detener una animación cuando se cumple una condición específica, como cuando el elemento llega al final de la pantalla.
Guardamos el ID devuelto por requestAnimationFrame en una variable. Cuando la posición del elemento supera el ancho de la ventana, llamamos a cancelAnimationFrame con ese ID para detener el bucle de animación. Siempre es buena práctica cancelar las animaciones cuando el componente o elemento se elimina del DOM para evitar que sigan ejecutándose en segundo plano.
Advertencia: Fugas de memoria
Si no cancelas las animaciones cuando el usuario navega a otra página o cuando un componente se desmonta, las animaciones seguirán ejecutándose en segundo plano, consumiendo CPU y batería. Siempre guarda el ID de requestAnimationFrame y cancélalo cuando ya no sea necesario.
Animaciones con Easing
Las animaciones lineales (velocidad constante) pueden verse poco naturales. En animaciones profesionales, se usan funciones de easing (suavizado) para crear movimientos más orgánicos que aceleran al inicio y desaceleran al final. requestAnimationFrame combinado con el timestamp te permite implementar estas animaciones de forma precisa.
Este ejemplo usa una función de easing llamada easeInOutQuad, que crea una curva de aceleración suave al principio y al final de la animación. El progreso se calcula dividiendo el tiempo transcurrido por la duración total, obteniendo un valor entre 0 y 1. Luego aplicamos la función de easing a ese progreso para obtener un movimiento más natural. Este patrón es la base de librerías de animación como GSAP o Anime.js, pero implementado en JavaScript vanilla.
Funciones de easing comunes
Además de easeInOutQuad, existen muchas funciones de easing: easeInQuad (solo acelera), easeOutQuad (solo desacelera), easeInOutCubic, easeInElastic (efecto rebote), etc. Puedes encontrar implementaciones en easings.net o crear las tuyas propias para efectos únicos.
Comparación con setInterval
Aunque setInterval puede usarse para animaciones, requestAnimationFrame tiene varias ventajas significativas que lo hacen superior para este propósito. Entender estas diferencias te ayudará a tomar decisiones informadas sobre cuándo usar cada método.
Comparación Directa
Este ejemplo compara ambas implementaciones mostrando las diferencias de comportamiento.
La implementación con setInterval ejecuta la animación en intervalos fijos de 16ms, independientemente de si el navegador está listo para repintar. Esto puede causar problemas como frames perdidos o animaciones que se ven entrecortadas. Además, setInterval sigue ejecutándose incluso cuando la pestaña está inactiva, consumiendo recursos innecesarios. requestAnimationFrame, por otro lado, se adapta al ciclo del navegador y se pausa automáticamente cuando la pestaña no está visible.
- <strong>Sincronización:</strong> requestAnimationFrame se sincroniza con el repintado del navegador
- <strong>Optimización:</strong> Se pausa en pestañas inactivas, ahorrando batería y CPU
- <strong>Consistencia:</strong> Usa el timestamp para animaciones independientes de framerate
- <strong>Garantía de ejecución:</strong> El navegador puede agrupar múltiples callbacks en un solo repintado
- <strong>Compatibilidad con CSS:</strong> Funciona mejor con animaciones y transiciones CSS
Errores Comunes
Al trabajar con requestAnimationFrame, hay varios errores que los desarrolladores cometen frecuentemente. Conocer estos patrones problemáticos te ayudará a evitarlos y escribir código más robusto y eficiente.
Error: Múltiples Animaciones Simultáneas
Un error común es iniciar múltiples animaciones sin cancelar las anteriores, lo que resulta en animaciones que se aceleran progresivamente y consumen recursos excesivos.
En este ejemplo, cada vez que se hace clic en el botón, se inicia una nueva animación sin cancelar la anterior. Esto causa que el elemento se mueva cada vez más rápido porque hay múltiples bucles de animación ejecutándose simultáneamente, todos modificando el mismo elemento. La solución es guardar el ID de la animación actual y cancelarla antes de iniciar una nueva.
La solución verifica si ya hay una animación en ejecución (comprobando si animationId no es null) y la cancela antes de iniciar una nueva. Esto garantiza que solo haya un bucle de animación activo a la vez, evitando el problema de aceleración progresiva.
Error: No Usar el Timestamp
Otro error común es no usar el timestamp proporcionado por requestAnimationFrame, lo que resulta en animaciones que se ven a diferentes velocidades en dispositivos con diferentes tasas de refresco.
Este código incrementa la posición en una cantidad fija por cuadro sin considerar el tiempo transcurrido. En un monitor de 60Hz, la animación se moverá a 120 píxeles por segundo (2px × 60 cuadros). En un monitor de 144Hz, se moverá a 288 píxeles por segundo (2px × 144 cuadros), haciendo que la animación se vea más del doble de rápido. Usar el timestamp para calcular el delta time resuelve este problema.
Cuándo NO usar requestAnimationFrame
requestAnimationFrame es solo para actualizaciones visuales. NO lo uses para lógica de negocio, temporizadores, polling de APIs o tareas que deben ejecutarse en segundo plano. Para esas tareas, usa setInterval o setTimeout. Si necesitas ejecutar tareas pesadas sin bloquear la UI, considera Web Workers.
Resumen: requestAnimationFrame
Conceptos principales:
- •requestAnimationFrame sincroniza animaciones con el ciclo de repintado del navegador
- •Devuelve un ID único que se usa con cancelAnimationFrame para detener animaciones
- •El callback recibe un timestamp en milisegundos desde que la página se cargó
- •Se pausa automáticamente cuando la pestaña está inactiva o minimizada
- •Funciones de easing transforman animaciones lineales en movimientos suaves y naturales
Mejores prácticas:
- •Usa transform CSS en lugar de left/top para animaciones de posición
- •Calcula delta time usando el timestamp para animaciones independientes de framerate
- •Guarda el ID de requestAnimationFrame y cancélalo cuando la animación termine
- •Verifica si ya existe una animación activa antes de iniciar una nueva
- •Úsalo solo para actualizaciones visuales, no para lógica de negocio o tareas en segundo plano