HTML5 Canvas world map

in Programming

Another map experiment in HTML5 Canvas this time. The main idea is to draw the world map with paths and fill each path with random colour on mouse hover.

You can see an example of this achieved with RaphaelJS and SVG in my previous post.

With HTML5 Canvas the task is not as straight forward as with SVG and it requires some work to be done to determine that mouse pointer is inside a given path. In theory context.isPointInPath(x, y) function should do just that according to the specification. But if your project has to work in earlier versions of IE and you are using ExCanvas for that then bad news - sPointInPath is not implemented yet.

Big thanks to Sam Hasler from stackoverflow.com for his idea to create second hidden canvas with the same paths, give each path a unique colour and fill the path with this colour. When user moves the mouse over the first visible canvas, get the coordinates of the mouse pointer and the colour of this pixel on the second hidden canvas. If the colour belongs to any of the paths then redraw the canvas and fill the hovered path with it's colour.

For that we need the following HTML structure:

<!-- Second hidden canvas -->
<div id="shim" style="position:absolute; top: 0; left:0;"> 
    <canvas id="canvas2" width="800" height="500" style="background-color: #000"></canvas> 
</div> 

<!-- First visible canvas -->
<canvas id="canvas" width="800" height="500" style="background-color: #000" onmousemove="mouseMoved(event)"> 
    <p>Your browser doesn't support canvas</p> 
</canvas> 

Note that second canvas has to be located right below the first canvas. So if you position first canvas in the middle of the page, make sure second canvas is also centered.

Then on window load draw the paths on both canvases. But paths on the second canvas have to be filled in with unique colours.

When you user moves the mouse over the canvas, "mouseMoved(event)" function is executed. Here we get the colour of the current pixel, check if colour belongs to existing path and redraw the updated canvas.

function mouseMoved(event)
{
    // get mouse position x,y with a function of your choice
    var mousePos = getMousePosition(event);

    // get the data about pixel on the second canvas		
    var data = context2.getImageData(mousePos[0], mousePos[1], 1, 1).data;
    // get pixel colour
    var color = '#';
    for (var i=0, hex = ''; i<3; i++)
    {
         hex = data[i].toString(16);
         hex = (hex.length==2) ? hex : '0'+hex;
         color+=hex;
    }
    
    // get the path that colour belongs to
    var hoveredCountry = countryColours[color];
    // check that colour is different from colour at previous position e.g. mouse is not over the same country
    if ((currentColour != color) && (visitedCountries[hoveredCountry] === undefined) )
    {
         // blank the canvas
         canvasEl.width = canvasEl.width;
			
         for (var sCountry in oWorldMap) 
         {
             // draw the path
         }
         // update the current colour
         currentColour=color;		
    }
}

The drawback of course is that path colours have to be unique. If background colour is not solid, for example you use gradients, you might want to think of a different method.

View the demo on GitHub.