Drawing Graphics with Canvas
From MDC
Most of this content has been rolled into the more expansive Canvas tutorial, this page should probably be redirected there as it's now redundant.
Contents |
[edit] Introduction
With Firefox 1.5, Firefox includes a new HTML element for programmable graphics. <canvas> is based on the WHATWG canvas specification, which itself is based on Apple's <canvas> implemented in Safari. It can be used for rendering graphs, UI elements, and other
custom graphics on the client.
<canvas> creates a fixed size drawing surface that exposes one or more rendering contexts. We'll focus on the 2D rendering context (incidentally, the only currently defined rendering context). In the future, other contexts may provide different types of rendering; for example, it is likely that a 3D context based on OpenGL ES will eventually be added to the <canvas> specification.
[edit] The 2D Rendering Context
[edit] A Simple Example
To start off, here's a simple example that draws two intersecting rectangles, one of which has alpha transparency:
<html>
<head>
<script type="application/x-javascript">
function draw() {
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
ctx.fillStyle = "rgb(200,0,0)";
ctx.fillRect (10, 10, 55, 50);
ctx.fillStyle = "rgba(0, 0, 200, 0.5)";
ctx.fillRect (30, 30, 55, 50);
}
</script>
</head>
<body onload="draw()">
<canvas id="canvas" width="300" height="300"></canvas>
</body>
</html>
The draw function gets the canvas element, then
obtains the 2d context. The ctx object can then be
used to actually render to the canvas. The example simply fills two
rectangles, by setting fillStyle to two different colors using CSS
color specifications and calling fillRect. The second
fillStyle uses rgba() to specify an alpha value along with
the color.
The fillRect, strokeRect, and clearRect calls render a filled, outlined, or clear rectangle. To render more complex
shapes, paths are used.
[edit] Using Paths
The beginPath function starts a new path, and
moveTo, lineTo, arcTo, arc, and similar methods are used to add segments to the path. The path can be
closed using closePath. Once a path is created, you
can use fill or stroke to render the path
to the canvas.
<html>
<head>
<script type="application/x-javascript">
function draw() {
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
ctx.fillStyle = "red";
ctx.beginPath();
ctx.moveTo(30, 30);
ctx.lineTo(150, 150);
// was: ctx.quadraticCurveTo(60, 70, 70, 150); which is wrong.
ctx.bezierCurveTo(60, 70, 60, 70, 70, 150); // <- this is right formula for the image on the right ->
ctx.lineTo(30, 30);
ctx.fill();
}
</script>
</head>
<body onload="draw()">
<canvas id="canvas" width="300" height="300"></canvas>
</body>
</html>
Calling fill() or stroke() causes the current path
to be used. To be filled or stroked again, the path must be recreated.
[edit] Graphics State
Attributes of the context such as fillStyle,
strokeStyle, lineWidth, and lineJoin are
part of the current graphics state. The context provides two
methods, save() and restore(), that can be used to
move the current state to and from the state stack.
[edit] A More Complicated Example
Here's a little more complicated example, that uses paths, state, and
also introduces the current transformation matrix. The context
methods translate(), scale(), and rotate()
all transform the current matrix. All rendered points are first
transformed by this matrix.
<html>
<head>
<script type="application/x-javascript">
function drawBowtie(ctx, fillStyle) {
ctx.fillStyle = "rgba(200,200,200,0.3)";
ctx.fillRect(-30, -30, 60, 60);
ctx.fillStyle = fillStyle;
ctx.globalAlpha = 1.0;
ctx.beginPath();
ctx.moveTo(25, 25);
ctx.lineTo(-25, -25);
ctx.lineTo(25, -25);
ctx.lineTo(-25, 25);
ctx.closePath();
ctx.fill();
}
function dot(ctx) {
ctx.save();
ctx.fillStyle = "black";
ctx.fillRect(-2, -2, 4, 4);
ctx.restore();
}
function draw() {
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
// note that all other translates are relative to this
// one
ctx.translate(45, 45);
ctx.save();
//ctx.translate(0, 0); // unnecessary
drawBowtie(ctx, "red");
dot(ctx);
ctx.restore();
ctx.save();
ctx.translate(85, 0);
ctx.rotate(45 * Math.PI / 180);
drawBowtie(ctx, "green");
dot(ctx);
ctx.restore();
ctx.save();
ctx.translate(0, 85);
ctx.rotate(135 * Math.PI / 180);
drawBowtie(ctx, "blue");
dot(ctx);
ctx.restore();
ctx.save();
ctx.translate(85, 85);
ctx.rotate(90 * Math.PI / 180);
drawBowtie(ctx, "yellow");
dot(ctx);
ctx.restore();
}
</script>
</head>
<body onload="draw()">
<canvas id="canvas" width="300" height="300"></canvas>
</body>
</html>
This defines two methods, drawBowtie and dot, that
are called 4 times. Before each call, translate() and
rotate() are used to set up the current transformation
matrix, which in turn positions the dot and the bowtie. dot
renders a small black square centered at (0, 0). That dot is
moved around by the transformation matrix. drawBowtie
renders a simple bowtie path using the passed-in fill style.
As matrix operations are cumulative, save() and
restore() are used around each set of calls to restore the
original canvas state. One thing to watch out for is that rotation
always occurs around the current origin; thus a translate()
rotate() translate() sequence will yield different results than a
translate() translate() rotate() series of calls.
[edit] Compatibility With Apple <canvas>
For the most part, <canvas> is compatible with Apple's and other implementations. There are, however, a few issues to be aware of, described here.
[edit] Required </canvas> tag
In the Apple Safari implementation, <canvas> is an element implemented in much the same way <img> is; it does not have an end tag. However, for <canvas> to have widespread use on the web, some facility for fallback content must be provided. Therefore, Mozilla's implementation has a required end tag.
If fallback content is not needed, a simple <canvas id="foo" ...></canvas> will be fully compatible with both Safari and Mozilla -- Safari will simply ignore the end tag.
If fallback content is desired, some CSS tricks must be employed to mask the fallback content from Safari (which should render just the canvas), and also to mask the CSS tricks themselves from IE (which should render the fallback content). Todo: get hixie to put the CSS bits in
[edit] Additional Features
[edit] Rendering Web Content Into A Canvas
Mozilla's canvas is extended with the drawWindow
method. This method draws a snapshot of the contents of a DOM window into the canvas. For example,
ctx.drawWindow(window, 0, 0, 100, 200, "rgb(0,0,0)");
would draw the contents of the current window, in the rectangle (0,0,100,200) in pixels relative to the top-left of the viewport, on a black background, into the canvas. By specifying "rgba(0,0,0,0)" as the color, the contents would be drawn with a transparent background (which would be slower).
With this method, it is possible to fill a hidden IFRAME with arbitrary content (e.g., CSS-styled HTML text, or SVG) and draw it into a canvas. It will be scaled, rotated and so on according to the current transformation.
Ted Mielczarek's tab preview extension uses this technique in chrome to provide thumbnails of web pages, and the source is available for reference.


