Drawing by touching using JavaScript


An updated version of this post and demo is available here: http://www.kinderas.com/technology/2014/3/13/a-drawing-application


The web is no longer exclusive for desktop and laptop computers. With the introduction of the iPhone and the iPad Apple changed how we interact with the web. In the wake of Apples success with iOS devices we see the emergence of a slew of "handheld" devices. They are all different, some are small, others bigger and more powerful, however most of them utilize touch as a means of input. In this article I take a look at how we can create a simple touch enabled drawing application using only JavaScript and a tinsy-winsy bit of HTML 5.

First, you can try the finished app (with comments) and download the source code from here.

The first thing we need to do is to make sure that the user visiting our web-app is on a device which can understand touch events. This is pretty straight forward. We accomplish this by asking the «window» DOM element if is knows about one of the touch events.
if('ontouchstart' in window == false){
   alert('Sorry, you need a touch enabled device to use this app');
   return;
}
If this does not stop our script, we know that the current device supports the touch events we need. The next step is to prevent the screen itself from scrolling when you drag your finger across it. We need to do this because touch events «bubbles» in JavaScript. This means that all the parent elements will get a chance to handle the touch event after we have handled it in our function. So after we have handled the «touchmove» event in our canvas element it will bubble right up to the window element where the browser will try to scroll the window. PS: Sometimes you might want this behavior, for example when you're scrolling a page. For our demo, we don't need it. To disable page scrolling we listen for the «touchmove» event on the document element, then we flag the event as handled using «event.preventDefault()».

Now to the construction part. We need to create a canvas element.
var canvas = document.createElement('canvas');
canvas.width  = window.innerWidth;
canvas.height = window.innerHeight;
document.body.appendChild(canvas);
We now have a «canvas» element which is the same size as our entire page. Next up we need to create a context in which to draw for the canvas. A context is kinda like a page within a drawing pad. The context can receive JavaScript drawing commands as we will see later on.
ctx  = canvas.getContext('2d');
ctx.strokeStyle = "rgba(255,0,0,1)";
ctx.lineWidth   = 5;
ctx.lineCap     = 'round';
The context is now set up using the color red with a 5 pixel wide stroke and rounded ends. You can change these values as you like of course.

In order for our fingers to be able to produce wonderful line drawings, we need to tell our application to listen for touch events. We'll need three of them. The «touchstart» event is where we set our starting position for the drawing operation. This event fires when a finger is added to the screen. The «touchmove» is where we draw the lines, this event fires when we move a finger on the screen. The «touchcancel» is where we handle exceptions, like what happens if you receive a call in the middle of your art creation extravaganza. This event fires whenever it needs to.
canvas.addEventListener("touchstart",touchstartHandler,false);
canvas.addEventListener("touchmove", touchmoveHandler,false);
canvas.addEventListener("touchcancel", touchcancelHandler,false);

Now we'll need to handle those events as well, here is where the real fun begins! Let's have a look a the «touchstart» handler first.
function touchstartHandler(event)
{
   ctx.moveTo(event.touches[0].pageX, event.touches[0].pageY);
}
It's just one line, but do take notice in the «touches» object contained within the event. This is an array (well, kind of) of touches. The reason for this is that most humans have more than one finger, so the «touches» object could actually contain several values. We only need one, so we refer to the first (0) element in the «touches» object. Now that we have found the first finger we get it's location on the screen by asking for the «pageX/Y» values. We then continue to move the canvas context pointer to the coordinates for this finger using the «moveTo» method. This happens every time we place a finger on the screen. But, only for the first finger.
function touchmoveHandler(event)
{
    ctx.lineTo(event.touches[0].pageX, event.touches[0].pageY);
    ctx.stroke();
}
In the «thouchmove» handler we do the actual work of telling the canvas context to draw a line from where it's last location was to where the finger is now. The first time this is called the line is drawn from where we placed the finger on to the screen, set in the «touchstart» handler. After that, the canvas context will update it's starting position to the location of the last drawing operation. Like the «moveTo» command in «touchstart» the «lineTo» will update the coordinate position, but it will also issue a drawing command which is rendered to the screen when we call the «stroke» method.

That's it. Try it out on your iPad, iPhone or any other touch device which understands the new HTML 5 APIs by clicking this magical link.

Recommended further reading:

Quicktip: Flipping an image with JavaScript


Let's say you wanted to flip an image in you web-app horizontally or vertically. By using a tiny bit of JavaScript and CSS 3 this is really easy.

//Get the image
var img = document.getElementById('myimage');
//Flip it horizontally
img.style.webkitTransform = 'scaleX(-1)';


And you're done! Note that this will only work in webkit browsers. The equivalent for Mozilla browsers would be «img.style.MozTransform». To flip the image vertically you could use: «scaleY(-1)». And to flip both horizontally and vertically at the same time: «scale(-1,-1)».

[edit 17.03.2011]
You can of course do this in other browsers besides Gecko or WebKit based browsers, as pointed out in the comments. Safari will actually support both the «webkit» and the «Moz» prefix, but this will most likely go away soon. So what you'll need to use is feature detection. This will still not work in ALL browsers, but the usable ones will most likely support it.
if(img.style.webkitTransform){
  img.style.webkitTransform = 'scaleX(-1)';
}else if(img.style.MozTransform){
  img.style.MozTransform = 'scaleX(-1)';
}else if(img.style.OTransform){
   img.style.OTransform = 'scaleX(-1)';
}else if(img.style.msTransform){
   img.style.msTransform = 'scaleX(-1)';
}else{
   img.style.transform = 'scaleX(-1)';
}