Tutorial #55
2D graphics - Introduction to Canvas Intermediate   2014-06-06


Generating Two Dimensional Graphics within a web page is an incredibly useful skill to master. You can display graphs and diagrams on the fly, you can create animations that engage your users and you can create full featured games.

There are several technologies available for web graphics. In the next few tutorials I am going to cover HTML5 Canvas and Scalable Vector Graphics (SVG) which are the primary technologies for In Browser 2D graphics.

Adobe Flash has been, and is, widely used for 2D graphics but this requires a plugin. This is not a big deal for desktop browsers but Apple made the decision not to support Flash on the iPad, which is a serious issue for some applications. Indeed, Adobe themselves are moving away from Flash and supporting HTML5.

WebGL is an exciting technology for 3D graphics in the browser. It is great for rendering and interacting with complex 3D objects such as buildings or molecular structures. But it is really overkill for most 2D graphics, both in terms of developing code and browser performance. I hope to cover it separately at some point.

So the real choice in 2D graphics is between Canvas and SVG. These work in two very different ways and each has its own pros and cons, so there is no right or wrong choice. It depends on what you want to draw and how you want users to interact with it.

There are two fundamental differences between the two technologies.

SVG is a File Format for drawing Vector Graphics

Canvas is a JavaScript API for drawing Pixel Graphics

While both technologies ultimately manipulate pixels on the screen, Canvas does this directly, whereas SVG abstracts this, which can make some types of operation, especially drawing curves, easier to develop in SVG. The difference is similar to that between Adobe Photoshop, which does pixel manipulation, and Adobe Illustrator which works with vectors.

SVG is an XML-based file format that contains the drawing instructions. You need to create that file using a tool like Illustrator or programatically using an SVG drawing library such as Raphaёl, Paper.js or D3.

Canvas is a JavaScript API - there is no such thing as a Canvas file format. You create Canvas objects by writing JavaScript commands that implement the drawing. That is what I am going to cover here.

In practice, you won't use Canvas directly very much. Instead you will use one of the Canvas drawing libraries such as KineticJS or Fabric.js. These libraries abstract away some of the messy details of drawing and, in particular, let you manipulate graphic objects from the browser DOM, which allows you to bind mouse events to them, etc. Even so, understanding how Canvas works is an important first step. This tutorial shows you how to draw all the basic types of graphic object using Canvas.

Demo 1 screenshot for this tutorial

Understanding the Code

In order to draw with Canvas you need to first define a canavs element in your page with the <canvas> </canvas> tag pair. You don't put anything between the tags, but in the opening tag you should define an element id, a width and height.
For example: <canvas id='mycanvas' width='400' height='300'> </canvas>

Although you can define width and height within a CSS style specification, this is not recommended. In some cases these are not handled correctly and the size and shape of the canvas is not what you expect.

In a script block the first steps are to fetch the canvas DOM element using its id and then get the Graphics Context from that. You can think of the Context as the data structure that contains all the functions that are used to draw within the canvas.

You get the Context by calling getContext("2d") on the canvas element. Note that the "2d" is required, even though this is currently the only value.

Now you have a context you can call any of the drawing methods on it. The core set of drawing operations include lines, arcs, bezier and quadratic curves, rectangles, text and image display. Combinations of these primitives allow you to draw pretty much anything.

Canvas uses a Coordinate frame that has coordinate 0, 0 in the Top Left with positve X values to the Right and positive Y values going down the page.

One or more Lines define a Path and with Canvas you can Stroke a Path, equivalent to drawing a line, and you can Fill a path, equivalent to coloring in the area defined by the path. Accordingly you can define a strokeStyle and a fillStyle using a CSS like color definition, or in more complex cases, a color gradient.

When drawing multiple paths and objects you can avoid unexpected results by using beginPath and closePath to start and end a specific drawing operation.

With those concepts in mind, let's walk through the demo and draw an example of each of the main types of graphic object.

First of all we draw a simple straight line. We define the strokeStyle to be red, we begin a new path and moveTo the starting coordinate, without drawing anything. We draw a lineTo to the end coordinate, close the path and stroke the path. Without stroke you would see nothing as that function call is what actually renders the defined path onto the screen. This gives us the red line in the top left hand corner.

Next we draw another line beow the first using an identical set of commands. But look at this - the second line is thinner that the first ! This is an issue that I think confuses everyone who works with 2D graphics at some point. The issue is that we want to draw a line that is One Pixel Wide. But in the first case we ask Canvas to draw this at Y coordinate 25. A one pixel wide line at the coordinate actually has its top edge at 24.5 and bottom edge at 25.5. The graphics machinery can only render complete pixels, not half ones, and so it renders this line as Two Pixels wide - Y coordinate 24 and 25.

The way you fix this is to ask Canvas to draw the line at Y coordinate 50.5 in the second example. In this case one pixel goes from 50.0 to 50.9999 and so it can be rendered as a single pixel at coordinate 50.

This is a common cause of confusion. In some cases the effect is not noticeable but if you want to draw diagrams with horizontal and vertical lines then it is extremely important.

Next we draw a more complex path that represents a triangle using a moveTo and 3 lineTo calls where the final coordinate is the same as the first in order to close the path. In fact, we could leave out this last call and simply use closePath which will join the first and last points automatically.

Because we have defined a closed path we can not only stroke the path but also fill it.

The same approach can be used to draw rectangles or other polygons, but Canvas provides two shorthand functions specifically for rectangles - strokeRect and fillRect do what you expect, given a starting X,Y coordinate, a width and a height.

There is no function to specifically draw a Circle in Canvas. Instead there is the arc function that will draw part or all of a circle, given a starting X,Y coordinate, a radius, start and end angles and a direction (clockwise or anticlockwise). All angles are specified in Radians and the Math.PI constant can be used to help specify these in your code.

In the first example an arc describes a semi circle, without fill and the second describes a complete circle, with fill but no stroke.

Canvas can display text using the fillText function, with style and font information being specified.

You can embed images within a Canvas using drawImage. You first create a new Image object and specify a URL to use as its src. Because the image might not have loaded by the time the drawImage function is invoked, you want to wrap that call in a function that is only called once the image has loaded.

Complex curves can be drawn using quadraticCurveTo and bezierCurveTo but these are difficult to define by hand as you need to know where to place the control points (2 for quadratic and 4 for bezier) which is not at all intuitive for most of us. These examples give an idea of how these can be used.

Demo 1 screenshot for this tutorial

These simple examples illustrate the core functions available in Canvas. But you will notice right away the big limitation of basic Canvas. It simply draws on the screen. There is no undo function and no way to bind events to graphic objects - and in fact there are no graphic objects.

With SVG on the other hand every graphic object is part of the DOM and so can be manipulated directly. So you will hear the argument that SVG is a better choice for many applications. But that is where the Canvas libraries and frameworks like KineticJS come into play. These create graphic objects that can be manipulated on the fly and have browser events bound to them, enabling animations, drag and drop and much more. The core Canvas machinery is still what is drawing to the screen, but the libraries update their own sets of objects and efficiently redraw the canvas as required.

I will describe how to use these libraries in later tutorials.

More Information

Mozilla Canvas Tutorial

Canvas Cheat Sheet

W3C HTML Canvas 2D Context Specification

Code for this Tutorial

Share this tutorial

Comment on this Tutorial

comments powered by Disqus