Canvas 101: Rotating Shape
Tags: javascript, codepen, tutorial, creativecoding
Published: 2019-07-24 18:55:54

Never built anything in canvas before?! This is perfect for ya!

Let's start with a few concepts of setting up and using the Canvas API. We can think of Canvas as a programmable etch a sketch that you erase and draw every time at a fraction of a second.

We can setup the first few lines of code in HTML, CSS, and JavaScript. We'll use CodePen because it's an easy way to jump in and start building with Canvas. You'll create a new pen, set CSS to normalize, add `<canvas id="canvas"></canvas>` to the HTML, and add `html { overflow: hidden; }` to CSS.

Now we can get into building with Canvas, breaking down each step.

```
const canvas = document.querySelector("#canvas");
const context = canvas.getContext("2d");
let canvasWidth = canvas.width = window.innerWidth;
let canvasHeight = canvas.height = window.innerHeight;
```

We'll store a reference to the HTMLCanvasElement with `const canvas = document.querySelector("#canvas");` this will let us access properties and methods to start drawing. `const context = canvas.getContext("2d");` reaches into the canvas HTMLCanvasElement to return the context of the canvas to draw. `canvasWidth` and `canvasHeight` are using the properties of the window to apply the width and height to the canvas.

First let's build the `render` function to set the color for the `context` then draw a shape `fillRect()` as the background.

```
function render(){
  context.fillStyle = 'lightsalmon';
  context.fillRect(0, 0, canvasWidth, canvasHeight);
}
```

Next we can construct a class for a shape, let's make it a Square ◻️ and give it these properties in the constructor `{ width, height, rotate, xPosition, yPosition }`. We'll deconstruct the incoming object in the constructor and set default values. After making the Square class let's make an instance of Square and set some properties.

```
class Square {
  constructor({
    width,
    height,
    rotate = 0,
    xPosition = canvasWidth / 2,
    yPosition = canvasHeight / 2
  }) {
    this.width = width;
    this.height = height;
    this.rotate = rotate;
    this.xPosition = xPosition;
    this.yPosition = yPosition;
  }
}

const square = new Square({width: 50, height: 50});
```

Now that we have the Square class and an instance of it created. We can start to add it to the render method. So let's step back into it and do a few important steps. The `context.save()` method will allow us to save the transformation, specific attributes, clipping, etc. Allowing you to place multiple shapes in the drawing context of canvas and `context.restore()` will reinstate the state from `context.save()`.

```
function render() {
  context.fillStyle = "lightsalmon";
  context.fillRect(0, 0, canvasWidth, canvasHeight);
  context.save();
  // Add styling for square in the context here!
  context.restore();
}
```

Nothing is going to change when setting this up but it will allow us to start building in the shapes and styles in the commented section! So let's add a darker color to next draw context `context.fillStyle`, set the context transformation matrix with `context.translate()`, `context.rotate()`, then draw in the context with `context.fillRect()`.

```
function render() {
  context.fillStyle = "lightsalmon";
  context.fillRect(0, 0, canvasWidth, canvasHeight);
  // animation method
  context.save();
  context.fillStyle = "salmon";
  context.translate(square.xPosition, square.yPosition);
  context.rotate(square.rotate);
  context.fillRect(-square.width/2, -square.height/2, square.width, square.height);
  context.restore();
  // requestAnimationFrame
}
```

Awesome! You drew a shape in the canvas... now let's animate it! We'll create a movement method to increment the rotation and position of the `square`. The transformation matrix property `rotate` is a value of 0 to 1; 1 represents 360 degrees. Where we place the movement method to manipulate the `square` properties will be very important.

```
function movement(shape) {
  shape.rotate += 0.01;
  shape.xPosition += 0.1;
}
```

Since we have the movement method, let's start by building it into the render method. The most important thing is we need to make sure we're not constantly updating the context transformation matrix every time movement method occurs. So `context.save` makes sure that doesn't happen and `context.restore` applies the initial state again. The last thing we'll do it use the requestAnimationFrame method so we're only calling the render method every 60 frames a second :D

```
function render() {
  context.fillStyle = "lightsalmon";
  context.fillRect(0, 0, canvasWidth, canvasHeight);
  movement(square);
  context.save();
  context.fillStyle = "salmon";
  context.translate(square.xPosition, square.yPosition);
  context.rotate(square.rotate);
  context.fillRect(-square.width/2, -square.height/2, square.width, square.height);
  context.restore();
  window.requestAnimationFrame(render);
}

window.requestAnimationFrame(render);
```

There we go! The square is slowly rotating and flying off the canvas! You can check out the final version on CodePen :D

 {% codepen https://codepen.io/brianmontanaweb/pen/jgWPmV %}