Lesson 16 - JS requestAnimationFrame - For better drawing
In the previous exercise, Solved tasks for JavaScript lesson 15, we've practiced our knowledge from previous lessons.
The previous lesson, Solved tasks for JavaScript lesson 15, was about animations. Together with HTML5, a new JavaScript API - requestAnimationFrame has been added. It's a technology that allows us to render animations in the browser more fluently and with higher performance. We'll introduce the technology in today's JavaScript tutorial.
If you ever tried to program a simple browser game, your main loop might looked like this.
setInterval(function() { update(); render(); }, 1000 / FPS);
Our previous animations looked very similar, we only combined updating and rendering into a single function. An alternative rendering loop may also look like this:
function loop() { setTimeout(loop, 1000 / FPS); update(); render(); } loop();
The code works, we can even limit FPS. So what's wrong with it?
Wasting computing power
The problem with the code above is that the browser invokes it even if the user is not looking at the page (they switched to another tab or minimized the browser). The Google Chrome browser handles these situations by restricting such loops to only 1 FPS. But this is only its voluntary behavior, and other browsers might not do it. We may experience unnecessary performance drops and discharges on mobile devices.
requestAnimationFrame
solves it!
Using the requestAnimationFrame()
function instead of
setTimeout()
, our code will look very similarly.
function loop() { requestAnimationFrame(loop); update(); render(); } loop();
Mozilla invented this feature and the WebKit team later took over and improved it. We can use it to draw CSS, DOM elements, WebGL, or on canvas.
Advantages
requestAnimationFrame()
will ensure that all of our animations
are rendered at once, with higher performance and lower battery consumption.
If you switch to another tab in your browser or you minimize the browser, the rendering will stop to save power and will continue as soon as the page is visible again.
You may have also noticed that using requestAnimationFrame()
we
have never set the number of FPS. By default, the function uses 60 FPS, but it
depends on the browser implementation. However, it should not be too different
and it depends on the PC's performance rather than on the browser creator's
decision. Also, the support across browsers is quite good.
Demo
At the end, we'll show a simple application in which a square moves on a
canvas. We'll implement it using both setInterval()
and
requestAnimationFrame()
. We won't clear the canvas this time on
purpose, so we'll see the square's trace.
The solution using
setInterval()
window.onload = function() { let canvas = document.querySelector("#canvas"); let context = canvas.getContext("2d"); let square = { "x": 25, "y": 25, "dirX": -1, "dirY": 1, "speed": 2, "sizeX": 50, "sizeY": 50, "color": "red" }; function loop() { update(); render(); } setInterval(function() { update(); render(); }, 1000 / 60); loop(); function update() { if (square.x + square.sizeX + square.speed * square.dirX > canvas.width) square.dirX = -1; if (square.x + square.speed * square.dirX < 0) square.dirX = 1; if (square.y + square.sizeY + square.speed * square.dirY > canvas.height) square.dirY = -1; if (square.y + square.speed * square.dirY < 0) square.dirY = 1; changeColor(); square.x += square.speed * square.dirX; square.y += square.speed * square.dirY; } function render() { context.fillStyle = square.color; context.fillRect(square.x, square.y, square.sizeX, square.sizeY); } function changeColor() { colors = ['green', 'blue', 'red', 'yellow']; square.color = colors[Math.floor(Math.random() * colors.length)]; } }
The result:
The solution using
requestAnimationFrame()
window.onload = function() { let canvas = document.querySelector("#canvas"); let context = canvas.getContext("2d"); let square = { "x": 25, "y": 25, "dirX": -1, "dirY": 1, "speed": 2, "sizeX": 50, "sizeY": 50, "color": "red" }; function loop() { update(); render(); requestAnimationFrame(loop); } loop(); function update() { if (square.x + square.sizeX + square.speed * square.dirX > canvas.width) square.dirX = -1; if (square.x + square.speed * square.dirX < 0) square.dirX = 1; if (square.y + square.sizeY + square.speed * square.dirY > canvas.height) square.dirY = -1; if (square.y + square.speed * square.dirY < 0) square.dirY = 1; changeColor(); square.x += square.speed * square.dirX; square.y += square.speed * square.dirY; } function render() { context.fillStyle = square.color; context.fillRect(square.x, square.y, square.sizeX, square.sizeY); } function changeColor() { colors = ['green', 'blue', 'red', 'yellow']; square.color = colors[Math.floor(Math.random() * colors.length)]; } }
The result:
I hope the lesson has been useful for you and you will be using
requestAnimationFrame()
in your animations
In the next lesson, Strict operators and casting in JavaScript, we'll talk about conditions before embarking on another bigger topic - working with graphics.
Did you have a problem with anything? Download the sample application below and compare it with your project, you will find the error easily.
Download
By downloading the following file, you agree to the license terms
Downloaded 5x (3.33 kB)
Application includes source codes in language JavaScript