Lesson 15 - Timers and animations in JavaScript
In the previous exercise, Solved tasks for JavaScript lessons 13-14, we've practiced our knowledge from previous lessons.
Welcome to the next lesson of our JavaScript online course. The previous lesson, Solved tasks for JavaScript lessons 13-14, was about the 2D context of a canvas. In today's JavaScript tutorial, we're going to look at timing and then continue with animations.
Timers
We can handle timers in JavaScript using two functions.
setInterval()
and
setTimeout()
The setInterval()
and setTimeout()
functions accept
two parameters. The function that will be called and the time interval (how
often or after what time it'll be called). We specify the intervals in
milliseconds (1 second = 1000 milliseconds). The difference between these two
methods is quite fundamental. While setInterval()
will call the
method every X milliseconds, setTimeout()
will call it only once
when X milliseconds elapses.
Example
The goal will be to create a typing text. At first, the letter
"H"
will show. One second later, "e"
will show up.
This will continue with letters "l"
, "l"
,
"o"
, "w"
, "o"
, "r"
,
"l"
, "d"
. Once all the letters are typed, the text is
cleared and it all starts again.
Now we won't have any element in the HTML code anymore, we'll create it via
JavaScript (the implementation is only up to us, but we'll leave the
<body>
empty to practice the DOM manipulation ).
We'll store the text to be typed into a text
variable and create
a <p>
element. Since it can happen that even the
<body>
wouldn't be loaded yet (if we imported the script in
the <head>
), we'll insert the <p>
element
into the <body>
element not before the page is loaded.
let text = "Hello world"; let element = document.createElement("p"); window.onload = function () { document.body.appendChild(element); }
Now we'll proceed to the function which will be changing the text in our element:
function changeText() { }
At first in this function, we'll verify that we haven't already written all
the letters. If we asked for the next letter when we're at the end and there're
no more letters left, we'd cause an error. If the text
variable
already equals to the element's content, we'll change the element's value to an
empty string so that the text can be typed again. We'll also add the else branch
where we'll insert further code.
if (element.textContent == text) { element.textContent = ""; } else { }
Next, we'll retrieve the letter (character) which we'll add to the element's
text. We already know that length
contains a string's length. So
we'll get the next character to be typed by measuring the length of the string
that is already typed on the screen and adding 1 to it. There's a catch. While
the string length is counted from 1 (1 character = length 1
), the
character at a certain position (index) is counted from zero (1st character =
index 0
), so we have to subtract 1. Finally +1 (for the next
character) and -1 (because 0 is the first character) eliminates, so we take the
character at the index of the length of the text already typed on the
screen:
let letterToType = text[element.textContent.length];
And we add the letter to the text of the element.
element.textContent += letterToType;
All we have to do is to set the interval now. Let's call the
setInterval()
method in the onload
event handler.
We'll pass changeText
as the name of the function (without
parentheses) as the first parameter, and the number 1000
to make
the text change every 1 second as the second parameter.
setInterval(changeText, 1000);
The interval will start one second later. In the meantime, the application
will look like it doesn't respond for a second. That's why we'll call the
changeText()
method manually above it:
changeText(); setInterval(changeText, 1000);
You can still improve the application since it'll appear stuck when typing whitespaces.
Advanced animations
It'd be nice to have more complex animations on our website. The simple ones can be done in CSS, but the more complicated ones have to be done in JavaScript. The core principle of animations is that we affect properties of the animated object in some interval.
Let's take autumn web decorations as an example. We'll program a script that
animates falling leaves, they'll fall from top to bottom. We'll place the images
of the leaves in the root <body>
element and each image will
have a data-autumn
data-attribute. We'll select only these images,
because there might be (and will be) other images on the website and we won't
want to animate those.
Download the leaf image below and put it into a new project with the following HTML contents:

<img src="leaf.jpg" data-autumn alt="leaf" /> <img src="leaf.jpg" data-autumn alt="leaf" /> <img src="leaf.jpg" data-autumn alt="leaf" /> <img src="leaf.jpg" data-autumn alt="leaf" /> <img src="leaf.jpg" data-autumn alt="leaf" />
Let's attach a CSS style to the project in which we'll set the absolute
position to the images (again, we'll affect only the images with the
data-autumn
attribute). We'll also style the
<body>
so it won't display scrollbars.
body > img[data-autumn] { position:absolute; } body { overflow:hidden; }
Let's define a leaves
variable to which we'll assign the leaves
images with the data-autumn
attribute once the page is loaded.
let leaves; window.onload = function () { leaves = document.querySelectorAll("body > img[data-autumn]"); }
We'll iterate through the leaves using a loop:
for (let i = 0; i < leaves.length; i++)
We want to set the default position to the leaves. This will be one-fifth of the window width for each leaf from the left (to justify them, as can be seen further). And minus their height from the top (so they'd fall from behind the top window edge).
Window size
Sometimes (like now) we need to work with window size. There're several ways and properties out there that are related to this size.
The actual screen size
We can retrieve the real screen size using the width
and
height
properties on the screen
object.
Available size
This is the size that applications can use. In other words, essentially the
screen size above minus the size of the system panels (such as the taskbar). We
can find the properties on the screen
object again, they're
availWidth
and availHeight
.
The website window size
Finally, we're getting to the most important size for us. It's the size of
the area our applications can use. We can find the properties on the
window
object as innerWidth
and
innerHeight
.
Setting CSS properties
For the time being, we still lack one essential piece of information which is how to set CSS properties of DOM elements.
All DOM elements have the style
property which contains
properties named as CSS properties. They're not written using the dash notation,
but camelCase (the first word is all in lowercase letters, each new word has the
first letter upper-cased, there're no spaces). So for example:
document.body.style.backgroundColor = "red";
sets the color of the <body>
element to red.
Let's go back to our falling leaves. We'll set the positions. Remember to not forget to specify the units when setting CSS values:
leaves[i].style.left = i * window.innerWidth / leaves.length + "px"; leaves[i].style.top = -leaves[i].height + "px";
Moving the leaves
Now let's implement a move()
function which will move the leaves
down. The function will iterate through all the leaves using a loop and set the
new positions to them. The new position will be based on the actual one (which
we must parse to remove the CSS unit from the value), and then we'll add some
reasonable value to it so the animation will be neither too fast nor too slow.
You can try to edit the value (the value 2
in the code).
function move() { for (let i = 0; i < leaves.length; i++) { let newPosition = parseInt(leaves[i].style.top) + 2; leaves[i].style.top = newPosition + "px"; } }
We still have to deal with the case when a leaf leaves the window. In this case, we need to reset its position to minus the height of the image. Let's place a condition between the previous two lines so we don't have to set the CSS value twice.
if (newPosition > window.innerHeight) { newPosition = -leaves[i].height; }
Finally, in the window's load
event handler, we'll set the
interval to a reasonable value (you can try to adjust it yourself again).
setInterval(move, 20);
Because the process was more complex, let's show the complete script for review:
let leaves; function move() { for (let i = 0; i < leaves.length; i++) { leaves[i].style.left = i * window.innerWidth / leaves.length + "px"; let newPosition = parseInt(leaves[i].style.top) + 2; if (newPosition > window.innerHeight) { newPosition = -leaves[i].height; } leaves[i].style.top = newPosition + "px"; } } window.onload = function () { leaves = document.querySelectorAll("body > img[data-autumn]"); setInterval(move, 20); }
Run the application. You'll see the leaves falling from top to bottom, and they'll reset once they leave the screen.
Congratulations, you've finished your first JavaScript animation. You can improve it, of course, to make it even more interesting.
Canvas animations
We can create animations using <canvas>
in a similar way.
In some interval, we'll clear the entire canvas, draw everything and so over and
over again. We can program a wheel of fortune as an example. We'll load it from
a static image to keep things simple. So, let's create a page with a
<canvas>
in it and select it in JavaScript. We'll add an
image with id="wheel"
to the page.
Download the image below and insert it along with the following HTML code to a new project:

<img src="wheel.png" id="wheel" /> <canvas id="canvas" width="500" height="500"></canvas>
In the script, we'll select these objects and also add an angle
variable where the current angle of the rotation will be stored. We won't forget
to hide the image from the page.
let canvas; let context; let angle = 0; let image; window.onload = function () { canvas = document.getElementById("canvas"); context = canvas.getContext("2d"); image = document.getElementById("wheel"); image.style.display = "none"; }
In the redraw()
method, we simply clear the canvas and draw the
wheel again. First, we'll move and rotate the context appropriately and then
we'll draw the image. Finally, we'll add one degree to the angle which
corresponds with Math.PI / 180
radians since Math.PI
is one half of the circle and one half of the circle has 180 degrees.
function redraw() { context.clearRect(0, 0, 500, 500); context.save(); context.translate(250, 250); context.rotate(angle); context.drawImage(image, -225, -225); context.restore(); angle += Math.PI / 180; // Rotates by 1 degree }
In the page's load
event, we'll set the interval as usual. We'll
also draw the wheel right at the start so we won't have to wait for the first
interval to come.
setInterval(redraw, 20); redraw();
The result:
Congratulations, you should be now able to handle animations. You can challenge your skills using our exercises.
Doing animations right
All of our animation solutions work, but let's look at them in terms of performance. When the user leaves our browser tab, the animation will keep running and drain the CPU. It's even worse on mobile devices, where we can notice a decrease in performance. We'll learn how to optimize our animations for the web browser in the next lesson, Solved tasks for JavaScript lesson 15.
In the following exercise, Solved tasks for JavaScript lesson 15, we're gonna practice our knowledge from previous lessons.
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 7x (218.61 kB)
Application includes source codes in language JavaScript