In this series, we’ll be exploring by example some of the common methods and patterns for creating stunning and interactive JavaScript animations with the HTML canvas element.

Prerequisites

Understanding how to render basic shapes is necessary, which I covered here.

Installation

To help automate our inputs, we’ll be using dat.gui to give us real-time control over our variables. Since we’re using it on our client side, you can just use the CDN.

<script src= "https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.7.6/dat.gui.min.js" ></script>

Basic Setup

As our boilerplate, we just need an HTML page with a canvas element and a JavaScript file setting our canvas to full screen. We need to set the dimensions in our JavaScript file to avoid a blurring effect that CSS causes.

index.html

<!DOCTYPE html> <html lang= "en" > <head> <meta charset= "UTF-8" /> <meta name= "viewport" content= "width=device-width, initial-scale=1.0" /> <meta http-equiv= "X-UA-Compatible" content= "ie=edge" /> <title> HTML Canvas </title> </head> <body> <canvas></canvas> </body> <script src= "https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.7.6/dat.gui.min.js" ></script> <script src= "./canvas.js" ></script> </html>

canvas.js

const canvas = document.querySelector('canvas'); const c = canvas.getContext('2d'); canvas.width = innerWidth; canvas.height = innerHeight; addEventListener('resize', () => { canvas.width = innerWidth; canvas.height = innerHeight; });

GUI Basics

With our new GUI instance in place, you should already be able to see a Close Controls in the upper right-hand corner of your page. All we need is an object to hold all of the our variables and gui.add() to add them to our panel. add just takes our object, the variable we want to add, and the minimum and maximum values.

We’ll worry about the actual values later, let’s just add them to our controls for when we’re ready for them.

canvas.js

const gui = new dat.GUI(); const controls = { dx: 0, // Speed on the x-axis dy: 0, // Speed on the y-axis }; gui.add(controls, 'dx', 0, 10); gui.add(controls, 'dy', 0, 10);

Animation Setup

The structure for most of your animations is going to be pretty similar. We have a class that we want to create, like a circle, with a draw method to render how it should look like and an update function to change its traits like color, position, or size.

Using the native requestAnimationFrame method we can make our browser repeat something very quickly forever, like run our update method. So if we were to have our x position set on our class and added 1 to it every time update was called, then we could use requestAnimationFrame to constantly repeat update and create the illusion of our object moving to the right. We can then set our x position to change by our dx variable in our GUI (d just means change in physics), so we can change the speed without even touching the code or reloading our screen.

For all of our methods we’re going to use prototype instead of adding them onto the class itself. That would add the function onto every object while prototype makes all the objects reference the one function, thus better for performance when we are working with hundreds of objects.

Let’s start with rendering something to the screen.

canvas.js

class Ball { constructor(x, y, radius, color) { this.x = x; this.y = y; this.radius = radius; this.color = color; } } Ball.prototype.draw = function () { c.beginPath(); c.fillStyle = this.color; c.arc(this.x, this.y, this.radius, 0, Math.PI * 2, false); c.fill(); c.closePath(); }; const redBall = new Ball(innerWidth / 2, innerHeight / 2, 50, 'red'); const init = () => redBall.draw(); init();

Now to make an animate function, we’ll use requestAnimationFrame and pass in its own animate function as an argument, which will infinitely loop over our update method, adding the dx and dy variables from our gui every frame. Try console.log after to see how quickly the window updates.

Ball.prototype.update = function () { this.x += controls.dx; // We can reference our ball object for gui controls this.y += -controls.dy; // Up is generally more intuitive as a positive value, so let's flip it to be a bit easier to handle. this.draw(); }; const animate = () => { requestAnimationFrame(animate); // Since we are creating a new ball every frame we first want to clear the old one. c.clearRect(0, 0, canvas.width, canvas.height); redBall.update(); }; animate();

With our controls working, we can start tinkering with our min/max values for more control.

gui.add(controls, 'dx', -20, 20); gui.add(controls, 'dy', -20, 20);

Now you can start playing with your new animation, adding more objects, and drawing more interesting shapes.

Conclusion

While very basic, this pattern of creating objects, drawing them to the screen, and changing some variables every frame is the foundation of most animations you’ll ever see or make. If you wish to explore some more examples, in part 2, we start working with collisions.

If you had a trouble getting this to work, I made the Codepen available here.