HTML5 canvas - the basics
Table of contents
Introduction
The HTML5 specification
includes lots of new features, one of which is the
canvas
element. HTML5 canvas
gives you an
easy and powerful way to draw graphics using JavaScript. For each
canvas
element you can use a "context" (think about a page in
a drawing pad), into which you can issue JavaScript commands to draw
anything you want. Browsers can implement multiple canvas contexts and the different APIs provide the drawing functionality.
Most of the major browsers include the 2D canvas context capabilities - Opera, Firefox, Konqueror and Safari. In addition, there are experimental builds of Opera that include support for a 3D canvas context, and an add-on that allows 3D canvas support in Firefox:
- Download an Opera build featuring 3D canvas, HTML video and File I/O support.
- Find more out about using the Opera 3D canvas context.
- Find more out about obtaining and using the Firefox 3D canvas context.
This article takes you through the basics of implementing a 2D canvas context, and using the basic canvas functions, including lines, shape primitives, images, text, and more. You are assumed to have mastered JavaScript basics already.
Note that you can download all the code examples in a single zip file, as well as viewing them live using the links below.
The basics of using canvas
Creating a canvas context on your page is as simple as adding the
<canvas>
element to your HTML document like so:
<canvas id="myCanvas" width="300" height="150">
Fallback content, in case the browser does not support Canvas.
</canvas>
You need to define an element ID so you can find the element later in your JavaScript code, and you also need to define the width and height of the canvas.
That's your drawing pad created, so now let's put pen to paper. To draw
inside your canvas you need to use JavaScript. First you find your canvas
element using getElementById
, then you initialize the context
you want. Once you do that, you can start drawing into the canvas using the
available commands in the context API. The following script (try running the example live) draws
a simple rectangle into the canvas we defined above:
// Get a reference to the element.
var elem = document.getElementById('myCanvas');
// Always check for properties and methods, to make sure your code doesn't break
// in other browsers.
if (elem && elem.getContext) {
// Get the 2d context.
// Remember: you can only initialize one context per element.
var context = elem.getContext('2d');
if (context) {
// You are done! Now you can draw your first rectangle.
// You only need to provide the (x,y) coordinates, followed by the width and
// height dimensions.
context.fillRect(0, 0, 150, 100);
}
}
You can choose to include this script inside the
head
of your document, or in an external file - it's up to
you.
The 2D context API
Now we have created our first basic canvas image, let's look a bit more deeply into the 2D canvas API, and see what is available for us to make use of.
Basic lines and strokes
You already saw in the example above that it's really easy to draw rectangles colored the way you want.
With the fillStyle and strokeStyle properties you can easily set the colors used for rendering filled shapes and strokes. The color values you can use are the same as in CSS: hex codes, rgb(), rgba() and even hsla() if the browser supports it (for example this feature is supportd in Opera 10.00 and later).
With fillRect
you can draw filled rectangles. With
strokeRect
you can draw rectangles only using borders, without
filling. If you want to clear some part of the canvas, you can use
clearRect
. These three methods all use the same arguments:
x, y, width, height. The first
two arguments tell the (x,y) coordinates, and the last two arguments tell
the width and height dimensions for the rectangle.
To change the thickness of the lines, you can use the
lineWidth property. Let's look at an example that uses fillRect
,
strokeRect
clearRect
and more:
context.fillStyle = '#00f'; // blue
context.strokeStyle = '#f00'; // red
context.lineWidth = 4;
// Draw some rectangles.
context.fillRect (0, 0, 150, 50);
context.strokeRect(0, 60, 150, 50);
context.clearRect (30, 25, 90, 60);
context.strokeRect(30, 25, 90, 60);
This example gives you an output like that seen in Figure 1.
Figure 1: Example rendering of fillRect, strokeRect and clearRect.
Paths
The canvas paths allow you to draw custom shapes. You draw the "outline"
first, then choose to draw the stroke and fill the shape at the end, if you
wish. Creating a custom shape is simple - to start drawing the path, use
beginPath()
, then draw the path that makes up your shape using
lines, curves and other primitives. Once you are done, call
fill
and stroke
if you want to fill your shape or
to draw the stroke, then call closePath()
to finish off your
shape.
An example is in order - the following code will draw a triangle:
// Set the style properties.
context.fillStyle = '#00f';
context.strokeStyle = '#f00';
context.lineWidth = 4;
context.beginPath();
// Start from the top-left point.
context.moveTo(10, 10); // give the (x,y) coordinates
context.lineTo(100, 10);
context.lineTo(10, 100);
context.lineTo(10, 10);
// Done! Now fill the shape, and draw the stroke.
// Note: your shape will not be visible until you call any of the two methods.
context.fill();
context.stroke();
context.closePath();
This will give an output like that shown in Figure 2.
Figure 2: A basic triangle.
I have also prepared a more complex paths example featuring lines, curves and arcs - check it out.
Inserting images
The drawImage
method allows you to insert other images
(img
and canvas
elements) into your canvas
context. In Opera you can also draw SVG images inside your canvas. This is
quite a complex method, which takes three, five or nine arguments:
- Three arguments: The basic
drawImage
usage involves one argument to point to the image to be included, and two to specify the destination coordinates inside your canvas context. - Five arguments: The middle
drawImage
usage includes the above three arguments, plus two to specify the width and height of the inserted image (in cases where you want to resize it). - Nine arguments: The most advanced
drawImage
usage includes the above five arguments, plus two values for coordinates inside the source images, and two values for width and height inside the source image. These values allow you to dynamically crop the source image before bringing it into your canvas context.
The following example code shows all
three types of drawImage
in action:
// Three arguments: the element, destination (x,y) coordinates.
context.drawImage(img_elem, dx, dy);
// Five arguments: the element, destination (x,y) coordinates, and destination
// width and height (if you want to resize the source image).
context.drawImage(img_elem, dx, dy, dw, dh);
// Nine arguments: the element, source (x,y) coordinates, source width and
// height (for cropping), destination (x,y) coordinates, and destination width
// and height (resize).
context.drawImage(img_elem, sx, sy, sw, sh, dx, dy, dw, dh);
This should render as shown in Figure 3.
Figure 3: Example drawImage
rendering.
Pixel-based manipulation
The 2D Context API provides you with three methods that help you draw
pixel-by-pixel: createImageData
, getImageData
, and
putImageData
.
Raw pixels are held in objects of type ImageData
. Each
object has three properties: width, height and
data. The data property is of type CanvasPixelArray, holding a number of elements equal
to width*height*4
; this means that for
every pixel you define the red, green, blue and alpha values of all the
pixels, in the order you want them to appear (all the values range from 0 to
255, including alpha!). Pixels are ordered left to right, row by row, from
top to bottom.
To better understand how all this works take a look at an example which draws a block of red pixels.
// Create an ImageData object.
var imgd = context.createImageData(50,50);
var pix = imgd.data;
// Loop over each pixel and set a transparent red.
for (var i = 0; n = pix.length, i < n; i += 4) {
pix[i ] = 255; // red channel
pix[i+3] = 127; // alpha channel
}
// Draw the ImageData object at the given (x,y) coordinates.
context.putImageData(imgd, 0,0);
Note: not all browsers implement createImageData
. On such
browsers, you need to obtain your ImageData
object using the
getImageData
method. Please see the provided example code.
With the ImageData
capabilities you can do a lot more than that. For
example, you can do image filtering, or you can do mathematical
visualisations (think fractals and more). The following code shows you how
to create a simple color inversion
filter:
// Get the CanvasPixelArray
from the given coordinates and dimensions.
var imgd = context.getImageData(x, y, width, height);
var pix = imgd.data;
// Loop over each pixel and invert the color.
for (var i = 0, n = pix.length; i < n; i += 4) {
pix[i ] = 255 - pix[i ]; // red
pix[i+1] = 255 - pix[i+1]; // green
pix[i+2] = 255 - pix[i+2]; // blue
// i+3 is alpha (the fourth element)
}
// Draw the ImageData
at the given (x,y) coordinates.
context.putImageData(imgd, x, y);
Figure 4 shows the color inversion filter applied to an Opera graphic (compare to Figure 3, which shows the original color scheme of the Opera graphic).
Figure 4: The color inversion filter in action.
Text
The Text API is only available in recent WebKit builds, and in Firefox 3.1 nightly builds, but I decided to include it here for completeness.
The following text properties are available on the context
object:
font
: Specifies the font of the text, in the same manner as the CSSfont-family
property)textAlign
: Specifies the horizontal alignment of the text. Values:start
,end
,left
,right
,center
. Default value:start
.textBaseline
: Specifies the vertical alignment of the text. Values:top
,hanging
,middle
,alphabetic
,ideographic
,bottom
. Default value:alphabetic
.
You have two methods for drawing text: fillText
and
strokeText
. The first one draws the text shape, filled using
the current fillStyle, while the latter draws the text
outline/border using the current strokeStyle. Both take three
arguments: the text you want to display, and the (x,y) coordinates to define
where to render it. There's also an optional fourth argument
- maximum width. This causes the browser to shrink the text to fit inside
the given width, if that's needed.
The text alignment properties affect the text position relative to the (x,y) coordinates you give to the drawing methods.
At this point, an example is in order - the following code is a simple canvas text "hello world" example.
context.fillStyle = '#00f';
context.font = 'italic 30px sans-serif';
context.textBaseline = 'top';
context.fillText ('Hello world!', 0, 0);
context.font = 'bold 30px sans-serif';
context.strokeText('Hello world!', 0, 50);
Figure 5 shows the output of this example.
Figure 5: A simple canvas text rendering.
Shadows
The Shadow API gives you four properties:
shadowColor
: Sets the shadow color you want. The value allowed is the same as the CSS color values.shadowBlur
: Sets the amount of blur on the shadow, in pixels. The lower the blur value, the sharper the shadows are. It gives a very similar effect to gaussian blur in Photoshop.shadowOffsetX
andshadowOffsetY
: Specifies the x and y offset of the shadow, again in pixels.
Usage is very straightforward, as shown by the following canvas shadow code example:
context.shadowOffsetX = 5;
context.shadowOffsetY = 5;
context.shadowBlur = 4;
context.shadowColor = 'rgba(255, 0, 0, 0.5)';
context.fillStyle = '#00f';
context.fillRect(20, 20, 150, 100);
This will render as shown in Figure 6.
Figure 6: Example canvas shadow - a blue rectangle with a red shadow.
Gradients
The fillStyle
and strokeStyle
properties can
also have CanvasGradient
objects assigned to them, instead of
CSS color strings - these allow you to use color gradients to color your
lines and fills instead of solid colors.
To create CanvasGradient
objects you can use two methods:
createLinearGradient
and createRadialGradient
. The
former creates a linear gradient - lines of color all going in one direction
- while the latter creates a radial gradient - circles of color emanating
out from a single point.
Once you have the gradient object you can add color stops along the
gradient using the addColorStop
method of the object.
The following code shows you how to use gradients:
// You need to provide the source and destination (x,y) coordinates
// for the gradient (from where it starts and where it ends).
var gradient1 = context.createLinearGradient(sx, sy, dx, dy);
// Now you can add colors in your gradient.
// The first argument tells the position for the color in your gradient. The
// accepted value range is from 0 (gradient start) to 1 (gradient end).
// The second argument tells the color you want, using the CSS color format.
gradient1.addColorStop(0, '#f00'); // red
gradient1.addColorStop(0.5, '#ff0'); // yellow
gradient1.addColorStop(1, '#00f'); // blue
// For the radial gradient you also need to provide source
// and destination circle radius.
// The (x,y) coordinates define the circle center points (start and
// destination).
var gradient2 = context.createRadialGradient(sx, sy, sr, dx, dy, dr);
// Adding colors to a radial gradient is the same as adding colors to linear
// gradients.
I have also prepared a more advanced example, which makes use of a linear gradient, shadows and text. The example produces an output as seen in Figure 7.
Figure 7: Example rendering using a linear gradient.
Online canvas demos
If you want to see what others have done with Canvas, you can take a look at the following projects and demos:
- Newton polynomial
- Canvascape - "3D Walker"
- Paint.Web - painting demo, open-source
- Star-field flight
- Interactive blob
Summary
Canvas is one of the most interesting HTML5 features, and it's ready to be used within most modern Web browsers. It provides all you need to create games, user interface enhancements, and other things besides. The 2D context API includes a wealth of functionality in addition to that discussed in this article - I hope you've gained a good grounding in canvas, and a thirst to know more!
Read more...
This article is licensed under a Creative Commons Attribution, Non Commercial - Share Alike 2.5 license.
Comments
The forum archive of this article is still available on My Opera.