05 Capítulo 5 · Lección 10

Animaciones

Redactado por
Author Sebastian V.
Fecha de publicación

18/02/2026

Tiempo de lectura

6 min

Tema

Three.js

El Latido del Mundo

Un mundo estático en 3D es solo una fotografía aburrida. Para que las cosas se sientan vivas y dinámicas (un molino girando, partículas flotando, naves avanzando), necesitamos el concepto de Game Loop o Bucle de Animación.

requestAnimationFrame

En el capítulo de Keyframes, dejábamos que CSS calculara todo. En WebGL, tú eres el dueño del bucle. El navegador nos ofrece la función requestAnimationFrame(funcion). Su única tarea es llamar a una función justo antes de pintar el siguiente frame de la pantalla. Si tu pantalla corre a 60 hercios (60 fps), llamará a tu función 60 veces por segundo.

El problema de los FPS

Si simplemente sumas mesh.rotation.y += 0.01 en cada frame, y mi ordenador es potentísimo corriendo a 144 fps, mi cubo girará al doble de velocidad que el tuyo si tu pantalla es de 60 fps. Esto arruina las experiencias jugables o sincrónicas.

Clock al rescate

Para solucionar que la velocidad dependa del hardware, Three.js trae THREE.Clock(). En lugar de sumar un número fijo cada frame, le preguntas al reloj "¿Cuánto tiempo (Delta) ha pasado desde el frame anterior?". Multiplicas tu movimiento por ese tiempo. Así, tu cubo girará siempre a 90 grados por segundo de forma real, ya sea en un iPhone 8 o en una PC Gamer de 5000 dólares.

Consejo profesional

GSAP se lleva maravillosamente bien con Three.js. Puedes usar gsap.to(mesh.position, { x: 5, duration: 2 }) para animar cosas a lugares exactos con easings, mientras requestAnimationFrame se encarga solo de mantener el renderizado activo.

El bucle perfecto usando Delta Time

> _
// 1. Instanciamos el Reloj
const clock = new THREE.Clock();

function animate() {
  // Solicitamos el próximo frame inmediatamente
  requestAnimationFrame(animate);

  // Obtenemos el tiempo transcurrido desde el último frame
  const elapsedTime = clock.getElapsedTime(); // Tiempo total
  const deltaTime = clock.getDelta(); // Tiempo entre frames

  // Rotación basada en el tiempo total (ideal para movimientos cíclicos continuos)
  mesh.rotation.y = elapsedTime * Math.PI * 0.5; // Medio giro por segundo

  // Oscilación estilo flotación con funciones trigonométricas
  mesh.position.y = Math.sin(elapsedTime * 2) * 0.5;

  // Renderizado final
  renderer.render(scene, camera);
}

animate();
> _

Una geometría flotando suavemente, usando Math.sin() impulsado por el reloj.

Errores comunes

01
Olvidar el render() en el bucle Actualizaste la matemática de la posición maravillosamente, pero olvidaste llamar a renderer.render(scene, camera) al final del requestAnimationFrame. Resultado: pantalla congelada.
02
Memoria desbordada (Memory Leaks) En SPAs (como Astro con view transitions o React), si cambias de página y no llamas a cancelAnimationFrame(), el bucle seguirá corriendo invisible en el fondo comiendo recursos de la PC. ¡Cierra tu bucle!

Practica lo aprendido

Domina el movimiento basado en tiempo.

01 Instancia new THREE.Clock() antes de tu función animate().
02 Consigue el elapsedTime dentro del bucle.
03 Asigna mesh.position.x = Math.sin(elapsedTime) y observa cómo el objeto oscila de izquierda a derecha sin fin.