개발자 소식으로 돌아가기

Re-Introducing Canvas

2012년 7월 31일제작:Christian Heilmann

This is a guest post by Christian Heilmann, a Developer Evangelist at Mozilla. In this post, he explains some of the canvas element's lesser-known properties and its potential for creating web tools and animations.

When HTML5 was put together, a lot was about improving features that existed in the past, adding more semantics and making quite a few things "web native" that historically were only possible with Flash or other plugins. Many of those features were welcome throughout the web community — after all, who could say no to native audio and video with improved accessibility? Others, however, caused a few raised eyebrows.

Canvas the troublemaker

One thing that appeared as an amazing opportunity, but also didn't sit too well with markup purists, was the canvas element. There were quite a few interesting concerns about it, many of them still unresolved.

• Without JavaScript, a canvas element does nothing in a browser. Canvas is one of the elements that fortify the dependency on JavaScript in modern web development. Purists of HTML had problems with that, because JavaScript is strongly tied to the browser. It is not a part of the abstract web stack of HTML being sent over HTTP, and can only be consumed by a browser specifically able to execute the JavaScript.

• There is no accessibility in the canvas element. When you modify it via script, nothing in the HTML DOM changes, meaning assistive technology like screenreaders will not get notified of any change. This confused people. It felt like re-inventing Java Applets or having a black box in the page — something we accused Flash of being, while claiming HTML5 was the cure for that dilemma.

• There was no fallback for it. We just did not have anything like that in HTML before, so we were confused as to how to progressively enhance this without blocking out older browsers. After all, HTML5 claimed to be backwards compatible, but there was nothing in HTML4 to which canvas was comparable.

• Old versions of Internet Explorer do not support canvas, which made it a no-go for a lot of developers.

First things first: providing a fallback

Yes, not every browser out there supports canvas. But that doesn’t mean you should just tell the people who come to your site to download another browser you are very excited about. This doesn’t help the web as a whole, and in many cases the people coming to your site cannot do that.

On the other hand, shoe-horning new technology into old browsers with libraries and shims may make us feel good because we are not leaving people out, but it also makes us a slave to old technology. If you use a library to bring canvas functionality to Internet Explorer 6, you also make an agreement to test in that browser and make sure that what you did performs well in it. Do you want to spend that time?

The best way to deal with this situation is to provide a fallback that makes sense and gives people something to look at or look forward to. Instead of telling people to change, you just show them what they are missing. With luck, this will entice them to upgrade or change.

The simplest way of doing that is to show an image as the fallback:

<canvas>
    <img src="screenshot.png" alt="screenshot of the rotating elephant animation"/>
    </canvas>
    

This does a few things: it tells the visitor that they are missing something, it provides a fallback for assistive technology, and, as an extra bonus, you provide an image that link services could offer as a thumbnail. Say, for example, you want to promote your cool canvas-based effect on Facebook. It would offer this screenshot as the preview — handy, isn’t it?

For animations, you could provide a video as the fallback, and a static image as a secondary fallback for the video to make everybody happy. Got it? Cool, let’s move on.

What is canvas good for?

So what exactly does canvas allow us to do that we couldn’t before? Well, it is — as the name says — a canvas in the page. You can use it to paint things, with a very low-level API and a few lines of code. To see this in action, check out this example on jsFiddle.

Simple ways of drawing

As you can see, it is very simple to allow a user to paint something inside your document. The Canvas painting API is ridiculously simple (which was another criticism leveled by developers accustomed to more sophisticated APIs like the ones Flash provides us with) but there is a reason for it: canvas was made for speed. The thinking was that the simpler it is, the more performant it would be. Canvas was meant to allow us to create dynamic graphics, but also to show things quickly on the page — meaning gaming in the browser is not science fiction or the task of plugins any longer.

When you scratch the surface of canvas, however, you will find that there is much more to the little element and the API than meets the eye. Imagine the canvas as a piece of paper and you have a pen in your hand. You can move the pen to a certain place on the canvas with the moveTo() method. This places the pen there, but it doesn’t leave a mark yet.

If you want to paint something you have the lineTo(), arc(), arcTo(), quadraticCurveTo(), bezierCurveTo() and rect() functions. You can also just paint rectangles using fillRect() and strokeRect(). You start new parts of your painting with beginPath() and you end them with closePath() (which is a bit annoying, and the opposite of begin is end and not close). All of these are explained in this drawing shapes with canvas tutorial.

You can also change your pen to paint in different colours and widths (explained in this applying styles and colours to canvas tutorial). You can choose a stroke style, a stroke width, a stroke and fill colour, the line ending (round, butt or rectangle), and you can define how lines should join. And if that’s not enough, you can also define gradients and drop shadows for your paintings. You can even define the compositing you want to have, which means that new paintings do not simply paint over the others but make them darker or lighter or replace part of them. Text is also supported, and you can write from a certain coordinate in all directions and rotations.

You can wipe your canvas using a clearRect() over its whole size or by resizing it dynamically.

A colourful etch-a-sketch in your page?

In the past, I always referred to canvas as an Etch-a-Sketch in your page (which actually inspired my colleague Gregory Koberger to develop a fully working canvas Etch-A-Sketch) — you can paint on it and wipe it and that is it — nothing there remains behind in the DOM or anywhere as an object. That is oversimplifying it, though, as you can retain what you painted and there are a few more tricks up canvas’ sleeve.

A dynamic coordinate system

One of the things that might be hard to get your head around at first is that the coordinate system of the canvas is not fixed. By default, the coordinate system starts at the top left at (0,0) and goes to width and height of your canvas on the bottom right. But you can change that system any way you want to whilst you paint. As explained in this canvas transformations tutorial you can save and restore the coordinate system of the canvas and scale, rotate, and translate it any way you want.

Jolly good, you might say, but what does that mean? Well, it means that you can draw a shape once and do all the necessary calculations to do that, and if you want to copy it in different sizes and rotations on different parts of the canvas, all you need to do is to change the underlying coordinate system instead of recalculating all the points. This makes canvas not an Etch-a-Sketch in the page, but a cutout animation tool with transparency layers over a background. You can even stretch and rotate all these transparencies without ripping them or losing quality. And that was good enough for Monty Python's Terry Gilliam to produce something awesome, so it should be good enough for you.

Storing, converting and sharing

This still leaves the issue of your paintings not being stored. Of course canvas has a solution for that — one that is bafflingly useful when it comes to images in the browser. With canvas you have pixel-level access to everything that is in it. You can use getImageData() to read all the pixels as an array containing the red, green, blue and opacity values of each pixel. You can manipulate this array and write it back using putImageData(). You can also use toDataURL() to create a data URL from the array that you can save as a PNG or set as the src value of an image in the page.

Working with images

There are many more ways to manipulate images with canvas, mostly using the drawImage() method to, well, draw images onto the canvas. You can also scale and rotate the images, and even crop parts out of one image to re-use in your new collage. The fun thing about this is that not only can you use images as a source, but you can also grab frames from a video element in the page and do things with that one.

The limit is your imagination — and sometimes the performance

Using all of this, you can do incredible things with the black sheep of the HTML5 element arsenal. Check out these canvas demos complete with source codes to get some inspiration. Of course, with lots of power comes some responsibility, and it is important to follow a few canvas performance best practices when you start animating your ideas. But that should not hold you back — you can always optimise once you are happy with what you created.

Thinking outside the box

It is very tempting to use canvas for what it is meant to do — paint stuff in the browser. But now that you know about its dynamic coordinate system and the option to access each pixel, have a go at thinking what can be done with that. It is exciting to see what people come up with. Michal Budzynski for example used array manipulation to create 3D images from normal images in canvas, I used image manipulation to create an in-browser thumbnail generator and a bookmarklet to crop images in any web site or a way to add a blue beanie to your photo to celebrate web standards day.

Resources

• Mozilla has a very detailed HTML5 canvas overview page with links to other tutorials, libraries and demos

• Jacob Seidelin has a great Canvas cheat sheet that has everything you need on two pages