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:

  1. Draw your graphic on screen
  2. 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)
  3. Draw your graphic on screen
  4. 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:

  1. 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.
  2. The calculation step is triggered by the setInterval method to maintain the frame rate.
  3. 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:

  1. I've upped the frame rate back to 60 fps.
  2. Each time draw() runs it first clears the canvas and then draws a new rectangle shape.
  3. The draw() function contains only drawing code, to keep the redraw logic as simple and performant as possible.
  4. 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.