Back

TechnologyJul 05, 2012

HTML5 Touch-Enabled Canvas

Stephen Morrow

introduction

Mobile web applications are becoming increasingly popular in our world, and we are always attempting to give them a more “native” feel. It was only recently that we gained the ability to handle “touch” events from a mobile device on a web-based application. This opens up a whole new door for creating that “native” feel in our web applications. If we combine this touch event handling functionality with HTML5’s new canvas element, we can actually capture the user’s movements and allow them to draw on this canvas element. Let’s dive into how we can use the HTML5 canvas element to capture the user’s touch events

canvas api

To understand how to capture touch events and translate them onto the canvas element, we must first understand how to use the canvas element. Let’s take a look at the built in canvas API. The actual canvas element itself only has 2 relevant methods.

  • String toDataUrl(String type);

  • Context getContext(String contextId);

The toDataUrl() will be useful for extracting the final image after the user has finished drawing on the canvas. The getContext() method returns the Context object that we will use to actually draw on the canvas. For our purposes, we are interested in the 2-dimensional context, so we will pass in “2d” as the argument to this method to obtain our context object. Now let’s look at some code so we can visualize this API.

The HTML

The JavaScript

var theCanvas = document.getElementById("theCanvas"); var drawingUtil = new DrawingUtil(theCanvas);

theCanvas.width = window.innerWidth; theCanvas.height = window.innerHeight;

You can see in our HTML, we have simply declared a canvas element and given it some fallback text for older browsers that do not support it. In the javascript, we get a reference to this canvas object we just created by using its id. We can then call that getContext(“2d”) method on it to obtain a reference to the context object we will use to actually draw on the canvas.

2d-context api

Now let’s talk about the context objects API and how we can use it to draw on the canvas. The context object has 5 methods and 4 attributes that are used for marking on the canvas element.

Methods

–      void moveTo(double x, double y);

–      void lineTo(double x, double y);

–      void beginPath();

–      void closePath();

–      void stroke();

Attributes

–      double lineWidth

–      String lineCap

–      String lineJoin

–      String strokeStyle

The beginPath() method will be the first method we call when starting our “drawing”. This method tells the context object we are about to give you something to draw. We can then use the moveTo() method to tell it where to start the drawing at. Now we can use lineTo() to draw a line on the context object from that starting point, to your desired destination. Once we have drawn all the lines we want, we then call the closePath() method to tell the context we are done. Finally, we call stroke() to tell the context to actually paint those lines we drew with some pixels. This is the point where it will use the 4 attributes listed above. The lineWidth is self-explanatory. The lineCap tells the context how the end of the lines should look. The options are butt, round, and square. You can see the difference in the three in the figure below.

The lineJoin attribute tells the context how to draw the connecting point between two lines. The three available options are miter, round, and bevel. You can see the effects of each in the illustration below.

The final property, strokeStyle, is simply the desired CSS color of the line stroke. This defaults to black. That is all you need to know to be able to take a canvas’s context and draw lines on it. There are plenty of other methods that are available to the canvas element that can make drawing certain shapes easier, like rectangles or circles, but lines are a good starting point.

drawing events

Since we want our user to be able to interact with the canvas, instead of just randomly executing javascript methods, we need to be able to capture the browser events that tell us the user is interacting with the canvas. There are two sets of relevant browser events we will look at, the desktop events and the touchscreen (mobile device) events.

Desktop

Touchscreen

Start Drawing

mousedown

touchstart

Draw Line

mousemove

touchmove

Stop Drawing

mouseup, mouseout

touchend

I have split each event up into its appropriate “drawing” related event. This will give us an idea of how we should be mapping each of our context api methods to these user-fired events for drawing. When the user puts the mouse down or starts touching the canvas, we want to prepare the context for the line they are about to draw. When the mouse moves or the touch moves, we want to actually draw a line from the starting point to the ending point of the event. And lastly, when the user removes their finger from the touch screen or releases the mouse button, we want to stop drawing on the canvas.

putting it all together

Now we have a basic understanding of the HTML5 canvas, it’s 2-dimensional context, and the relevant events for drawing on the canvas using the context. Let’s take all this knowledge and put it together in some Javascript. To make this a bit more modular, I am going to create a DrawingUtil class to house all our necessary drawing methods. You can see in the figure below that we are going to pass in the reference to our canvas element that we obtained earlier from its ID. We can then call our getContext() method to obtain a reference to the 2-dimensional context. The isDrawing flag will be used in our drawing methods to determine whether our user is in the process of drawing on the canvas or not.

function DrawingUtil(aCanvas) { var canvas = aCanvas; var context = canvas.getContext("2d"); var isDrawing = false; }

This will be the skeleton for our drawing class. Now let’s add the relevant drawing methods we discussed in the last section. To recap, there are three main user interactions / methods that we want to capture: start drawing, draw, and stop drawing. Let’s start with the “start drawing” method.

function start(event) { isDrawing = true; context.beginPath(); context.moveTo(getX(event),getY(event)); event.preventDefault(); }

This is a pretty simple method, but let’s go ahead and break it down. Since the user has started drawing on the canvas, we set our isDrawing flag to true. We then call the beginPath() method on the canvas’s context, which tells it we are about to draw something. The moveTo method on the context tells it where we want to start the drawing, and we must manually tell it where we want to start at. We obtain this information from the user event that was fired. We will cover the particulars of how to get this location soon. Finally, the event.preventDefault() method’s main responsibility is to prevent touch enabled browsers from capturing our touches and scrolling the users view.

The next method we need is the “draw” method. Again, this is a pretty straight-forward method.

function draw(event) { if(isDrawing) { context.lineTo(getX(event),getY(event)); context.stroke(); } event.preventDefault(); }

We simply check the “isDrawing” flag to make sure the user actually clicked the mouse, or put their finger down, and then draw a line on the canvas to that point and stroke the line for the user to see.

Finally, we have the method to “stop” drawing. At this point, no explanation should be required.

function stop(event) { if(isDrawing) { context.stroke(); context.closePath(); isDrawing = false; } event.preventDefault(); }

Revisiting the problem presented earlier, let’s take a look at how we can get the x and y coordinates of the users mouse or touch event. The actual event API for touch-enabled browsers vs desktop browsers is outside the scope of this post, but feel free to dive into the details. You just need to know that getting the x and y coordinates from the two types of events are different, and the following snippet shows how. This shows how to obtain the x coordinate from the user event. Obtaining the y coordinate should be trivial.

function getX(event) { if(event.type.contains("touch")) { return event.targetTouches[0].pageX; } else { return event.pageX; } }

We have now established everything required to actually draw on our canvas object. All that is left to do is tell the browser when to actually execute these methods. This is where we register each of the methods discussed in the previous section. For the sake of readability, I am registering the events in an init() method, and calling this method from the beginning of our constructor.

function init() { canvas.addEventListener("touchstart",start,false); canvas.addEventListener("touchmove",draw,false); canvas.addEventListener("touchend",stop,false); canvas.addEventListener("mousedown",start,false); canvas.addEventListener("mousemove",draw,false); canvas.addEventListener("mouseup",stop,false); canvas.addEventListener("mouseout",stop,false); }

To see a slightly embellished working copy of this code, visit http://canvas.sjmorrow.com on your mobile device. Feel free to view the source and see how easy it is to capture the user’s drawing events. You will notice that there are no device-specific optimizations / big-fixes in this code, so you may encounter a few glitches that could easily be worked out by attaching a debugger to your browser.

This technology opens up the opportunity to write a few apps in the cloud that normally would have required a native application, such as signature / initial capturing or sketch recognition style input. Tune in next time for an example of how to use this in a real business application to capture a user’s signature on a formal agreement.