Using canvas and multi touch
I'm excited about the possibilities that touch screens and multi-touch screens will give us. I'm also interested in HTML 5 and making something that will work accross a variety of devices whether they have a touch screen or just a mouse. To play about with both I'm currently making a colouring book app that will work on an iPad, a desktop computer's browser and hopefully any other touch enabled phone browser.
<Canvas> is a new HTML tag that is part of HTML 5. It acts like a drawing surface that you draw on using JavaScript commands.
In JavaScript you can catch mouse and touch events. At first I thought that an iPad would issue mouse events as well as touch events, but it doesn't. Touch and mouse events are slightly different, but when handled correctly you can make a good interface that works with both.
This is just a simple example that responds to both touch and mouse events to draw a line on the HTML 5 canvas screen. It will work on an iPad, iPhone (and hopefully Android and other touch phones) using touch and touches. It will also work in any modern browser that supports the <canvas> tag.
To try it out click on the box above and while the mouse is still pressed drag it about. Or, if you have a touch device try touching it with one or more fingers.
The code
The HTML for the canvas tag is very simple:
<canvas id="cbook" width="400" height="300" style="position: relative;">
<strong>[Your browser can not show this example.]</strong>
</canvas>
It's a lot like an image tag, except it needs the closing tag. The content enclosed between the two canvas tags will only be seen by browsers that don't support canvas, like Internet Explorer 8. The position relative bit is needed to make capturing the mouse position work across different browsers.
The JavaScript for this was a bit more complicated. Drawing to a canvas element is very easy, capturing mouse and touch events was a bit more difficult, but the most difficult bit was working out where the mouse was in a way that worked on all the different browsers.
You can download my full JavaScript Source here, but I'll go over some of the key bits.
Drawing on the canvas area
I found Firefox's Canvas Tutorial a very helpful guide to get started with using canvas.
Here's the code I needed to know to draw lines on the canvas:
// Get canvas HTML element
var canvas = document.getElementById("cbook");
// Check that canvas is supported by the browser
if (canvas.getContext) {
// Get a context to draw with
ctx = cb_canvas.getContext('2d');
// Set line style
ctx.lineWidth = 2;
ctx.strokeStyle = "rgb(0, 0, 0)";
// Draw a line
ctx.beginPath();
ctx.moveTo(sX, sY);
ctx.lineTo(eX, eY);
// Add stroke to line
cb_ctx.stroke();
cb_ctx.closePath();
}
It's quite easy really. The main things that caught me out were that a line will not appear on screen until you add a stroke to it. And, if you do not close and start a new path the stroke will get applied again to all lines since beginPath was called.
Capturing mouse and multitouch events
I found this article about touching and gesturing on the iPhone very helpful in getting this to work. I couldn't have figured out multi touch events without it. I wanted my example to work with a mouse as well as with touch and this took a little bit more work.
Here is how you can capture a mousemove and touchmove event:
// Set up handlers (this needs to be done in an onload event)
canvas.ontouchmove = moveEventFunction;
canvas.onmousemove = moveEventFunction;
function moveEventFunction(e) {
if (e.touches) {
// Touch Enabled (loop through all touches)
for (var i = 1; i <= e.touches.length; i++) {
var p = getCoords(e.touches[i - 1]); // Get info for finger i
// ... Do something with point touch p
}
}
else {
// Not touch enabled (get cursor position from single event)
var p = getCoords(e);
// ... Do something with cursor point p
}
return false; // Stop event bubbling up and doing other stuff (like pinch zoom or scroll)
}
Working out the cursor position relative to the element it clicked on
Getting this to work in all the browsers I tested made me very angry. It really shouldn't have been this hard. There are a few JavaScript libraries that make this easier, but it didn't seem worth it just to get this one simple thing to work.
Here is the code I finally came up with:
// Get the coordinates for a mouse or touch event
function getCoords(e) {
if (e.offsetX) {
// Works in Chrome / Safari (except on iPad/iPhone)
return { x: e.offsetX, y: e.offsetY };
}
else if (e.layerX) {
// Works in Firefox
return { x: e.layerX, y: e.layerY };
}
else {
// Works in Safari on iPad/iPhone
return { x: e.pageX - cb_canvas.offsetLeft, y: e.pageY - cb_canvas.offsetTop };
}
}
It still needs some more testing. I've tested it in the latest versions of Firefox, Safari (desktop/iPhone/iPad), Chrome. I've not tested it in IE 8 because this example already needs canvas which IE8 doesn't support. I've got high hopes for IE 9 though.
These little code snippets should give you some idea of how the whole thing works but you really need to refer to the full JavaScript Source to see how it works fully.

26 Comments
JP said
Excellent article, and relevant to my interests.
Thanks for spending the time to figure out the difficult bits so other people don't have to :D
Paul said
Works on my dell streak.
Randy said
Thanks.
Jean-Philippe said
Thanks for sharing this, Richard.
I felt into the same problems with coordinates determination.
Hope your article will help me...
Andrewiski said
add the explorercanvas.js to add canvas to internet explorer. You will need to chekc if ie to prevent script error on even e.touches but other then that it should work
Hal gumbert said
Has any one gotten this to work in IE with either ExplorerCanvas or Google Frame?
If so, could you share your code?
Richard said
@Hal I've not tried, but I'd be interested if anyone else has.
Lee said
Is it possible to remove the for loop so the touch works with only one finger?
Richard said
@Lee yes it is. Just remove the loop and access the first touch event in the array.
Paul Telford said
Hi and thanks for this superb article.
I'd like to capture the canvas contents and stuff into a database - can anyone point me in the right direction please?
Stephan van der Feest said
Hi Richard,
in your source code, you have an onTouchStop event handler.
This should be onTouchEnd instead.
Paul Bevis said
Hey, great article, got me out of a hole!
If you include jquery though you could maybe reduce getCoords method down to the following...
return { x: e.pageX - $("#cbook").offset().left, y: e.pageY - $("#cbook").offset().top };
That seemed to work for iPad/Safari/Firefox/Chrome for me...
Rajashekar Burugupally said
Excellent piece of code....working perfectly
setiri said
any way to emulate multitouch on a normal pc/laptop?
Sloppy said
After testing,it can not work on the Chrome(12.0.742.112)/Safari(5.0.5)/Firefox(5.0, our test browser is all for desktop. Can you tell me if we need to do some configuration to run the demo successfully?
Richard said
I've just tested in FF 5 on Windows and it still works fine. I've not done exhaustive testing on new browsers though since I completed the code sample when I wrote this post.
Abhishek said
There seems to be a problem with your coordinate offset detection, its offsetting the offset... Lol...
So the only point which is accurate is the 0,0 coordinate of the element, extreme top left... Everything else is offset by itself, so the coordinate 20,30 is input by the user, but the canvas draws at coordinate 40,60...
Please check your code...
vivid said
Hi, thanks for the great script. It's the best cross platform touch demonstration I have seen. One question tho, I am having trouble detecting a single 'touchup' event. I can see it fire from 2 fingers, when one is lifted, but not one finger when it's lifted. Is there a way to have the equivalent of a mouseup fired for a single touch?
Thanks,
vivid.
eaglejohn said
Is it possible to erase the drawing without reloading the page?
Richard said
@eaglejohn you can clear the canvas using:
ctx.clearRect(0,0,canvasWidth,canvasHeight)
eaglejohn said
@Richard Is it possible to use this with a <img> or <a> tag? So you can click a button which erases the canvas?
Richard said
@eaglejohn yes, just hook up a javascript onclick event to the <img> or <a> tag that executes the clearRect code.
bert2020 said
How would it be possible to change the brush? btw. really good instructions :)
Mike Croswell said
Thank you for a great example! Tested fine with Chrome 15.
venkata said
hi thanks for this amazing code snippet but i noticed one problem on my android tablet. if anyone have solution for the same please let me know
## Problem: When i try to draw on my android tablet the code is working but there is lot of delay in the drawing.
Can anyone please help me in this..
thanks
Jussi Kauhanen said
Hello!
Big thanks for the great example! I just got into PHP-coding and this example proved to be a big help on the iPhone / iPad -front.
Tested and working fine on: FF 10, IE 9, Chrome 16, and iPhone 3GS (newest software-patch).
I have cobbled together a small drawing program, where i use (on the mobile device side) your event handling code with an ugly hack to change the colors :)
Hope you don't mind and all the best from Finland!