Introduction to Canvas

This article is an introduction to the HTML5 canvas element. Here we will cover the basics of how to create the element, draw, and animate on it, as well as discuss a bit about when and how we can use it.

What and Why

Canvas is an element in which you can draw graphics using JavaScript. It works very differently than SVG, for example. While in SVG you create elements, and changes in them will change the objects on the screen, in canvas you can use commands to draw things step by step, like a painter, and anything you draw is permanent.

See the Pen gajxNe by Lucas Bebber (@lbebber) on CodePen.

This has its upsides and downsides: It makes it harder to do simple things like moving an object around; to do that, you have to keep track of every object and clear the canvas every frame. However, it is very useful when you do want that behavior – for example, if you want to make a drawing app. Canvas also allows pixel manipulation, which is useful for things like photo editing and anything that requires fine-grained control of graphics. You should also consider using canvas when graphic performance is an issue. In this demo, I’m animating 1.500 squares using both Canvas and SVG. Switch between both to compare the performance: See the Pen dYjzJp by Lucas Bebber (@lbebber) on CodePen.

Support for canvas is great by now, working very well even on mobile devices.

How

First, let’s create the Canvas element:

<canvas id="graphic"></canvas>

For accessibility and fallback purposes, we can include a description of the contents of the canvas between the <canvas></canvas> tags. This description will not be visible if the browser supports canvas:

<canvas id="graphic">
Description of the graphic
</canvas>

Finally, we must define the actual size of our canvas. This should be done via width and height attributes rather than CSS, either straight in the HTML or through JS.

<canvas id="graphic" width="800" height="600">
Description of the graphic
</canvas>

You can then resize it with CSS, but think of these dimensions as the size of an image file – it will lose resolution if you stretch it with CSS. In that vein, if you want to make it look sharp on retina screens, you should make it twice the resolution:

<canvas id="graphic" width="1600" height="1200">
Description of the graphic
</canvas>
#graphic{
  width:800px;
  height:600px;
}

You can do that dynamically via JS, detecting the resolution via window.devicePixelRatio:

var canvas=document.querySelector('#graphic');
var dpi=window.devicePixelRatio;
canvas.setAttribute("width",800*dpi);
canvas.setAttribute("height",600*dpi);
canvas.style.width="800px";
canvas.style.height="600px";

Rendering Context

Canvas is simply a surface for drawing. To draw on it, we have to choose and use a rendering context.

A context is an API that exposes methods for drawing and manipulating data on a canvas. Currently, we can use either the 2d context, or the webgl context, which is used mostly for 3d or high performance content.

In this article, we will use the 2d context. To get it, call:

var canvas=document.querySelector('#graphic');
var ctx=canvas.getContext('2d');

Now we will use that context for everything we want to do on the canvas.

So what we have so far is:

See the Pen ZbjXxO by Lucas Bebber (@lbebber) on CodePen.

…well, just an empty canvas. Let’s see how to draw on it.

Shapes

Let’s start drawing a rectangle on it:

ctx.fillStyle="red";
ctx.fillRect(20,20,100,100);

  See the Pen wKxrmb by Lucas Bebber (@lbebber) on CodePen.

Note that this code is not saying “this rectangle is red” – it is saying “use the color red; now draw a rectangle”. This is how canvas works: you have to think more like painting things rather than creating objects. If we just call fillRect multiple times, it will draw them all red. To change the color, just set a different fillStyle between drawings:

// we can use colors like in CSS
ctx.fillStyle="red";
ctx.fillRect(20,20,100,100);
ctx.fillStyle="rgba(34,170,20,0.6)";
ctx.fillRect(40,80,80,100);
ctx.fillStyle="#3265f7";
ctx.fillRect(150,40,40,100);

See the Pen avjLKy by Lucas Bebber (@lbebber) on CodePen.

Now, drawing rectangles is its own special case. For most other shapes, you have to do things differently:

// stroke works the same way as fill
ctx.fillStyle="red";
ctx.strokeStyle="black"
ctx.beginPath();
ctx.moveTo(20,20);
ctx.lineTo(130,10);
ctx.lineTo(120,100);
ctx.lineTo(10,120);
ctx.closePath();
ctx.fill();
ctx.stroke();

  See the Pen OywxoY by Lucas Bebber (@lbebber) on CodePen.

So, here we tell the canvas that we are beginning a path, list all the commands necessary to draw the shape we want, and tell it to fill it and draw a stroke around it.

We can draw circles the same way, using the arc function:

...
ctx.beginPath();
ctx.arc(50,50,40,0,Math.PI*2);
...

See the Pen VvBMqG by Lucas Bebber (@lbebber) on CodePen.

It looks more complicated than it should be – in it we specify, in order: center x, center y, radius, and start angle and end angle in radians. Since we want a full circle, the end angle should be 2π.

Animating

How can we animate thins if everything we draw is permanent? By cleaning everything every frame. So first let’s create a function to call on every frame:

function draw(){
  ...
  requestAnimationFrame(draw);
}
draw();

requestAnimationFrame calls a function the next time an animation frame will be drawn. Calling the function itself creates an animation loop, to make a function be executed on every frame and thus making it possible to create animations. We can then create our animation logic:


//store positions etc. outside the function
var pos={x:0,y:100};
var speed={x:2.5,y:1.6}
...
function draw(){
  // move things every frame
  pos.x+=speed.x;
  pos.y+=speed.y;
  ...

  //draw
  ctx.fillStyle="#eee";
  ctx.beginPath();
  ctx.arc(pos.x,pos.y,40,0,Math.PI*2);
  ctx.fill();
  ...

  // loop
  requestAnimationFrame(draw);
}
draw();

  See the Pen rOrGgw by Lucas Bebber (@lbebber) on CodePen.

The animation is done, but that still leaves us with our trails. To get rid of that, we just have to use clearRect to clear the canvas every frame. This function takes 4 parameters, which are the position and the size of the rectangle we want to clear. To clear the whole canvas, just pass the whole dimensions of it:

...
function draw(){
  ctx.clearRect(0,0,600,400);
  ...

See the Pen YyjEXB by Lucas Bebber (@lbebber) on CodePen.

If we want, we can take advantage of the permanent aspect of canvas and make effects that would be difficult otherwise, such as this trail effect: See the Pen wKxPMa by Lucas Bebber (@lbebber) on CodePen.

To accomplish this, instead of cleaning the canvas completely, we draw a semi transparent rectangle over the entire area. Doing this every frame fades out previous frames gradually, thus creating trails.

This covers some of the basics. Now you can play with what we’ve seen here, and look up other ways of drawing if you want. Hope you enjoyed it!

Get Started

Free TrialInspired by what you've read? Kick off your next project with Dreamweaver. It's part of Creative Cloud. Download a trial for free!

Share your thoughts

Your email address will not be published. Required fields are marked *

*