Exploring browser animation options: Part three, Canvas

Published by | Saturday, October 6th, 2012

Over the past few weeks my blog posts have been exploring different animation options available in modern browsers. First, I looked at using CSS, and then jQuery. In this article I want to take a look at a third option, Canvas, and talk about some of the advantages and limitations of working with this part of the HTML5 specification.

What is Canvas?

Canvas part of a series of expansions to the HTML language known as HTML5. Specifically, the <canvas> tag is an element that lets you identify a drawable bitmap region within an HTML5 page.

The canvas element is known as an immediate mode drawing element, which means as soon as you execute Canvas drawing commands, they get converted into pixels and will no longer be accessible as objects. This is wildly different than drawing with other methods like CSS, jQuery, SVG, or Flash where what you draw and animate remain accessible as vector objects. It’s one of the main weaknesses of working with Canvas.

That being said, what you lose in convenience you gain in speed, as drawing with Canvas is way faster than drawing any other way. This is because Canvas doesn’t need to keep track of objects created like other methods do. So it’s ideal for game development, and other drawing tasks where performance is critical.

The immediate drawing mode also has some less-than-ideal side effects when it comes to animation. For instance, when you want to animate an object, you don’t simply tell that object to move to a new location. You have to draw every element in the canvas, including the object you want to move at new coordinates. It’s up to you to keep track of the objects because having no saved objects also means you don’t have a list of display objects when you work with Canvas. Thankfully, there are some cool libraries like CreateJS that help overcome some of the weaknesses associated with Canvas.

Drawing with Canvas

Let’s take a closer look at Canvas drawing with a simple example. If your browser has a problem displaying the example below, try viewing it with this direct link.

JS Bin

HTML

The HTML of a Canvas drawing is pretty simple. In the body of our document, there’s only one element—the <canvas> tag. When we create a Canvas element, we need to give it an ID (in this case htmlcanvas) since we are going to need to target the element with JavaScript. The canvas is usually assigned a width and height. Canvas elements are bitmaps, so it makes sense for you to specify a width and a height.

Since we’re going to use JavaScript to draw into this canvas, we’ve added an onload=”init();” handler to our tag. That way, our code will execute after the page has completely loaded.

CSS

Using the <canvas> tag creates a completely transparent inline element. If you want to see it, you’ll need to style it with some CSS. In our CSS above, we simply type in a background color for our body and our canvas. Since I want to center the <canvas> element, I need to change the display type from inline to block. Finally, to center the canvas on our window, we use a margin of 0 (top and bottom) and auto so the element centers horizontally.

Now that our canvas tag is set up and we can see it in the browser, all the HTML and CSS you’ll need is written.

JavaScript

Because the onload handler calls on an init() function, the first thing we do in our JavaScript is write that function. After that, we need to target the <canvas> element, which we store in a variable called canvas. Since JavaScript doesn’t understand Canvas drawing commands we never draw into the canvas directly. Once you have the canvas variable in place, you will need to add your drawing commands using something called the context.

The context

To draw things with Canvas we need to let JavaScript know that we want to access Canvas drawing methods using the context. You create a context with the getContext() method, then the context will transfer your drawing commands into the canvas. Since there could theoretically be different kinds of drawing contexts, like 2D or 3D, we’ll need to specify that we want our context to work with the 2D commands, like this:

context = mycanvas.getContext("2d");

This specification tells the browser that we want to draw into mycanvas with 2D methods. Now we can simply execute any of the 2D drawing commands. In our example below, I’ve added some extra variables so I can keep track of the object’s size as well as the center of the canvas and the rotation property of the object. Altogether our variable definitions look like this:

var canvas = document.getElementById("htmlcanvas"),
context = canvas.getContext("2d"),
SIZE = 100,
centerX = canvas.width/2,
centerY = canvas.height/2,
rotation = 0;

If you want to draw a rectangle into your canvas, you will need to specify a fillStyle with the fillStyle property:

context.fillStyle = "rgb(162, 216, 255)";

You can also specify a color using CSS-compatible notation, so elements like an RGB color, as seen in the example above, will work just as well as a hex #F00, or a color name like blue. Once you have a color designated, you can use one of the canvas 2D primitives to draw something. Here’s an example:

context.fillRect(0, 0, SIZE, SIZE);

Canvas tough primitives

Canvas has very few graphic primitives, or in layperson terms, ways to draw simple shapes. You can draw lines, rectangles, arcs, and curves, but there’s not a lot of variety and arcs and curves can be pretty complicated. Let’s say you wanted to draw the outline of a circle. There is no circle method, so you would use an arc with the following code:

Canvas Example

That’s enough to scare a lot of people away from Canvas because drawing simple shapes like circles involves converting the starting and ending points of the arc from radians to degrees, and specifying the X and Y positions, a radius, the starting and ending point of the arc, plus the direction of the curve, which has a scary boolean parameter called anticlockwise.

Transformations

If drawing primitives with Canvas is scary, transformations like rotations can be downright terrifying. That’s because transformations in Canvas affect the entire context. In other words, you can’t normally rotate individual objects (remember—there are no objects in Canvas, just pixels), you have to rotate everything. If you want to rotate an object, you have to first save the way the canvas looks (it’s called saving the context), rotate, then restore the context to its original state. Here’s what that looks like:

Canvas example

In the JavaScript tab of the above example, try removing the lines context.save(); and context.restore();

If we don’t save and restore the context, Canvas draws all objects with the same rotation, and all objects after the original transformation will be drawn at the same rotation as well. Additional transformations would compound the problem.

Animating

When you animate you’re essentially drawing every object on the canvas over and over again, and you have to keep track of all the objects and their movements yourself. Here’s a simple example of a rectangle animating:

Canvas Example

This doesn’t seem too bad until you have to keep track of a ton of elements. In that case you’ll probably have to create classes, methods, and properties to keep track of the objects dancing across the screen.

Canvas with rainbows and kittens

Animating with Canvas is great for creating games, but it’s definitely tough to work with. That’s why there are so many libraries that simplify this process. If you come from the Flash gaming arena like me, you need to check out a library like EaselJS, which is part of the CreateJS library of products that Adobe acquired. It makes drawing in Canvas more approachable by creating a display list and adding a bunch of methods for drawing primitives and animating objects, which makes the process more like working with Adobe Flash. Check out my course EaselJS First Look to get started.

Share this:Share on Facebook0Tweet about this on Twitter4Share on Google+0Pin on Pinterest0Share on LinkedIn0

lynda.com - start learning today

Tags:


Leave a Reply