It is possible to directly use <canvas> in conjunction with a map.
Here’s an example
Details of how to use <canvas> are available from the Mozilla Developer Centerdeveloper.mozilla.org/en/Canvas_tutorial
However there are a few drawbacks.
Not all browsers support <canvas>, and there is some variation between levels of support in the browsers the do support it. The HTML5 standard which defines <canvas> is still evolving.
In particular, MSIE does not currently support <canvas>.
There is an add on called ExplorerCanvas which can intercept <canvas> canvas commands and convert some of them into VML and Filter commands that MSIE does recognise, but many <canvas> features are not supported, the end result is extremely slow.
Therefore it’s a good idea to test for <canvas> support, and provide some sort of alternative when that support is absent. In the above example, I have a small test canvas which always exists outside the map. If that canvas doesn’t have a .getContext() Method, then I don’t create the active canvas, but create a simple marker instead.
Cryptic error messages
Firefox3 <canvas> error messages are seriously cryptic. They thow a couple of lines of complete gibberish, out of which the only bit that means anything to me is the line number on which the error occurred.
<Canvas> elements don’t seem to accept attributes like “onclick” and “onmouseover”, etc.
In some circumstances they seem to eat such events, even in the transparent parts of the <canvas>, preventing them from dropping through to the map controls and overlays below them. In other cirsumstances the clicks do drop through. I don’t know what causes the change in behaviour.
Therefore, if you’re going to use <canvas> in such a way that it overlaps clickable objects, do perform a quick sanity test to see if the clicks are going to reach the underlying elements.
Placing <canvas> elements in a map
You could place your <canvas> elements directly onto one of the map panes, but then you’d have to manage the lat/lng handling yourself.
In the example, I use an ELabel as a container for the <canvas>. This gives the advantage of having all the standard methods like .hide(), .setPoint(), etc. built in.
You could place <canvas> elements inside other structures, such as info windows, or GControls.
You could appendChild a <canvas> element to the map container, but watch out for the click passing problem mentioned above.
<canvas> images and onload function
I don’t usually recommend using onload functions, but if you’re using images in <canvas> then it is necessary to ensure that the image has completely loaded before you attempt to use it in <canvas>. One way to do this is to load the image in the main inline code and launch the <canvas> from the onload event.
The awkward thing with rotations in <canvas> is that they don’t rotate the object, they rotate the canvas axes around point (0,0), the top left corner of the canvas workspace. That’s not what we generally want. We usually want to rotate an element about its centre.
In other graphics systems, we would translate the object to (0,0), then rotate it, then perform the inverse translation to put it back where it came from.
That’s basically what we have to do in <canvas> but the inverse translation takes place in the rotated frame, so instead of it being (-x,-y) it becomes (x*sin(a)+y*cos(a),y*cos(a)-y*sin(a)). The whole rotate-about-centre operation looks like this:
var cosa = Math.cos(angle); var sina = Math.sin(angle); canvas.clearRect(0,0,x*2,y*2); canvas.save(); canvas.rotate(angle); canvas.translate(x*sina+y*cosa,x*cosa-y*sina); canvas.drawImage(img,-x,-y); canvas.restore();
The save() and restore() operations put the axes back where they originally came from.