Introduction to HTML5 Canvas: Part 2 (Example)

This post gives you a complete walkthrough of creating a diagram on canvas.

Introduction

This post is the sequel to my previous post of Introduction to HTML5 <Canvas>: Part 1 (also on CodeProject) and shows a walkthrough example of using canvas for some static 2D drawing (for Introduction to animation wait for my next post). This is the second post to my “HTML5 <Canvas>” series.

In the previous post, we saw how <Canvas>'s context gives us an API with set of methods and properties to draw on its surface. Now in this post, we will use some of those methods to draw a logo of one of my favorite game PORTAL2.

Above is the actual completed art drawn on canvas, and below we will see how we draw that art piece by piece. And let me tell you this article is just for introducing you to the use of various methods provided by canvas and may not show the best and efficient way of drawing on it. So keeping that in mind, let’s get started.

First Things First

As you may know by now, before any drawing we need to have a handle on <Canvas> element to do manipulations, and that handle is the context object of the <Canvas>. The context of the <Canvas> element provides us all that API of methods to do drawing and manipulation on <Canvas>. To get the context we use "getContext()” method and pass in the string “2d” as parameter * . Also, since we feel more natural using “degrees” for specifying angles but all functions of canvas context take clockwise “radians” as parameter, we’ll use the following function to convert degree into anticlockwise* radians. Also note that it is recommended to use "save()” and “restore()” methods of context to save the current state of the context on the stack before any manipulation & transformation and restore it back to its previous state respectively.

If we want, we could also draw the above rectangle as a small horizontal line with line-width equal to the height of the above rectangle. But that would involve more steps like creating a path and then drawing the line along the path, which is more complicated for this primitive shape.

2’s Slant: The slant rectangle is just a simple rectangle but elevated to an angle of 35 Degrees. So to create a slant rectangle, first we will translate the origin of the coordinate space (i.e., the transformation matrix) to the top edge of 2’s base rectangle using “translate( newX, newY)” and then rotate the coordinate space by 35 degrees in anti-clockwise direction, taking new origin as the pivot/center, by using the “rotate(radians)” method and then simply draw the rectangle using fillRect( x, y, width, height).

Also note how we have used save() before any manipulation and then used restore() after drawing, this makes sure that the translation and rotation of coordinate space does not affect rest of drawings we are about do later on. This way context state of the canvas always remains in previous state, in this case, the Initial state. Remember save() and restore() doesn’t save/restore the contents on the canvas, it just save/restore the state of properties/attributes like “fillStyle”, “strokeStyle”, “lineWidth”, etc. and coordinate space on the <Canvas>drawing context.

2’s Arc: The top arc of number “2” cannot be drawn by rectangle methods but can be simply drawn like a line arc whose line-width is equal to the height of previous rectangles. For creating any line shape, we first start a path by calling “beginPath()” method, then call any shape method like “rect()”, “arc()”, “lineTo()”, etc. to add them to path and then optionally call “closePath()” method to complete the path and start new one. For this step, we will start a new path and add an arc to the path by using “arc(x,y,radius, startAngle, endAngle)” method. So far, we have only created the path, to actually draw the arc on canvas we will call “stroke()” method. But since stroke will draw with default color, so before calling “stroke()“ we will set “strokeStyle” property of context to light gray.

Head: The blue guy’s head is also a simple circle, but since we not have a direct method like “fillCircle()”, we’ll need to use a similar trick as we did for “2’s” arc. We will start a new path, add a full 360 degree arc and fill it with color. For filling we will use “fill()” method accompanied by “fillStyle” property set to light blue color, to fill the path with blue color thereby creating a filled circle.

A thing to note here is that when you create a path you have two options, either to call “stroke()” method to draw that path using current “strokeStyle”, as we did earlier and will do for hand and legs, or to call “fill()” method to fill the path with current “fillStyle”, as we did just now for head and will do for tummy. We also have an option of calling “clip()” method (discussed later).

Tummy: The tummy of the blue guy is also drawn by creating a triangle path and filling the triangle with blue color. To create the triangle path, we first start a new path, move the initial drawing point from origin or any last position (lets it be O) to a point on the wall below the head using “moveTo(x, y)” method which moves the drawing point from current draw point to a new point (let it be A) without adding the line between O & A to the path, then use “lineTo( x, y)” method which moves the drawing point to a new point (let it be B) and also adds the line between A & B to the path. Similarly add the third point (let it be C) to complete the three points of the triangle. This will also add the line between B & C to the path. Now optionally you can use “lineTo( x, y)” method to go back to point A and add line between C & A to the path and thus closing the path but by default the “fill()” will automatically assume a line between opening and closing points and will fill the enclosed area.

Hand: For hand, we will simply create two lines as we did above. A to B for shoulder to elbow and B to C for forearm. But by default, the line is 1px wide first we will set “lineWidth” property of context to 25px, then since line’s ends and joints (at B) are rectangular by default, we will set both “lineCap” property, for line ends and “lineJoin” property to “round”. And since we want lines to be drawn instead of filling the space between, we will call “stroke()” method of context.

But there is a problem with the above code, the result leg doesn’t look like what we want. Part of the leg is hidden in the wall, so we need to clip the extra piece of leg from the drawing.

Clip Leg: With the above code, we didn’t get what we wanted but fret not, “clip()” method comes to rescue. Clip is similar to fill method but instead of filling the area enclosed by the path with some color, it creates an enclosed invisible boundary (outline of triangle is drawn in figure only to highlight the clip area) where any drawing on it clipped if it does not lie in the clipping region and only the drawing lying in the clipping region is shown. So to clip legs protruding through the wall, we’ll first create a clipping region by creating a triangular path with one of its side coinciding with the wall and then draw the leg same as we drew the hand.

And that’s all we need to do to create the that Portal 2 Logo, Below is an animated canvas showing all the pieces we used to draw it (in fact, every image in this article is actually drawn on the <Canvas> elements) accompanied by complete combined code I used to draw it. So I hope that you liked this article and stay tuned for my next post where I will discuss 2D animation on <Canvas>.

The simplest ay to test above code is to create a Canvas Element in your HTML fie and set its Id Attribute to "portalcanvas". And Paste above code in a script tag, that's all. You can also see my previous code about basics of canvas Canvas Part 1

You're showing how to use canvas as svg (kind of) - i doubt people will actually make complex graphics with writing code. You'll probably get some tool to export (kind of) vector graphics from graphics programs to your style of drawing.

You are right in saying that what i have drawn can be drawn with use of SVG, but I have two things to say, firstly I posted this article to introduce some of the methods and properties by actually using them in drawing that stuff, secondly I believe that use of canvas is equally important when drawing static content (like graphs and charts) and dynamic content (like animations and games) and can not be done with SVG. SVG could be used for making some shapes but anything more than that is quite frankly like killing yourself . The best part i like about canvas is that it can be manipulated so easily with JavaScript without even worrying about DOM further it being stateless makes it more efficient drawing surface.