Using Touch Events with the HTML5 Canvas
by Ben Centra
The HTML5 canvas element has been around for a while now, and it’s great for lots of things: drawing, games, user input, and more. It’s also fairly easy to use, and its API is similar to other drawing APIs out there. What’s not so easy is getting the canvas to work with both mouse and touch events, a requirement for mobile-friendly applications.
Partially as a joke I wanted to add an “e-signature” feature to a friend’s project. Since it was meant to be mobile-friendly, it was time to use the canvas with touch support! Here’s how it was done:
You can check out the final demo here and view the code here.
Canvas Setup
Canvas setup is easy enough:
Mouse Input
In order to draw a signature, I needed to capture user input on the canvas. Starting with mouse input, I handled these three mouse events:
mousedown
- to toggle drawing mode “on”mouseup
- to toggle drawing mode “off”mousemove
- to toggle mouse position, used in drawing
Drawing
Now that I knew the state of the mouse, I could start drawing to the canvas. To make it happen smoothly and efficiently, I took advantage of the browser method requestAnimationFrame
. I used a handy function that determines the appropriate method of getting the window’s animation frame or, failing that, falling back on a simple timeout loop:
To actually do the drawing, I set up a draw loop. I created a renderCanvas()
function for drawing the signature, connecting the previous and current mouse positions with a line (if drawing is enabled). By hooking into the window’s animation frame and running that function in a loop, I got a fully interactive signature field!
Touch Input
Since the project to which I was supposedly contributing was a modern web app, I needed to support smartphones and tablets. This meant adding touch controls to supplement the mouse controls. Keeping it simple, I only used one touch at a time (sorry, multitouch). For starters, I utilized three touch event counterparts to the mouse events from earlier:
touchstart
– to toggle drawing mode “on”touchend
– to toggle drawing mode “off”touchmove
– to track finger position, used in drawing
Because I wanted to play around with event dispatching, I used these touch events to trigger their mouse event counterparts and do the appropriate conversions (touch position to mouse position, etc).
Unfortunately, all was not well. An issue arose from a conflict with built-in browser gestures. When I moved my finger on the canvas, I wanted the page to stay still so I could draw. However, if my page had horizontal or vertical scrolling (and it did) the page would move along with my finger and making proper drawing impossible. After some frustration, I stumbled upon the solution: preventing scrolling on document.body if the target of a touch event is the canvas.
And viola, a mobile-friendly e-signature!
Bonus: Save the Canvas as a Data URL
Now that I had a signature (or doodle, or whatever) it would make sense to save it somewhere. The cheap and easy solution is to save the contents of the canvas directly as a 64-bit data URL, and here’s how:
You can then easily store the data URL in a database, set it to the src
attribute of an image element, etc.
Bonus: Clearing the Canvas
I don’t exactly know why (though I would love to know), but using canvas.clearRect()
or canvas.fillRect()
to clear or cover the canvas in white, respectively, didn’t actually clear the canvas. The next time I went to draw, the signature I thought I deleted would come back! The unintuitive solution is to reset the canvas’ width, which completely resets the canvas and its context.
Next Step: Scalable Canvas
The one thing my e-signature demo doesn’t do is scale the canvas based on the window size. As far as I know, the canvas needs to have a fixed height and width, which rules out media queries and CSS. I could hook into the window.resize event and do it through JavaScript, but that didn’t seem like a great solution. There was also the question: do I want scaling to cause a variety of image sizes since the canvas size will change, or am I just lazy and don’t want to do it? If you have any advice (aside from “don’t be lazy”), please let me know!
Resources:
Subscribe via RSS