The setInterval(...) function is still popular on the Web, and even though this is not the recommended way to do 60 frames/second canvas animation, it is worth understanding how it works.
Syntax: setInterval(function, ms);
The setInterval(...) function calls another function or evaluates an expression at specified intervals of time (in milliseconds), and returns the unique id of the action. You can always stop it by calling the clearInterval(id) function with the interval identifier as an argument.
This is how pre-HTML5 games were written. Before the introduction of the canvas element, developers made games using div elements. By changing their background color, top and left CSS positions, it was possible to animate characters in games. The animation was created by calling repeatedly a function that did the drawing, using the JavaScript setInterval or setTimeout functions.
Please try this example that moves/animates a div using setInterval:
Extract from the source code:
<body>
<div id="animatedDIV">Animated DIV :-)</div>
<button onclick="start()">Start animation</button>
<button onclick="stop()">Stop animation</button>
<script>
var elm = document.getElementById("animatedDIV");
var requestId;
var x = 0;
function render(time) {
elm.style.left = x++ + "px";
}
function start() {
requestId = setInterval(render, 10);
}
function stop() {
if (requestId) {
clearInterval(requestId);
}
}
</script>
</body>
Here, we define a <div> element, (see the online source code for the CSS properties involved), and we use the setInterval method (line 17) to call every 10ms the render() method that will just increment the position of this element. Notice that since we're using the DOM, the horizontal position of the div is modified by changing its left CSS property.
The call to setInterval returns an id we can use to stop the animation, by calling clearInterval (line 22).
We use the drawMonster() function:
Source code:
<body onload="init();">
<canvas id="myCanvas" width="400" height="400">
Your browser does not support the canvas element.
</canvas>
<p>
<button onclick="start()">Start animation</button>
<button onclick="stop()">Stop animation</button>
<script>
var canvas, ctx;
var monsterX=100, monsterY=100, monsterAngle=0;
function init() {
// This function is called after the page is loaded
// 1 - Get the canvas
canvas = document.getElementById('myCanvas');
// 2 - Get the context
ctx=canvas.getContext('2d');
}
function animationLoop() {
// 1 - Clear the canvas
ctx.clearRect(0, 0, canvas.width, canvas.height);
// 2 Draw the monster using variables for pos, angle, etc.
drawMonster(monsterX, monsterY, monsterAngle, 'green', 'yellow');
// 3 Move the monster (change pos, angle, size, etc.)
monsterX += 10;
monsterX %= canvas.width
monsterAngle+= 0.01;
}
function drawMonster(x, y, angle, headColor, eyeColor) {
// BEST PRACTICE: SAVE CONTEXT AND RESTORE IT AT THE END
ctx.save();
// Moves the coordinate system so that the monster is drawn
// at position (x, y)
ctx.translate(x, y);
ctx.rotate(angle)
// head
ctx.fillStyle=headColor;
ctx.fillRect(0,0,200,200);
...
// BEST PRACTICE!
ctx.restore();
}
function start() {
// Start the animation loop, change 20 for bigger values
requestId = setInterval(animationLoop, 20);
}
function stop() {
if (requestId) {
clearInterval(requestId);
}
}
</script>
</body>
Lines 52-61: The code for launching and stopping the animation is similar to that from the previous example.
Lines 34-50: The code that draws the monster is that which we saw earlier when we presented the 2D transformations. Best practice is to save and restore the context at the beginning and end of each function that changes the context.
Lines 21-32: The most interesting part is the animation loop that implements the basic animation steps: clear-draw-move. In order to make a shape "movable", we use some "state variables" for its position and angle, and we modify them at each iteration (lines 29-32). We will see later on how to modify the value of these variables on user interactions (keyboard, mouse, etc.).
While the above example works, there are several reasons not to use setInterval for doing smooth animations.
The setInterval function may become hard to debug, particularly if you run several animations simultaneously. For example, if you have two intervals, one running every 100 milliseconds, the other every second, and if you want to debug the second one, the first one will constantly be run at regular intervals, making step by step debugging really difficult.
setInterval will execute the function passed as first parameter every n milliseconds regardless of when the function was last called or how long the function takes to execute. If the function takes longer than the interval, then setInterval might queue too many function executions back to back when the interval is too short, leading to unpredictable results.