Let's study an important technique known as "time-based animation", that is used by nearly all "real" video games.
This technique is useful when:
Your application runs on different devices, and where 60 frames/s are definitely not possible. More generally, you want your animated objects to move at the same speed on screen, regardless of the device that runs the game.
For example, imagine a game or an animation running on a smartphone and on a desktop computer with a powerful GPU. On the phone, you might achieve a maximum of 20 fps with no guarantee that this number will be constant; whereas on the desktop, you will reliably achieve 60 fps. If the application is a car racing game, for example, your car will take 30s to make a complete loop on the race track when running on a desktop, whilst on a smartphone it might take 5 minutes.
The way to address this is to run at a lower frame-rate on the phone. This will enable the car to race around the track in the same amount of (real) time as it does on a powerful desktop computer.
Solution: you need to compute the amount of time that has elapsed between the last frame that was drawn and the current one; and depending on this delta of time, adjust the distance the car must move across the screen. We will see several examples of this later.
You want to perform some animations only a few times per second. For example, in sprite-based animation (drawing different images as a character moves, for example), you will not change the images of the animation 60 times/s, but only ten times per second. Mario will walk on the screen in a 60 fps animation, but his posture will not change every 1/60th of second.
You may also want to accurately set the framerate, leaving some CPU time for other tasks. Many games consoles limit the frame-rate to 1/30th of a second, in order to allow time for other sorts of computations (physics engine, artificial intelligence, etc.)
Let's take a simple example with a small rectangle that moves from left to right. At each animation loop, we erase the canvas content, calculate the rectangle's new position, draw the rectangle, and call the animation loop again. So you animate a shape as follows (note: steps 2 and 3 can be swapped):
erase the canvas,
draw the shapes,
move the shapes,
go to step 1.
When we use requestAnimationFrame for implementing such an animation, as we did in the previous lessons, the browser tries to keep the frame-rate at 60 fps, meaning that the ideal time between frames will be 1/60 second = 16.66 ms.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset=utf-8 />
<title>Small animation example</title>
<script>
var canvas, ctx;
var width, height;
var x, y;
var speedX;
// Called after the DOM is ready (page loaded)
function init() {
// init the different variables
canvas = document.querySelector("#mycanvas");
ctx = canvas.getContext('2d');
width = canvas.width;
height = canvas.height;
x=10; y = 10;
// Move 3 pixels left or right at each frame
speedX = 3;
// Start animation
animationLoop();
}
function animationLoop() {
// an animation involves: 1) clear canvas and 2) draw shapes,
// 3) move shapes, 4) recall the loop with requestAnimationFrame
// clear canvas
ctx.clearRect(0, 0, width, height);
ctx.strokeRect(x, y, 10, 10);
// move rectangle
x += speedX;
// check collision on left or right
if(((x+5) > width) || (x <= 0)) {
// cancel move + inverse speed
x -= speedX;
speedX = -speedX;
}
// animate.
requestAnimationFrame(animationLoop);
}
</script>
</head>
<body onload="init();">
<canvas id="mycanvas" width="200" height="50" style="border: 2px solid black">
</canvas>
</body>
</html>
If you try this example on a low-end smartphone (use this URL for the example in stand-alone mode) and if you run it at the same time on a desktop PC, it is obvious that the rectangle moves faster on the desktop computer screen than on your phone.
This is because the frame rate differs between the computer and the smartphone: perhaps 60 fps on the computer and 25 fps on the phone. As we only move the rectangle in the animationLoop, in one second the rectangle will be moved 25 times on the smartphone compared with 60 times on the computer! Since we move the rectangle the same number of pixels each time, the rectangle moves faster on the computer!
Here is the same example to which we have added a loop that wastes time right in the middle of the animation loop. It will artificially extend the time spent inside the animation loop, making the 1/60th of second ideal impossible to reach.
Try it on JsBin and notice that the square moves much slower on the screen. Indeed, its speed is a direct consequence of the extra time spent in the animation loop.
function animationLoop() {
...
for(var i = 0; i < 50000000; i++) {
// slow down artificially the animation
}
...
requestAnimationFrame(animationLoop);
}