With apologies to Tina Turner.

### Creating a Canvas

HTML5 and JavaScript can be used to build all manner of business applications. But what about having a little fun? Let’s start looking at some of the things we can do with HTML5 Canvas.

What is a Canvas? It’s simply a *drawing context*. It has a coordinate system, the ability to set stroke and fill colors, draw shapes, and so on and so forth. Think of it as a programmatic version of MS Paint. And don’t worry if you’re not an artist. I’m not either.

How do we create a Canvas? Simple! Here’s the HTML you’ll need:

<canvas id="circle1Canvas" width="320" height="200"> </canvas>

HTML5 introduces a new *<canvas>* element. We need an *id* so we can reference this Canvas with our JavaScript. We also need to establish the size of our canvas using the *width* and *height* attributes. That’s it for creating a Canvas!

### Accessing & Using a Canvas

Accessing a Canvas is simplicity in itself:

`circle1Canvas = document.getElementById("circle1Canvas");`

There’s not a lot though that you can do with a Canvas. What you really need in order to do anything is get a *Context*. That’s easy enough to do:

`circle1Context = circle1Canvas.getContext("2d");`

Don’t worry about the **“2d”** parameter for the time being. That’s the only option there is for now. It provides a measure of extensibility, for example there’s been talk of there being a **“webgl”** context that could be created in the future.

### Ok, now I’ve Got a Context

Now we’re to the point where we can start doing something. But before we do that, let’s go over some basic Context fundamentals. There’s two important concepts we need to know when working with a Context:

- Path
- Properties

We start off by beginning a Path, drawing some shapes on the Path and then *stroke* the path—which forces it to be drawn.

// we have to begin a path in order to do anything context.beginPath(); // our path will be comprised of an arc context.arc(/* don't worry about parameters for now */); // let's fill in the path context.fill(); // let's draw the path context.stroke();

Upon seeing this code hopefully you were wondering how the fill color was determined? The line color? Line style and line width? This is where *Properties* come in to play.

A Context has the following properties that are of interest to us:

- lineWidth: measured in pixels
- lineCap: One of: “butt”, “round”, “square”
- strokeStyle: #RRGGBB hexadecimal color tuple for line color
- fillStyle: #RRGGBB hexadecimal color tuple for fill color

From our code snippet above *context.stroke()* is affected by the following Context properties:

- lineWidth
- lineCap
- strokeStyle

Whereas *context.fill()* is affected by the property:

- fillStyle

It’s important to understand that the functions *fill()* and *stroke()* ** apply to the entire path** from start to finish, meaning the set of properties for everything in the path will be the same. If you need a different set of properties then you need to start another path. A Context can have several different paths drawn simultaneously as we shall see shortly.

### Context Geometry

Before we can delve any further into the *arc* function parameters we need to understand the Context coordinate system. We need to know that because we must specify *where* to draw the arc.

The Context coordinate system places the ** origin**, point (0,0), at the upper left-hand corner of the Canvas. As we go across the Canvas towards the right the X coordinate value increases. Likewise as we go down the Canvas towards the bottom the Y coordinate value increases. This means the bottom right-most corner of the Canvas has the coordinate (Canvas.width-1, Canvas.height-1).

This is a different coordinate system than you may be accustomed to from mathematics. Means have been provided to deal with this issue, which we’ll delve into in future posts.

### Let’s Draw an Arc

The parameters for the *arc* function are:

- X coordinate: center of arc
- Y coordinate: center of arc
- start angle: in radians
- end angle: in radians
- true/false counter-clockwise/clockwise indicator

The *start* and *end* angles behave a little differently than you’d expect, though it makes sense in light of the Canvas coordinate space, where the Y coordinate increases as we go *down* the Canvas. Essentially the unit circle is flipped “upside down”, with π/2 being at the bottom and 3*(π/2) being at top.

We can easily compensate for this “flipping” of the unit circle by subtracting 2π from both our *start* and *end* angles. Then we’ll set the counter-clockwise indicator to *true* and we’ll have established a “traditional” unit circle—with π/2 being on top!

Now we can create the following simple JavaScript for drawing arcs:

// Draw arc on specified context // context: context on which we're to draw the arc // center: point -> x,y location of center of arc // radius: radius of arc // startAngle: starting angle in radians // endAngle: ending angle in radians // (angles are standard mathematical, going counter-clockwise) function drawArc(context, center, radius, startAngle, endAngle) { context.beginPath(); context.arc(center.x, center.y, radius, 2*Math.PI - startAngle, 2*Math.PI - endAngle, true); context.fill(); context.stroke(); }

One final point (ha!)—the *center* is of type *Point* which I’ve defined as:

// define a simple mathematical point function point(x, y) { this.x = x; this.y = y; }

### Let’s Draw a Wheel (Ok, a Circle)

It’s simple to use our *drawArc* function to draw a circle. I’ve created a *drawCircle* function capturing this functionality:

// Draw circle on specified context function drawCircle(context) { center = new point(context.canvas.width/2, context.canvas.height/2); radius = context.canvas.height/2 - context.lineWidth/2; context.lineWidth = 15; context.lineCap = "butt"; // Fill the arcs with Yellow context.fillStyle = "#ffff66"; // Draw Red Arc context.strokeStyle = "#cc6666"; drawArc(context, center, radius, 0 - Math.PI/6, 0.5 * Math.PI); // Draw Green Arc context.strokeStyle = "#66cc66"; drawArc(context, center, radius, 0.5 * Math.PI, Math.PI + Math.PI/6); // Draw Blue Arc context.strokeStyle = "#6666cc"; drawArc(context, center, radius, Math.PI + Math.PI/6, 0 - Math.PI/6); }

Hopefully you’ll agree that factoring out the arc drawing code in our *drawArc* function makes this function easy to understand and maintain.

### Let’s Get this Wheel Turning

Let’s first look at a function I’ve created to rotate this wheel:

// Rotate circle on specified context, rotating by specified // angle which is measured in radians function rotateCircle(context, angle) { width = context.canvas.width; height = context.canvas.height; context.clearRect(0, 0, width, height); context.translate(width/2, height/2); context.rotate(angle); context.translate(-width/2, -height/2); drawCircle(context); }

You may think it’s obvious what the statement

`context.rotate(angle);`

is doing, but it’s actually quite subtle. It’s not rotating the objects that have been drawn on the Canvas, rather it’s transforming the Canvas *coordinates*. And it’s rotating the Canvas coordinates around the origin—the point (0,0). But right now the origin is at the upper left-hand corner of the Canvas. So we use the *translate()* function to move the origin to the center of the Canvas.

The *translate()* function though doesn’t take absolute X and absolute Y coordinate positions for its parameters. It takes *differential* X and *differential* Y coordinates instead. So after rotating the Canvas coordinate plane after translating the origin to the center of the Canvas, we need to remember to translate the origin back to where we found it.

Finally we re-draw our circle. But wait, there’s one more thing we need to do. If we were to re-draw our circle now it would superimpose the new circle over the circle we’ve previously drawn. So we need to erase it first. That’s what the function *clearRect()* accomplishes. Now we have a circle that appears to have been rotated about its center.

### How Do We Keep the Wheel Turning?

We need to animate. Animation is a little tricky, though. We need to think about how fast we want our wheel to rotate (revolutions per second). How fluid of an animation we want (frames per second). And then use this information to determine the angle by which we need to rotate our wheel for each frame. Finally, we need to determine what’s going to trigger our animation routine.

The last part is simple. The *window* object provides the method *setInterval(“eval”, interval)*. The first parameter is a string that will be **eval()**ed when the corresponding interval, expressed in milliseconds, has expired.

What we want to do then is provide an eval expression causing our *rotate()* function to get invoked. Here’s my solution:

window.onload=(function() { circle1Canvas = document.getElementById("circle1Canvas"); circle1Animator = getCircle1Animator(circle1Canvas.getContext("2d")); function getCircle1Animator(context) { _period = 1000; // milliseconds, that's 1 revolution/second _fps = 20; // 20 frames per second _angle = ((2*Math.PI)/(_period/1000))/_fps; return { period: _period, fps: _fps, angle: _angle, animate: (function(){ rotateCircle(context, _angle); }) } } setInterval("circle1Animator.animate()", 1000/circle1Animator.fps); }

You can see here I’ve set the period to 1000 ms (1 second), and the frames per second to 20. This results in relatively smooth animation. You can play with the period and frames per second on your own. You’re cautioned though to not increase the frames per second above 20, as that will make the interval specified to *setInterval()* drop lower than 50 ms, which is not recommended.

### All Together Now

This is the entire source code, all in one spot.

<!doctype html> <html> <head> <script type="text/javascript"> // define a simple mathematical point function point(x, y) { this.x = x; this.y = y; } // Rotate circle on specified context, rotating by specified // angle which is measured in radians function rotateCircle(context, angle) { width = context.canvas.width; height = context.canvas.height; context.clearRect(0, 0, width, height); context.translate(width/2, height/2); context.rotate(angle); context.translate(-width/2, -height/2); drawCircle(context); } // Draw circle on specified context function drawCircle(context) { center = new point(context.canvas.width/2, context.canvas.height/2); radius = context.canvas.height/2 - context.lineWidth/2; context.lineWidth = 15; context.lineCap = "butt"; // Fill the arcs with Yellow context.fillStyle = "#ffff66"; // Draw Red Arc context.strokeStyle = "#cc6666"; drawArc(context, center, radius, 0 - Math.PI/6, 0.5 * Math.PI); // Draw Green Arc context.strokeStyle = "#66cc66"; drawArc(context, center, radius, 0.5 * Math.PI, Math.PI + Math.PI/6); // Draw Blue Arc context.strokeStyle = "#6666cc"; drawArc(context, center, radius, Math.PI + Math.PI/6, 0 - Math.PI/6); } // Draw arc on specified context // context: context on which we're to draw the arc // center: point -> x,y location of center of arc // radius: radius of arc // startAngle: starting angle in radians // endAngle: ending angle in radians // (angles are standard mathematical, going counter-clockwise) function drawArc(context, center, radius, startAngle, endAngle) { context.beginPath(); context.arc(center.x, center.y, radius, 2*Math.PI - startAngle, 2*Math.PI - endAngle, true); context.fill(); context.stroke(); } window.onload=(function() { circle1Canvas = document.getElementById("circle1Canvas"); circle1Animator = getCircle1Animator(circle1Canvas.getContext("2d")); function getCircle1Animator(context) { _period = 1000; // milliseconds, that's 1 revolution/second _fps = 20; // 20 frames per second _angle = ((2*Math.PI)/(_period/1000))/_fps; return { period: _period, fps: _fps, angle: _angle, animate: (function(){ rotateCircle(context, _angle); }) } } setInterval("circle1Animator.animate()", 1000/circle1Animator.fps); }) </script> <body> <canvas id="circle1Canvas" width="320" height="200"> </canvas> </body> </html>

#### More to Explore

Another Look at JavaScript

Canvas Breakout