Maquetación Web Dinámica / Canvas y videojuegos 2D
El game loop: requestAnimationFrame
Un canvas dibuja cosas quietas. Un JUEGO dibuja cosas quietas... 60 veces por segundo, cada vez un poquito distintas. Ese motor que borra y repinta sin parar se llama game loop, y es el corazón de todo lo que se mueve en pantalla — desde una bola que rebota hasta un plataformero completo. Hoy lo construyes y entiendes por qué el cine llevaba razón todo este tiempo.
El cine que engaña a tu ojo
La estructura de todo game loop
Tres pasos, en este orden, para siempre:
function loop() {
// 1. LIMPIAR: borrar el fotograma anterior (sin esto, quedan rastros)
ctx.clearRect(0, 0, canvas.width, canvas.height);
// 2. ACTUALIZAR: cambiar el estado (posiciones, puntajes...)
x = x + velocidad;
// 3. DIBUJAR: pintar el estado nuevo
ctx.fillStyle = "#6d28d9";
ctx.fillRect(x, 60, 40, 40);
// 4. pedir el SIGUIENTE fotograma → se repite ~60 veces por segundo
requestAnimationFrame(loop);
}
requestAnimationFrame(loop); // arranca el motorrequestAnimationFrame(loop) le dice al navegador "cuando estés listo para
pintar el próximo fotograma, llama a loop". Como loop se vuelve a
pedir a sí mismo al final, el ciclo se perpetúa. El orden importa: si
dibujas ANTES de limpiar, o no limpias, cada fotograma se acumula sobre el
anterior y tu bola deja un reguero (a veces ES el efecto que quieres —
pero a propósito).
requestAnimationFrame contra setInterval
Podrías pensar en setInterval(loop, 16) para "60 veces por segundo". No
lo hagas — requestAnimationFrame es superior por tres razones:
- Se sincroniza con la pantalla: pinta justo cuando el monitor va a refrescar, sin fotogramas a destiempo que causan tirones. Y se adapta solo a pantallas de 60, 120 o 144 Hz.
- Se pausa en pestañas ocultas: si el usuario cambia de pestaña, el
loop se detiene y ahorra batería.
setIntervalseguiría corriendo a ciegas, calentando el equipo para nada. - Nunca se atropella: espera a terminar un fotograma antes de pedir el siguiente.
Es, además, el mismo motor sobre el que corren las animaciones CSS que optimizaste en el módulo 11 — ahora lo manejas a mano.
Tu primera animación de canvas
<canvas id="loop" width="340" height="140"></canvas>body { font-family: system-ui; padding: 16px; background: #f8fafc; }
canvas { background: white; border: 1px solid #e2e8f0; border-radius: 12px; }const canvas = document.getElementById("loop");
const ctx = canvas.getContext("2d");
const quieto = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
let x = 20;
const velocidad = 2.5;
function dibujar() {
ctx.fillStyle = "#6d28d9";
ctx.beginPath();
ctx.arc(x, 70, 22, 0, Math.PI * 2);
ctx.fill();
}
function loop() {
ctx.clearRect(0, 0, canvas.width, canvas.height); // limpiar
x += velocidad; // actualizar
if (x - 22 > canvas.width) x = -22; // reaparece por la izquierda
dibujar(); // dibujar
requestAnimationFrame(loop);
}
// respeta la preferencia de movimiento reducido: un solo fotograma quieto
if (quieto) { x = 170; dibujar(); }
else requestAnimationFrame(loop);Fíjate en el detalle de accesibilidad, que en canvas es responsabilidad
tuya (no hay un @media que lo haga solo): al inicio consultamos
matchMedia("(prefers-reduced-motion: reduce)") y, si el usuario pidió
menos movimiento, pintamos UN fotograma quieto en vez de arrancar el loop.
Desde aquí, TODOS tus loops llevan esta cortesía. El movimiento perpetuo
puede marear tanto como una animación CSS — la empatía del módulo 11 no
era solo para CSS.
Tu bola se mueve, pero en vez de una bola limpia deja un tubo sólido de color, como si pintara sin levantar el pincel. ¿Qué falta en el loop?
Mini-reto
Toma el loop del ejemplo y haz que la bola cambie de COLOR según su
posición: cuando x esté en la mitad izquierda, violeta; en la derecha,
verde (un if en la fase de dibujo). Bonus: haz que crezca y encoja
suavemente usando Math.sin sobre un contador de fotogramas para el radio
— tu primer coqueteo con el movimiento matemático que domina la próxima
lección.
Qué sigue
Tu bola se mueve a velocidad constante y reaparece por el otro lado — funciona, pero no se SIENTE viva. La próxima lección le da física: velocidad en dos ejes, gravedad que la hace caer y rebotes contra las paredes. El momento en que tus dibujos empiezan a comportarse como cosas de verdad.