Gameloop – a maneira correta

Em muitas páginas da web, as pessoas ainda tendem a usar setInterval()como função principal de um gameloop. Esta não é a melhor maneira de fazer isso. Os navegadores modernos são capazes de avisá-lo assim que estiverem prontos para renderizar uma nova saída usando requestAnimationFrame.

Exemplo básico usando setInterval ()

var canvas = document.getElementById('canvas'),
cw
= canvas.width,
ch
= canvas.height,
cx
= null,
fps
= 30,
bX
= 30,
bY
= 30,
mX
= 10,
mY
= 20;

function gameLoop() {
cx
.clearRect(0,0,cw,cw);

cx
.beginPath();
cx
.fillStyle = 'red';
cx
.arc(bX, bY, 20, 0, Math.PI * 360);
cx
.fill();
if(bX >= cw || bX <= 0) { mX*=-1; }
if(bY >= ch || bY <= 0) { mY*=-1; }

bX
+=mX;
bY
+=mY;
}

if (typeof (canvas.getContext) !== undefined) {
cx
= canvas.getContext('2d');

setInterval
(gameLoop, 1000 / fps);
}

jsFiddle Demo

O mesmo usando requestAnimationFrame

var vendors = ['webkit', 'moz'];
for (var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
window
.requestAnimationFrame = window[vendors[x] + 'RequestAnimationFrame'];
window
.cancelAnimationFrame = window[vendors[x] + 'CancelAnimationFrame'] || window[vendors[x] + 'CancelRequestAnimationFrame'];
}

var canvas = document.getElementById('canvas'),
cw
= canvas.width,
ch
= canvas.height,
cx
= null,
fps
= 30,
bX
= 30,
bY
= 30,
mX
= 150,
mY
= 300,
lastTime
= (new Date()).getTime(),
currentTime
= 0,
delta
= 0;

function gameLoop() {
window
.requestAnimationFrame(gameLoop);

currentTime
= (new Date()).getTime();
delta
= (currentTime - lastTime) / 1000;
cx
.clearRect(0, 0, cw, cw);

cx
.beginPath();
cx
.fillStyle = 'red';
cx
.arc(bX, bY, 20, 0, Math.PI * 360);
cx
.fill();
if (bX >= cw || bX <= 0) {
mX
*= -1;
}
if (bY >= ch || bY <= 0) {
mY
*= -1;
}

bX
+= (mX * delta);
bY
+= (mY * delta);

lastTime
= currentTime;
}

if (typeof (canvas.getContext) !== undefined) {
cx
= canvas.getContext('2d');

gameLoop
();
}

jsFiddle Demo

Outro método que limita a renderização a um máximo de fps

var vendors = ['webkit', 'moz'];
for(var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
window
.requestAnimationFrame = window[vendors[x]+'RequestAnimationFrame'];
window
.cancelAnimationFrame =
window
[vendors[x]+'CancelAnimationFrame'] || window[vendors[x]+'CancelRequestAnimationFrame'];
}

var canvas = document.getElementById('canvas'),
cw
= canvas.width,
ch
= canvas.height,
cx
= null,
fps
= 30,
bX
= 30,
bY
= 30,
mX
= 10,
mY
= 20,
interval
= 1000/fps,
lastTime
= (new Date()).getTime(),
currentTime
= 0,
delta
= 0;

function gameLoop() {
window
.requestAnimationFrame(gameLoop);

currentTime
= (new Date()).getTime();
delta
= (currentTime-lastTime);

if(delta > interval) {

cx
.clearRect(0,0,cw,cw);

cx
.beginPath();
cx
.fillStyle = 'red';
cx
.arc(bX, bY, 20, 0, Math.PI * 360);
cx
.fill();
if(bX >= cw || bX <= 0) { mX*=-1; }
if(bY >= ch || bY <= 0) { mY*=-1; }

bX
+=mX;
bY
+=mY;

lastTime
= currentTime - (delta % interval);
}
}

if (typeof (canvas.getContext) !== undefined) {
cx
= canvas.getContext('2d');

gameLoop
();
}

jsFiddle Demo