A reusable setup for Javascript animations
Javascript animations have many uses. They play a role in page effects, game development, user interface design and much more. I'd like to share a reusable setup for Javascript animations that you can use as the starting point for your projects.
Javascript animations - an overview
What's included in this post:
- a calculation loop that calculates new values for your animated properties
- an animation loop that redraws your graphical elements
- a handy way to set the frame rate (FPS)
What you will use:
- Javascript's
setInterval
method - Javascript's
requestAnimationFrame
method
The code in this post can be used to create Canvas API animations. It can also be used to animate HTML elements on your page, CSS settings, and anything in between. The sky is the limit - if you can store it as a number, you can animate it.
Basic animation setup
Animations consist of 2 simple ingredients:
- Draw your graphic on screen
- Change a property of your graphic so that it will look different next time (for example move it to a new position, change it's size or colour, etc)
- Draw your graphic on screen
- Repeat from Step 2.
Since it can be performance intensive to redraw your graphics you want to limit the amount of times something is drawn to screen.
The faster you repeat these steps the smoother your animation becomes.
A quick word about FPS
The frequency at which your graphics are redrawn determines how smooth the animation looks, and how quickly it plays. This frequency is measured in frames per second
or FPS for short. It's probably a term you're already familiar with.
24fps for example is the standard frame rate for movies.
Gamers on the other hand generally look for at least 60fps for smooth game play, with anything less than 30fps seen as pretty much unplayable.
So which frame rate should you use for your animations?
I'd suggest starting with a frame rate of 60fps, and then adjusting downwards if you find that the calculations are too complex for the computer to keep up.
Your first Javascript animation loop
Use Javascript's setInterval
to create your animation loop. It allows you to run a function continuously at a set time interval.
Since we're aiming for 60fps, and setInterval
counts time in milliseconds, we need to run our function every 1000/60
milliseconds:
const interval = 1000/60;
setInterval(() => {
console.log("animate");
}, interval);
Now that we have our function running regularly, let's try a simple animation:
<p id="output"></p>
<script>
const interval = 1000 / 60;
const el = document.getElementById("output");
let count = 0;
setInterval(() => {
count++;
if (count > 100) {
count = 0;
}
el.innerText = count;
}, interval);
</script>
This code will rapidly count up from 0 and display the value on the page.
Perhaps it's a little quick - why don't you try adjusting the frame rate a little to get a slower count?
const interval = 1000 / 5;
This change reduces the frame rate to 1000/5
milliseconds, which equates to 5 FPS.
Optimising the drawing cycle
While setInterval
allows us to trigger the animation function at the correct FPS, we can do better in terms of optimising when the browser redraws graphics on the screen.
The browser provides a function called requestAnimationFrame
that will run the next time the screen is redrawn. We can use requestAnimationFrame
to trigger our drawing code. In this way we can be sure it will be called at the most efficient moment.
Let's split our code into two distinct pieces to benefit from setInterval
and requestionAnimationFrame
.
<p id="output"></p>
<script>
const interval = 1000 / 5;
const el = document.getElementById("output");
let count = 0;
function draw() {
el.innerText = count;
}
function animate() {
count++;
if (count > 100) {
count = 0;
}
requestAnimationFrame(draw);
}
setInterval(animate, interval);
</script>
Key points to note about this code:
- The animation code is now split into two portions; the calculation step updates the animatable properties; the redraw step displays the result on the screen.
- The calculation step is triggered by the
setInterval
method to maintain the frame rate. - The redraw step is triggered using
requestAnimationFrame
for optimised redraw performance.
Here is a working example:
Stopping the animation
Some animations have a limited lifetime and need to be stopped. You can use clearInterval
to interrupt the looping behaviour.
Here is an altered script that stops the animation once the counter has reached 10.
<p id="output"></p>
<script>
const interval = 1000 / 5;
const el = document.getElementById("output");
let count = 0;
let intervalLoop;
function draw() {
el.innerText = count;
}
function animate() {
count++;
if (count === 10) {
clearInterval(intervalLoop);
}
requestAnimationFrame(draw);
}
intervalLoop = setInterval(animate, interval);
</script>
By storing the value returned from setInterval
we can call clearInterval
later on to interrupt the animation loop.
Animating Canvas API Drawings
Let's expand the above example and animate a drawing made with the Canvas API.
You can use a <canvas>
tag as the drawing surface.
You can then animate properties of the drawing and redraw the canvas to create an animation.
Here is the code:
<canvas id="drawing"></canvas>
<script>
const interval = 1000 / 60;
const canvas = document.getElementById("drawing");
const w = 400;
const h = 200;
canvas.width = w;
canvas.height = h;
const ctx = canvas.getContext("2d");
const boxSize = 20;
const boxY = h / 2;
let boxX = 0;
let dirX = 1;
function draw() {
ctx.clearRect(0, 0, w, h); // clear the canvas
ctx.fillRect(boxX, boxY, boxSize, boxSize);
}
function animate() {
boxX += dirX * 5; // change 5 to alter the speed
// bounce
if (boxX > w - boxSize) {
boxX = w - boxSize;
dirX = -1;
} else if (boxX < boxSize) {
boxX = boxSize;
dirX = 1;
}
requestAnimationFrame(draw);
}
setInterval(animate, interval);
</script>
Key points to note about this code:
- I've upped the frame rate back to 60 fps.
- Each time
draw()
runs it first clears the canvas and then draws a new rectangle shape. - The
draw()
function contains only drawing code, to keep the redraw logic as simple and performant as possible. - The
animate()
function only recalculates the coordinates of the box, it doesn't handle any drawing logic.
Here is a CodePen for you to experiment with:
Conclusion
In this post I shared with you a simple setup to create Javascript animations using setInterval
and requestAnimationFrame
. We animated a counter value and a Canvas API drawing.
I hope this has been helpful and look forward to seeing your examples of animations! Share them in the comments below.