HTML5 Canvas Basics

by Patrick Horgan

(Back to canvas tutorials)

What is a canvas?

Web developers have long wanted a standard element that you could draw on. Even more they wanted it to be interactive. They wanted it to work like flash. In HTML5, their wishes are granted. An HTML5 canvas element gives you access to a two dimensional drawing context that permits all the 2d drawing and transform and coloring and patterning and rendering text you might want. In this tutorial, I'm just going to touch on some of the fun things you can do with the HTML5 canvas.

How do I get one on the page?

First you need to put it into your html, and then you need to be able to get to it from javascript. I'm going to show you an HTML5 page template this first time, but later, I'll just show you enough javascript to explain the current topic.

<!DOCTYPE html> <html> <head> <meta charset='utf-8'> <title>Cool Canvas Page</title> <style> canvas{ border: 1px solid rgb(0,0,0); } </style> </head> <body> <canvas id='thecanvas' width='200' height='160'> If you see this that means that your browser doesn't support the cool new HTML5 canvas elements. Boy are <em>you</em> missing out! You might want to upgrade. </canvas> <script> var thecanvas=document.getElementById("thecanvas"); if(thecanvas){ var ctx=thecanvas.getContext("2d"); if(ctx){ // here would be the code that did the drawing. }else{ alert("no context! Can't go on"); } } else { alert("Couldn't find the canvas!"); } </script> </body> </html>

The first thing to notice here is the HTML5 DOCTYPE, it's simply <DOCTYPE html>. Isn't that nice and simple? All modern browsers will shift into standards mode when they see it even if they don't yet support HTML5.

Next, in the <head> element we find <meta charset 'UTF-8'>. This tells the browser that we're using utf-8 character encoding, a good choice usually, since your editor on your system probably uses the same encoding. It must occur within the first 1024 characters of the file.

Of course <title> puts the title into the browser.

As a simple illustration I've put a <style>section into the header which gives the canvas a 1 pixel wide solid black outline. In the normal course of events we'd have one or more separate style sheets included here instead of having the style section inline.

In the body, the only thing I've included is the canvas element. I've specified it as having a width or 200 pixels and a height of 160 pixels. If you don't specify the width it has a default value of 300 pixels, and if you don't specify the height, it has a default height of 150 pixels. It's important to specify an id, as I have here, so that we can find the canvas element from javascript. The text between the <canvas> and </canvas> tags will only be seen by people using browsers which don't support the canvas element.

Just before the body element finishes you see the javascript in between the <script> and </script> and tags. First we use document.getElementById("thecanvas") to get a reference to the canvas and store it in a variable thecanvas. Once we've gotten that, we ask the canvas for its 2d context using thecanvas.getContext("2d");. It returns a reference to a CanvasRenderingContext2D object which we store in the variable ctx. We need that to do any two-dimensional graphics with the canvas element. I've included tests for the validity of the canvas and context references, and just call alert if they don't get set up correctly. If they don't, there's really not anything you can do, you won't be able to draw into the canvas.

Now we're ready to draw something

The first thing we'll do is to draw filled and an outlined rectangles. Coordinates are in (x,y) pairs running from (0,0) at the top left, to (canvas.width,canvas.height) on the bottom right.

The arguments for strokeRect(x,y,width,height) and fillRect(x,y,width,height) are the same.

The color used to fill the rectangle comes from the fillStyle and the color used to outline the rectangle comes from the strokeStyle. In our first example you see four rectangles, one filled one stroked, and two both filled and stroked. The third one is filled and then stroked, and the fourth stroked and then filled. As you can see, the color for fill is green, the outline color is purple, and the size of the line used for the outline is 5 pixels.

ctx.strokeStyle='rgb(255,0,255)'; ctx.fillStyle='rgb(5,255,0)'; ctx.lineWidth=5; ctx.fillRect(10,10,40,40); ctx.strokeRect(70,10,40,40); ctx.fillRect(130,10,40,40); ctx.strokeRect(130,10,40,40); ctx.strokeRect(190,10,40,40); ctx.fillRect(190,10,40,40);

All of the rectangles are 40 pixels X 40 pixels. Our first rectangle has the top left at coordinates (10,10), the second has the top left at coordinates (70,10), the third, which we first fill and then stroke, has the top left at coordinates (130,10) and the fourth, which we stroke and then fill has the top left at coordinates (190,10). If you look closely, you'll see that the 5 pixel outline is half way inside the rectangle, and half way outside. The center of the line runs along the edge of the rectangle. If you fill after stroking, like we did in the last example, the fill covers up half the width of the stroke.

Let's go down a new path

With an HTML5 2-d context you can create a path and then either stroke the path or fill the inside of the path. You do this be calling context.beginPath() to clear the path, and then by calling one of a series of things that create subpaths. At any time you can call context.fill() or context.stroke() to either fill the inside of the path, or to draw a line upon the path. The first one I'll show you is

context.arc(x, y, radius, startAngle, endAngle [, anticlockwise ] )

N.B. as of writing this, some browsers, for example SeaMonkey, fail to draw if you don't specify the last argument. This is clearly their bug, but it's easy enough to put the false explicitly rather than leaving it off. Historically this was true, but now all browsers get this right.

The brackets, [] around the last argument tell you that it's an optional argument

RADIANS = (DEGREES * π)/180

DEGREES = (RADIANS * 180)/π

If you're used to thinking in degrees instead of radian, just remember that halfway around the circle is 180° and is also π radians. To convert degrees to radians, you divide the degrees by 180 and multiply by π.

Drawing a few arcs

// purple arc ctx.beginPath(); ctx.arc(120,120,100,0,Math.PI/6); ctx.lineWidth=30; ctx.strokeStyle='rgb(255,0,255)'; ctx.stroke(); // blue arc ctx.beginPath(); ctx.arc(120,120,65,Math.PI/3,Math.PI/2); ctx.strokeStyle='rgb(55,88,255)'; ctx.stroke(); // yellow arc ctx.beginPath(); ctx.arc(120,120,30,5*Math.PI/6,4*Math.PI/3); ctx.strokeStyle='rgb(255,200,12)'; ctx.stroke(); // green arc ctx.beginPath(); ctx.arc(120,120,90,Math.PI,3*Math.PI/2); ctx.strokeStyle='rgb(0,200,12)'; ctx.stroke();

I know, I know, it's not a very interesting example, all I did was to draw a few arcs. The first one goes from 0, to π/6 radians. Just for practice, let's convert the radians to degrees, we divide by π, that leaves us 1/6, and multiply by 180°. That leaves us 30°. So we swept through an arc of 30°. Not so hard, huh? For the others, you can verify them by comparing their angles against the Angles in Radians diagram. As an example, the yellow arc is drawn like, ctx.arc(120,120,30,5*Math.PI/6,4*Math.PI/3);. Looking at the third argument, it says that the arc starts at 5*Math.PI/6, so looking up at the diagram, would would look for 5π/6. The fourth argument is 4*Math.PI/3, so we'd look up at the diagram for 4π/3, and sure enough, those are the same angles as we see the yellow arc being drawn through.

Although we've only stroked paths so far, don't fear, in the next example we'll be filling as well.

So what else can make subpaths?

There's a lot more you can do with the canvas, you can set clipping paths, do translations and rotations, and draw text. I'm not going to explain all that here in a basics article, but I'll leave you with one more simple example that does a bit of translation and rotation.

We only know how to draw one!

var drawLeaf=function(ctx) { ctx.beginPath(); // Go to the origin, at 0,0, and draw a triangle, with the // point at 0,0, and the base at the bottom ctx.moveTo(0,0); ctx.lineTo(-19,80); ctx.lineTo(19,80); ctx.closePath(); ctx.fill(); // Now go to the bottom and draw a circle centered on the middle // of the base. ctx.arc(0,80,19,0,Math.PI*2,false); ctx.fill(); } var drawLeaves=function(ctx){ colors=['red','orange','yellow','green','blue','indigo','violet', 'pink','lime','aqua','maroon','fuchsia']; // move the origin (0,0) to the middle of the canvas ctx.translate(ctx.canvas.width/2,ctx.canvas.height/2); // 12 times pick one of the colors, // rotate 30 degrees and draw one leaf. for(var ctr=0;ctr<12;ctr++){ ctx.fillStyle=colors[ctr]; ctx.rotate(Math.PI/6); drawLeaf(ctx); } } var c3=document.getElementById('c3'); var ctx3=c3.getContext('2d'); drawLeaves(ctx3);

The first thing we do in our function drawLeaf() is to draw a triangle with the point at (0,0) and the base pointing down. Then at the bottom, we'll draw a circle to give a nice rounded end to it. That's all there is to the function drawLeaf(). Usually we don't draw anything around (0,0). It's the top left of our canvas. Don't worry about it for now, we'll take care of that in the next function.

In draw leaves we are going to call drawLeaf() 12 times. Each time we're going to rotate another 30 degrees. That's why drawLeaf draws the leaf starting at the origin, because rotations are all around the origin.

To make it visible, we're going to shift the origin to the middle of our canvas. That's why we call translate(ctx.canvas.width/2,ctx.canvas.height/2). Each time through the loop we rotate 30 degrees, pick a color from our array, and then call drawLeaf to do the one thing he knows how to do. Draw a leaf pointing down. The poor guy doesn't know that we're spinning the world under him.

Finally at the bottom, we look up our canvas, get its context, and pass it to drawLeaves() to make our pretty picture.

(Back to canvas tutorials)