JS Animation
Tags: javascript, promise, async, beginners
Published: 2020-07-25 19:39:48

Hello and good morning! Today I will be showing you how to turn a sprite sheet into an animation sequence using JavaScript. Here is the sprite sheet we will be working with and the final result.

![Dog Sprite Sheet](https://i.ibb.co/FX5nhQ2/dog-moving.png)
![Dog Walking](https://i.ibb.co/gPqHVV0/walking.gif)

### Prerequisites
* An understanding of the HTML canvas and context
* Basic knowledge of JavaScript classes
* A bit of async and await

### Things to note about this sprite sheet
* Has a total of **8 frames**
* Each frame is exactly **113x79 pixels**

## Our First Class
```javascript
class Sprite {
  constructor(src, width, height, frames) {
    this.image = new Image;
    this.image.src = src;
    this.width = width;
    this.height = height;
    this.frames = frames;
  }
}
```
This class is in charge of maintaining the properties of a sprite sheet. The width and height properties belong to the expected width/height for all of the frames in the sheet (113x79).

## The Animation Handler
```javascript
class AnimationHandler {
  constructor(sprites) {
    this.sprites = sprites;
    this.currentSprite = sprites[0];
    this.loop = true;
    this.currentSprite.image.onload = this.render();
  }
...
```
The AnimationHandler class takes in an array of **Sprite Objects** and calls the **render** function in a looping state after the sprite loads.

## The Brains
```javascript
...
async render() {
    for (let i = 0; i < this.currentSprite.frames; i++) {
      context.clearRect(0, 0, canvas.width, canvas.height);
      context.drawImage(this.currentSprite.image, (this.currentSprite.width * i), 0, this.currentSprite.width, 
      this.currentSprite.height, 50, 50, this.currentSprite.width, this.currentSprite.height);

      await sleep(80)
    }
    if (this.loop) {this.render();}
  }
```
We first define an **asynchronous** function named _render_ using the **async** keyword. This allows us to run multiple animations at once without creating blockage.

We then create a basic **for loop** to loop over the number of frames. For every _frame_ we do these things in order.

1. Clear everything on the canvas
2. Draw the sprite image and crop it to show the current frame
3. Pause the loop for 80 milliseconds before continuing
4. Use **recursion** to keep the animation running

Check this link out to learn about how context.drawImage works [MDN](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/drawImage)

You may be asking yourself, "Where the hell is the sleep function defined?!" I decided to keep it out of this tutorial to avoid confusion. The _sleep_ function just returns a **Promise** after setTimeout.

### Putting It All Together
```javascript
const dogIdle = new Sprite(dog_idle, 109, 79, 4);
const dogMoving = new Sprite(dog_moving, 113, 79, 8);
new AnimationHandler([dogMoving, dogIdle])
```
We create two **Sprite** instances and pass in the required parameters. Then we create an **AnimationHandler** instance for the sprites. This should automatically play the first sprite sheet you pass i although the class can be easily modified to jump between animations and even pause them.

Here is the _idle_ we defined above and an attacking animation.

![sheet](https://i.ibb.co/g7tsS7r/dog-idle.png)
![dog idle](https://i.ibb.co/PNM0hw7/idle.gif)
![attacking](https://i.ibb.co/1K8MB1M/attacking.gif)

Thank you for reading ! I left a few things out for the sake of keeping this simple. Like defining the canvas and context and the sleep function.