JS 09: Animaciones en JavaScript

Crea movimiento y vida en tus páginas web

1. ¿Qué son las Animaciones en JavaScript?

Las animaciones en JavaScript permiten crear movimiento, transiciones y efectos visuales modificando propiedades CSS o elementos del DOM en el tiempo.

Analogía: El FlipBook

Imagina un flipbook (librito de dibujos animados): cada página muestra el objeto en una posición ligeramente diferente. Al pasar las páginas rápidamente, se crea la ilusión de movimiento. Las animaciones en JavaScript funcionan igual: cambiamos propiedades repetidamente para crear esa ilusión.

2. Métodos para Animar en JavaScript

Comparación de Técnicas

Técnica Ventajas Desventajas Uso Ideal
setInterval Simple, predecible No sincronizado con refresco, consume más recursos Animaciones simples, relojes
setTimeout recursivo Más control que setInterval Código más complejo Animaciones con lógica compleja
requestAnimationFrame Óptimo para rendimiento, sincronizado a 60fps Ligeramente más complejo Animaciones suaves y profesionales
CSS Transitions/Animations Máximo rendimiento (GPU) Menos control dinámico Animaciones predefinidas simples

3. Animación con setInterval

Ya vimos setInterval en el módulo de temporizadores. Es la forma más básica de crear animaciones.

Ejemplo: Movimiento Horizontal

let posicion = 0;
const caja = document.getElementById('caja');

const animacion = setInterval(() => {
    posicion += 2;
    caja.style.left = posicion + 'px';
    
    if (posicion >= 400) {
        clearInterval(animacion);
    }
}, 16); // ~60fps (1000ms / 60 ≈ 16ms)

⚠️ Problemas de setInterval para Animaciones

  • No sincronizado: No está sincronizado con el refresco de pantalla (60Hz)
  • Consume recursos: Sigue ejecutándose aunque la pestaña esté inactiva
  • Puede saltar frames: Si el navegador está ocupado, puede dar tirones

4. requestAnimationFrame: La Forma Profesional

requestAnimationFrame() es el método moderno y óptimo para crear animaciones suaves. Le dice al navegador: "ejecuta esta función justo antes del siguiente repintado de pantalla".

Ventajas de requestAnimationFrame

  • Sincronizado a 60fps: Aprovecha el refresco nativo de la pantalla
  • Ahorro de energía: Se pausa automáticamente en pestañas inactivas
  • Suavidad: El navegador optimiza el timing para animaciones fluidas
  • Rendimiento: El navegador agrupa repintados para eficiencia

Sintaxis Básica

function animar() {
    // Tu lógica de animación aquí
    
    // Llamada recursiva para continuar
    requestAnimationFrame(animar);
}

// Iniciar la animación
requestAnimationFrame(animar);

Ejemplo: Movimiento con requestAnimationFrame

let posicion = 0;
let animacionId = null;
const caja = document.getElementById('caja');

function animar() {
    posicion += 2;
    caja.style.left = posicion + 'px';
    
    if (posicion < 400) {
        animacionId = requestAnimationFrame(animar);
    }
}

// Iniciar
animacionId = requestAnimationFrame(animar);

// Detener (cuando sea necesario)
cancelAnimationFrame(animacionId);

5. Control de Velocidad con Delta Time

Para animaciones independientes de la velocidad de refresco, usamos delta time (tiempo transcurrido entre frames).

let ultimoTiempo = 0;
let posicion = 0;

function animar(tiempoActual) {
    // Calcular tiempo transcurrido desde el último frame (en segundos)
    const deltaTime = (tiempoActual - ultimoTiempo) / 1000;
    ultimoTiempo = tiempoActual;
    
    // Velocidad en píxeles por segundo
    const velocidad = 100; // 100px/s
    
    posicion += velocidad * deltaTime;
    caja.style.left = posicion + 'px';
    
    if (posicion < 400) {
        requestAnimationFrame(animar);
    }
}

requestAnimationFrame(animar);

Ejemplo Interactivo: Velocidad Constante

Esta animación mantiene velocidad constante independientemente de los FPS

6. Ejemplo Avanzado: Animación Compleja

Bola Rebotando

let x = 0, y = 0;
let velocidadX = 3;
let velocidadY = 2;
const bola = document.getElementById('bola');
const contenedor = document.getElementById('canvas_rebotar');

function rebotar() {
    // Actualizar posición
    x += velocidadX;
    y += velocidadY;
    
    // Rebotar en los bordes
    if (x <= 0 || x >= contenedor.offsetWidth - 30) {
        velocidadX = -velocidadX;
    }
    if (y <= 0 || y >= contenedor.offsetHeight - 30) {
        velocidadY = -velocidadY;
    }
    
    // Aplicar posición
    bola.style.left = x + 'px';
    bola.style.top = y + 'px';
    
    requestAnimationFrame(rebotar);
}

requestAnimationFrame(rebotar);

7. Casos de Uso Reales

Scroll Suave

function scrollSuave(destino, duracion) {
    const inicio = window.scrollY;
    const distancia = destino - inicio;
    const tiempoInicio = performance.now();
    
    function paso(tiempoActual) {
        const transcurrido = tiempoActual - tiempoInicio;
        const progreso = Math.min(transcurrido / duracion, 1);
        
        // Función de easing (suavizado)
        const easing = progreso * (2 - progreso); // easeOutQuad
        
        window.scrollTo(0, inicio + distancia * easing);
        
        if (progreso < 1) {
            requestAnimationFrame(paso);
        }
    }
    
    requestAnimationFrame(paso);
}

// Uso: scrollSuave(1000, 500); // Scroll a Y=1000 en 500ms

Contador Animado

function contadorAnimado(elemento, valorFinal, duracion) {
    const valorInicial = 0;
    const tiempoInicio = performance.now();
    
    function actualizar(tiempoActual) {
        const transcurrido = tiempoActual - tiempoInicio;
        const progreso = Math.min(transcurrido / duracion, 1);
        
        const valorActual = Math.floor(valorInicial + (valorFinal - valorInicial) * progreso);
        elemento.textContent = valorActual;
        
        if (progreso < 1) {
            requestAnimationFrame(actualizar);
        }
    }
    
    requestAnimationFrame(actualizar);
}

8. Buenas Prácticas

✅ Recomendaciones

  • Usa requestAnimationFrame para animaciones visuales
  • Siempre guarda el ID para poder cancelar la animación
  • Usa delta time para animaciones independientes de FPS
  • Prefiere transformar propiedades CSS (transform, opacity) sobre position
  • Usa will-change en CSS para animaciones complejas

❌ Evitar

  • Animar con setInterval cuando necesitas suavidad
  • Modificar el layout (width, height, position) en loops rápidos
  • Olvidar cancelar animaciones que ya no necesitas
  • Animar demasiados elementos simultáneamente sin optimizar
Volver a JS