Sunday, September 9, 2012

jQuery Mobile, HTML5 Canvas, & Touch-based Drawing

There is a PhoneGap version of this tutorial at:




One of the reasons that I like jQuery Mobile (JQM) is that it is a framework and not a straight-jacket. Anything not provided by jQuery or jQuery Mobile can  simply be created regular using HTML. Which brings me to the HTML5 canvas object. It is allows for the dynamic creation of drawings. It seems like a natural fit to combine it with the touch ability that most mobile devices have built-in. 

Unfortunately JQM doesn't have any built in support for the canvas object. But this isn't a problem. Since we can use our knowledge of HTML5 to added it. In this demo I will use some information from two prior tutorials which you may want to read before proceeding:




The basic framework of the application is a slightly modified form of my jQuery Mobile Skeleton. The only real difference is that I am now wrapping nearly all JavaScript code in an anonymous/immediate function to minimize my pollution of the global space.

The Canvas Object

<canvas id="myCanvas" width="200" height="100"></canvas>

The HTML5 canvas object has two important attributes, width and height. Normally these are set in the <canvas> tag and left alone. This would make our drawing application fairly boring. We would be forced to choose a size which would be less than ideal on nearly every platform we ran our web app on. If it looked good on a phone, it would be way too small on a tablet. If it looked right on a tablet, phones would be screwed. Luckily it isn't too difficult for us to determine the size of the content area and then size our canvas accordingly.

The Dimensions function determines the size of the first of the window object then subtracts heights of either a header, a footer, both or neither. Note: I am  using window object and not the document object. The two are different. The window refers to the viewport, while the document refers to the HTML document. It is very easy to have a document which exceeds the size of the viewport, hence why documents sometimes have scrollbars.

// size the content area
RocknCoder.Dimensions = (function () {
  var get = function () {
    var isFirstPass = false,
        isIPhone = (/iphone/gi).test(navigator.appVersion),
width = $(window).width(),
height = $(window).height() + (isIPhone ?  60 : 0),
hHeight = $('header').outerHeight() || 0,
fHeight = $('footer').outerHeight() || 0;
    return {
      width: width,
      height: height - hHeight - fHeight
    };
  };
  return {
    get: get
  };
}());

Once we have the size of the content area we use it to dynamically updated the width and height attributes of the canvas object. Luckily, jQuery has all the tools we need to change the attributes of any HTML element. Below, we just set the width and height by sending a object literal to attr method of the canvas selector.

reSizeCanvas = function () {
  var dims = RocknCoder.Dimensions.get();
  $canvas.attr({
    width: dims.width - 4,
    height: dims.height - 4
  });
  return dims;
},

Note: I have cached the jQuery selector for the canvas object ($canvas). This is an important performance boost for any jQuery app. It takes quite a bit of time to scan the DOM for any element. It is always smart to save the results to avoid a future hit.

Touch Events

The final bit of code which needs explanation are the event handlers for touchstart and touchmove. This is core of our drawing program. When we receive a touchstart event we begin a new line. When receive a touchmove, we draw a new line segment from the previous point to the current one. That's it nothing super difficult going on here.

// start tracking the touches, move the pen to the beginning of a line
$canvas.bind('touchstart', function (event) {
  var xy = extractXY(event.originalEvent.touches[0]);
  ctx.moveTo(xy.x, xy.y);
  event.preventDefault();
  return false;
});
// draw a line from the last point to this one
$canvas.bind('touchmove', function (event) {
  var xy = extractXY(event.originalEvent.touches[0]);
  ctx.lineTo(xy.x, xy.y);
  ctx.stroke();
  event.preventDefault();
  return false;
});

Go To The Demo
Next Steps

Our drawing program is very simple. There is no support for non-touch devices such as mice. It only supports the drawing of thin yellow lines. There is no support for more colors, thicker lines, easing or anything else. It wouldn't be too tough to add more features to this program and I hope that someone will.

The complete source for this tutorial is on GitHub, as always. A working demo is now on my demo site, just tap on TouchPaint.

4 comments:

  1. I tried using on instead of bind for the touch events, but that didn't work. Would other changes have to be made?

    Thanks for the tutorials! It helps me as a new jQuery Mobile person.

    ReplyDelete
  2. Hi,

    Thanks a lot for this example ! About the touch events, I have replaced touchstart and touchmove by vmousedown and vmousemove (http://jquerymobile.com/demos/1.2.0/docs/api/events.html). It's not recommended to use event.originalEvent, so I use ctx.moveTo(event.pageX, event.pageY); . It works fine on Android 4.2 (Nexus 7). extractXY function can be remove from the code.

    Bye !

    ReplyDelete
  3. This comment has been removed by a blog administrator.

    ReplyDelete
  4. Mobile App Development Services in Dwarka are increasingly helping businesses in empowering workers, boosting productivity, and engage customers in a better way.

    ReplyDelete