- Drawing in a Page
- Understanding the Canvas
- Drawing a Bzier Curve
- Browser Support
Understanding the Canvas
The canvas element was created by Apple for use with the Dashboard, a feature of OS X that allows floating widgets, often connected to some kind of web service. These widgets are rendered using WebKit. Adding the canvas element made rich drawing with widgets possible.
Given this origin, you might expect the canvas to be a fairly simple abstraction of the drawing systemand you would be right. Although it's object-oriented, the canvas is closer to Core Graphics than to the higher-level Cocoa drawing.
Core Graphics is a procedural API that implements the PDF rendering model, and most of the canvas calls have a direct analog in Core Graphics. Each canvas has a single graphics context object, just like a window in OS X. This design supports methods like lineTo(), which is equivalent to the CGContextAddLineToPoint() function in Core Graphics. Similarly, the CGContextSaveGState() and CGContextRestoreGState() functions, which push and pop the current graphics state, respectively, have equivalents in the canvas in the form of the save() and restore() methods on the context object.
If you've programmed with another API that exposes a PDF or PostScript model, such as Core Graphics, AppKit, or Cairo, the canvas will be very easy for you to learn.
Creating a Canvas
Before you can use any of the JavaScript code related to the canvas element, you need to create the canvas itself. You do this in the same way that you create any other HTML element:
<body onload='load()'> <canvas id='canvas' width="640" height="480">Your browser is no good. Please get a better one.</canvas> </body>
This example creates a 640 x 480 canvas in which you can draw. If the browser doesn't support the <canvas> tag, or has JavaScript disabled, it will display a simple text message instead. In real code, you'd probably display a static image or maybe a Flash applet in this section.
This command creates the canvas node in the DOM tree, but before you can draw on the canvas, you need to get a reference to it from your JavaScript code. The load() function is called when the page loads in this example. The function might look something like this:
function load() { canvas = document.getElementById('canvas'); canvas.addEventListener('click', click, false); context = canvas.getContext('2d'); }
The function's first line gets the canvas element itself. The second line defines an event handler for click events. Many things that you might want to do with a canvas are interactive, so it's common to register at least one event handler for the canvas.
The last line in this function gets the drawing context. This is the object with which you'll interact to draw on the canvas. Notice that you have to pass '2d' as the argument to the getContext() method. This is the only valid argument, according to the standard. In the third article in this series, we'll look at some not-yet-standard alternative canvas types that you can use for 3D drawing.
Drawing on the Canvas
PostScript is a simple domain-specific stack-based language. It is very terse and is designed to be easy to parse and easy to generate automatically. As a side-effect of this design, PostScript is quite painful to read, if you happen to be a human. Fortunately, although the canvas has a similar drawing model, it also has a much simpler programmer model.
When using the canvas, remember that PostScript is a stack-based language. To draw a line in PostScript, for example, you write something like this:
newpath 100 100 moveto 100 200 lineto 1 setlinewidth stroke
The first command defines a new path, which is an object in the PostScript virtual machine describing a sequence of lines and curves. The next two commands define that this is a line from (100,100) to (100,200), the first line moving the start of the path, the second appending a line segment to it. The fourth line sets the width used for line drawing with this path. The final line draws a line along this path. For more complex paths, you can also fill the interior.
The equivalent set of operations on the canvas would look something like this:
context.beginPath(); context.moveTo(100, 100); context.lineTo(100, 200); context.lineWidth = 1; context.stroke();
Aside from the camelCase function names and the parameter-passing syntax, the two sets of commands are almost identical. It's important to remember PostScript's drawing model, because you might intuitively expect a function like lineTo() to draw a line to the specified point. In fact, it appends the line segment to the current path.
If you've done any AppKit programming, you can think of the current path as being equivalent to an NSBezierPath instance. Unlike with NSBezierPath, however, you can't store the current path in a variable. The current path also isn't stored as part of the graphics state, butand this is crucialits properties are. If you saved and restored the context's graphics state after this sequence, the line width property would be set to 1, but the path would only contain the defined line segment if you didn't put a beginPath() sequence somewhere in the middle.
Everything you're likely to do in PostScript involves manipulating one of two objects: the current graphics state or the current path.