Javascript Animation with Canvas
Tags: javascript, html, canvas, tutorial
Published: 2023-06-27 00:33:45

## Bouncing Around the Screen

In this tutorial you'll learn how to create a basic procedural animation with JavaScript and Canvas: the classic bouncing ball.

This tutorial assumes that you understand modern JavaScript, including things like classes and anonymous functions, but does not assume any knowledge of the Canvas API. If you're not up to speed on the latest JS, don't worry, try to follow along and leave a comment if you need help.

## Setting Up

To start create an HTML file and link it to a single CSS and JavaScript file.

**index.html**
```html
<!DOCTYPE html>
<html lang="en">
  <head>
    <title>Bouncing Around</title>
    <link rel="stylesheet" href="styles.css">
  </head>
  <body>
    <script src="main.js"></script>
  </body>
</html>
```

**main.js**
```js
window.onload = () => {
  // Add your code here
  console.log('Hello, Canvas Animation!');
}
 ```

**styles.css**
```css
/* Add your styles here */
```

When you're done you should have a folder structure similar to this.
```sh
/canvas-demo
  index.html
  main.js  
  styles.css  
```

We'll be filling out this template in the rest of the tutorial.

**Note**
The code examples in this tutorial will need to be served via _http_ in order to work properly. Just opening them in a browser won't do it. If you're using VS-Code to follow along you can use the [Live Server](https://marketplace.visualstudio.com/items?itemName=ritwickdey.LiveServer) extension to serve them, or choose another method if this doesn't work for you.

### The Canvas Element

The `canvas` is essentially a blank surface that we can draw on with JavaScript. All of the drawing in this tutorial will be done on one. It has two main attributes, `width` and `height`, that we can use to set its size in pixels.
```html
<canvas width="640" height="480"></canvas>
```
It also accepts inherited attributes like "class" and "id" that we can use for styling.

Place it within the `<body />` of an HTML document to use it. In this example we also give it an `id` of "stage" that we'll use to style it and reference it in JS.
```html
<!DOCTYPE html>
<html lang="en">
  ...
  <body>
    <canvas id="stage" width="640" height="480"></canvas>
  </body>
</html>
```

By default the canvas will be invisible because it has a white background and blends into the HTML document. We can change this by setting its background color. Let's give it a black background.

```css
#stage {
  background-color: black;
}
```

Before moving on to drawing with canvas, here's the complete example up to this point.
{% codepen https://codepen.io/James-Johnson-the-encoder/pen/GRwrOJj %}

## Drawing a Ball

To start drawing with `canvas` we must first get a reference to our `canvas#stage` and then get its 2D rendering context.
```js
const canvas = document.getElementById('stage');
const context = canvas.getContext('2d');
```

The `context` object contains methods for drawing shapes, setting fill and lines styles & much more. We'll be using it to draw our ball.

After getting the context it's usually a good idea to clear the screen before doing any other drawing. We can call the `context.clearRec()` method to accomplish this. It takes the coordinates of a rectangle as arguments. Pass in the `canvas` bounding rectangle to clear it.
```js
context.clearRect(0, 0, 640, 480);
```

With the screen clear, we can use the context's `arc` method to draw a ball.
```js
context.arc(
  x, y        // Position of the center of arc
  radius,     // Distance from the center of the arc to its outer edge
  startAngle, // Starting position of the arc in radians
  endAngle,   // Ending position
);
```

Let's start drawing. First set a `fillStyle` to tell the canvas what color it should be. This can be any valid CSS color for our purposes. Then call `canvas.beginPath()` to start drawing. Finally draw the ball and fill it in.
```js
context.fillStyle = 'green';
context.beginPath();
context.arc(100, 100, 16, 0, 2 * Math.PI);
context.fill();
```

We could just paste this code into the `onload` function in our project and get it to draw. But instead, let's wrap it up into a `class`. This will come in handy when we get to the animation portion of the tutorial.

```js
class Ball {
  constructor(x, y, radius, color) {
    // Give the ball a position, size and color
    this.x = x ?? 320;
    this.y = y ?? 240;
    this.radius = radius ?? 8;
    this.color = color ?? 'white';
  }
  
  draw(context) {
    // Draws the ball, using the member vars we defined 
    // in the constructor
    context.fillStyle = this.color;
    context.beginPath();
    context.arc(this.x, this.y, this.radius, 0, 2 * Math.PI);
    context.fill();
  }
}
```

Put this code above the `window.onload()` function.

We can then create an instance of the ball in the `window.onload()` function and draw it.
```js
window.onload = () => {
  // Get the rendering context
  const canvas = document.getElementById('stage');
  const context = canvas.getContext('2d');

  // Create a green ball at the top left corner of the canvas
  const ball = new Ball(100, 100, 32,  'green');

  // Clear the screen and draw the ball
  context.clearRect(0, 0, 640, 480);
  ball.draw(context);
};
```

Here's the complete example.

{% codepen https://codepen.io/James-Johnson-the-encoder/pen/XWypZyB %}

## Make it Bounce

The formula for motion that we'll be using is:

`new position = current position + direction * speed`

where `direction` is an [Euler angle](https://en.wikipedia.org/wiki/Euler_angles) and `speed` is a scalar. The theory being that if you multiply the direction of an object by its speed then you can calculate the object's position relative to its starting point.

For our purposes this translates into code like this:
```js
const xPosition += xDirection * speed;
const yPosition += yDirection * speed;
```

To use this in our project we'll need to make some changes to the `Ball` class. First, add some member variables for direction and speed. While we're at it, let's also refactor the constructor to take a config object as it's argument. Since we're starting to use a large number of variables.
```js
class Ball {
  constructor({ 
    x, 
    y,
    dx, // Direction
    dy,
    speed, // Speed
    radius, 
    color 
  }) {
    this.x = x ?? 320;
    this.y = y ?? 240;
    this.dx = dx ?? 0;
    this.dy = dy ?? 0;
    this.speed = speed ?? 2;
    this.radius = radius ?? 8;
    this.color = color ?? 'white';
  }
}
```

Next add a `move()` method to the class that implements the motion formula.
```js
class Ball {
  ...
  move() {
    // Apply the motion formula to this Ball
    this.x += this.dx * this.speed;
    this.y += this.dy * this.speed;
  }
  ...
}
```

Now that we've updated the `Ball` class let's look at the `window.onload()` function. Currently it's pretty static. It just draws a single ball and returns. In order to get things moving we'll need to add a timed loop or something to apply the move calculations at a fixed interval. `setInterval` seems like a good choice, but there's something even better.
```js
// Takes only one argument, a callback function that 
// draws one frame
requestAnimationFrame(callback);
```
You use it by calling it once with the callback function and calling it again at the end of that function to create a loop. 
```js
// Set up an animation loop
const frame = () => {
  // Draw stuff for one frame
  requestAnimationFrame(frame);
};

requestAnimationFrame(frame);
```
This will create a 60 FPS frame interval in most browsers. 

Let's update our window function to use it.
```js
window.onload = () => {
  const canvas = document.getElementById('stage');
  const context = canvas.getContext('2d');

  // Create an instance of our new Ball class
  const ball = new Ball({ 
    x: 16,
    y: 16, 
    dx: 1,
    dy: 1,
    radius: 32, 
    color: 'green' 
  });
  
  // Set up the animation
  const frame = () => {
    ball.move();
    
    context.clearRect(0, 0, 640, 480);
    ball.draw(context);
    
    requestAnimationFrame(frame);
  };

  // Start the animation
  requestAnimationFrame(frame);
};
```

### What About the Bounce?

To make our ball bounce on the walls we can update the `Ball.move()` function to check for when the ball moves off the canvas and reverse its direction.
```js
class Ball {
  ...
    move() {
    this.x += this.dx * this.velocity;
    this.y += this.dy * this.velocity;
    
    // Bounce off the walls
    if (this.x < 0 || this.x > 640) {
      this.dx = -this.dx;
    }
    if (this.y < 0 || this.y > 480) {
      this.dy = -this.dy;
    }
  }
...
}
```

You can see the finished project below.

{% codepen https://codepen.io/James-Johnson-the-encoder/pen/zYMNRVM %}

## Bonus

That's it for this tutorial. I hope you enjoyed it and maybe even learned a thing or two. Tell me what you think in the comments.

And as a parting gift, here's a CodePen with a hundred colorful bouncing balls...

{% codepen https://codepen.io/James-Johnson-the-encoder/pen/LYXxdNP %}

### References
- [The canvas element](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/canvas)
- [The canvas JS API](https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API)
- [requestAnimationFrame](https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame)