INFO-I400 Topics in Informatics

Graphics, Animation, and Multimedia for the Web

Lesson 3: Introduction to the Canvas

Prerequisite: 2 JavaScript Fundamentals

Reading Assignment

Read one of the following (or more if you like). Try not to panic if you see JavaScript constructs that we haven’t covered yet, such as function definitions and event handlers.

Basic Concepts

Canvas Element

The canvas element, with its default dimensions explicitly specified:

<canvas id="canvas1" width="300" height="150">
   Fallback text
</canvas>

Fallback text is anything you want to be displayed in browsers that do not implement the canvas element. For example, you could urge users to upgrade their browsers, and tell them what they’re missing.

Coordinates

The top left corner of the canvas is x = 0, y = 0. The x value increases to the right, and the y value increases downwards.

2D Context Object

To draw on the canvas, first we must get the canvas and its 2D drawing context:

var canvas = document.getElementById('canvas1');
var ctx = canvas.getContext('2d');

The context object (the value of the variable ctx here) is the one we’ll use to actually draw.

Simple Colors and Rectangles

We can set now set some colors and draw some basic geometric shapes. For now we will just use simple, named colors—more on colors later. The fill color is used to fill in the interior of a shape, and is set with the fillStyle method. The stroke color is used to draw the outline or edge of a shape, and is set with the strokeStyle method.

Here’s an example:

ctx.fillStyle = "red";
ctx.strokeStyle = "black";
ctx.fillRect(10, 20, 50, 40);
ctx.strokeRect(10, 20, 50 40);

Our rectangle has a top right corner at (10, 20), width 50, height 40; it is both filled (red interior) and stroked (black edges).

Setting the properties fillStyle and strokeStyle does not affect anything previously drawn; it merely sets up the styles (here, colors) for whatever is drawn afterward. The methods fillRect, strokeRect, and clearRect act immediately, using whatever colors or styles have been set, or the default style, if none have been set.

To erase the rectangle, we can say

ctx.clearRect(10, 20, 50, 40);

The clearRect method also acts immediately.

Example 03-01

Draw two overlapping rectangles, one green and the other red.

Text

You can draw strings on the canvas using ctx.fillText(text, x, y) and ctx.strokeText(text, x, y). You can also set the font size and face with ctx.font = "SIZE FACE", e.g., ctx.font = "48pt sans-serif",

Example 03-02

Demonstrate text which is filled, stroked, filled then stroked, and stroked then filled.

We will learn more about fonts, and much more about text in general, in a later lesson; but for now, this should give us the ability to draw basic labels on our canvas.

Paths

While fillRect, strokeRect, and clearRect do their drawing immediately, we usually take a more deferred approach to complex drawing. This involves laying down a path and then filling and/or stroking the path. The “path” is, roughly speaking, just a sequence of lines, curves, and shapes to be rendered (drawn) later. There are three or four steps: begin the path, put stuff in the path, (optionally) close the path, and then render (fill and/or stroke) the path.

  1. Begin the path. This just creates an empty path:

    ctx.beginPath();
  2. Put stuff in the path. “Stuff” can be lines, curves, and shapes. Here are some examples:

    ctx.moveTo(x0, y0); // position pen without drawing
    ctx.lineTo(x1, y1); // add line from (x0, y0) to (x1, y1) to path
    ctx.rect(x, y, width, height); // add rectangle to path
    
    // Add the arc of a circle to the path.
    ctx.arc(x, y, radius, startAngle, endAngle, anticlockwise);
    
    // Add an ellipse to the path
    // (not well supported 2012 July 20)
    ctx.ellipse(x, y, radiusX, radiusY, rotation, startAngle,
                endAngle, anticlockwise);

For the arc and ellipse, (x, y) is the center; the rotation and angles are measured in radians. Remember, 2π radians = 360 degrees, and you can use the constant Math.PI as in Java. Angles are measured in a clockwise direction, with angle 0 pointing to the right (like a clock hand at “3:00”), π/2 radians (90 degrees) is down (6:00), π radians (180 degrees) is left (9:00), and 3π/2 radians (270 degrees) is up (12:00). The last argument, anticlockwise, is boolean and indicates the direction of drawing from startAngle to endAngle: a semicircle from 0 to 180 degrees “hangs down” if anticlockwise is false (i.e., it is drawn clockwise), but “floats up” if it is true.

Ellipses would be cool, and they are in the specification, but currently not well supported.1

All of these statements just put stuff into the path, without actually drawing anything. It’s as though you’re laying down a trail with pheromones which busy painter ants can follow when it is time to do their work. Without the pheromones, the ants would not know where to paint; but the pheromones themselves do not do any painting.

There are also methods for quadratic and cubic Bezier curves. Those are more complex, and we’ll save them for later.

  1. Optionally, close the path. This lays a pheromone trail in a straight line from the ending point of the path back to its beginning. Consequently, if you draw two connected lines and close the path, you’ll have a triangle; if you don’t close the path, you just have two lines.

    ctx.closePath();
  2. Render the path: that is, either fill it, or stroke it, or both. These statements send your painter ants down the pheromone trail, happily applying their paintbrushes.

    ctx.stroke();
    ctx.fill();

Example 03-03

Draw two overlapping rectangles, one green and the other red, like before, but this time use a path.

Example 03-04

Draw a semicircle above a couple of horizontal lines.

Paths and Subpaths

Earlier, I described a path as “just a sequence of lines, curves, and shapes to be rendered (drawn) later.” That statement was approximately true, but we need to look at the nature of paths in a little more depth now, so pay close attention! The canvas specification states:

A path has a list of zero or more subpaths. Each subpath consists of a list of one or more points, connected by straight or curved lines, and a flag indicating whether the subpath is closed or not. A closed subpath is one where the last point of the subpath is connected to the first point of the subpath by a straight line. Subpaths with fewer than two points are ignored when painting the path.2

This means:

  1. A path is not a mere sequence of lines and curves; it is organized into subpaths.
  2. Each subpath can be open or closed; if closed, a line connects its end back to its beginning.
  3. Subpaths with zero or one point have no effect on the drawing.

To build (and render) paths effectively, we need to understand how each operation contributes to the current path or subpath:

Example 03-05

Fill blue and yellow rectangles.

So if you begin a path, make a rectangle, close the subpath, fill with blue paint, make another rectangle, close the subpath, and fill with yellow paint, do you now have a blue rectangle and a yellow rectangle?

What, then, is the minimum we need to do, to get two differently colored rectangles? Do we need to close any subpaths? Do we need to begin a path?

Exercise

Draw a yellow triangle and a blue pentagon.

References


  1. Firefox 13.0.1 and Chromium Version 20.0.1132.57 reported errors when I tried to use the ellipse function on 2012 July 20.

  2. HTML Living Standard, Sec. 4.8.11.1.6 Building paths