Friday, November 21, 2008

Using the Canvas Html element

In this post I'm going to show a little example of using the HTML 5 Canvas element.

The Canvas element provides a way to draw graphics using Javascript. It is currently supported in browsers such as Safari, Firefox, Opera and Chrome. Sadly it is not supported in IE.

There's a lot of documentation on how to use this element. The Mozilla Developer Center provides a nice tutorial: Canvas tutorial. Also the HTML 5 specification document provides a detailed description of the element in The canvas element section.

The Example



In order to play learn about this element I decided to create a toy program that takes an image and creates a little Jigsaw Puzzle of it.

Here's how the page looks:



The running version of this program can be found here.

The Code



The code to create this little program is very simple. First the canvas element is specified as follows:


...
<body onload="init();" >
<p>Canvas Object Tests</p>
<canvas id="myCanvas" width="700" height="600"
onmousemove="moveHandler(event);"
onmousedown="mouseDownHandler(event);"
onmouseup="mouseUpHandler(event);"
onmouseout="mouseUpHandler(event);"></canvas>
<hr/>
<div id="notSup" ></div>

</body>
...


Four event handlers are added to the canvas element to handle mouse interactions to move the pieces of the puzzle.

The init function is called in the "load" event of the body element.


var imageName = 'http://farm3.static.flickr.com/2161/2543461722_a68f3d1e2e_m.jpg';
...
function init() {
img = new Image();

img.onload = function() {
imageLoaded = true;
draw();
}
img.src = imageName;

theObjects = createImageSegments(3,3,{width:240,height:180},{x:5,y:10});

draw();
}


The createImageSegments function creates the pieces of the puzzle.


function createImageSegments(lines,cols,imageSize,basePosition)
{
var incrY = imageSize.height/lines;
var incrX = imageSize.width/cols;
var theX = basePosition.x;
var theY = basePosition.y;
var result = new Array();
for(var i = 0;i < lines;i++) {
for(var j = 0;j < cols;j++) {
result.push(new ImageSegment(this.img,
{x:theX,y:theY},
{width:incrX,height:incrY},
{x:(j*incrX),y:(i*incrY)},
{width:incrX,height:incrY}));
theY += 2 + incrY;
}
}
return shuffleArray(result);
}


The ImageSegment objects are defined by the following function:


function ImageSegment(image,position,size,insidePoint,insideSize){
this.image = image;
this.position = position;
this.size = size;
this.insidePoint = insidePoint;
this.insideSize = insideSize;

this.draw = function(context) {
context.drawImage(
this.image,
this.insidePoint.x,
this.insidePoint.y,
this.insideSize.width,
this.insideSize.height,
this.position.x,
this.position.y,
this.size.width,this.size.height);
};

this.isInside = function(point) {
return (this.position.x <= point.x &&
this.position.y <= point.y &&
this.position.x + this.size.width >= point.x &&
this.position.y + this.size.height >= point.y);
}
}



Two methods are defined: one to draw the section of the image and one to determine of a given point hits the current object.

In order to draw the image the following function is defined:


function draw() {
var myCanvas = document.getElementById("myCanvas");
if(myCanvas.getContext) {

var ctxt = myCanvas.getContext('2d');
ctxt.clearRect(0,0,myCanvas.width,myCanvas.height);
if (imageLoaded) {
for(var i = 0;i < theObjects.length;i++) {
theObjects[i].draw(ctxt);
}
}

// Draw the frame
...


In order to allow the user to drag the pieces of the puzzle across the canvas the following mouse event handlers were added to the canvas.


function mouseDownHandler(e)
{
var x = e.pageX - e.target.offsetLeft;
var y = e.pageY - e.target.offsetTop;
for(var i = 0; i < theObjects.length;i++) {
var theObject = theObjects[i];
if (theObject.isInside({x:x,y:y})) {
dragging = true;
lastPoint.x = x;
lastPoint.y = y;
currentDragObject = theObject;
}
}
}

function mouseUpHandler(e)
{
dragging = false;
lastPoint.x = -1;
lastPoint.y = -1;
}

function moveHandler(e) {
if (dragging) {
var x = e.pageX - e.target.offsetLeft;
var y = e.pageY - e.target.offsetTop;
var deltaX = x - lastPoint.x;
var deltaY = y - lastPoint.y;


currentDragObject.position.x += deltaX;
currentDragObject.position.y += deltaY;


lastPoint.x = x;
lastPoint.y = y;
draw();
}
}



Basically what these event handlers do is to calculate the new position of the image being dragged and update the canvas.

Conclusion




The Html Canvas object provides a nice way to draw custom graphics in a web page using Javascript. For future posts I'm going to try to rewrite the example from the
JavaFX Script, Silverlight and Flex posts using Javascript and Canvas.