Posted by: digdug2k | July 10, 2012

Detecting taps using touch events

We recently shipped our first release of Firefox for Android with full multi-touch touchevents support, and I’d like to keep hearing from people if they find bugs or sites that don’t work. I recently had to debug and fix (for a second time) a bug involving Google maps, touch events, and tap detection. I thought it might be worth blogging here as well for future authors looking at this problem. Plus, Maps code is super obfuscated which made it tough to find the problem (both times).

Sidenote to try and avoid questions: Our users also don’t get some other nice Google Maps features like pinch-zoom and nice looking buttons. We fully support multitouch touch events in Firefox for Android, and pinch zoom works (last I checked) if you change your UA to match the stock browser or iPhone. The CSS problems are mostly due to the use of -webkit-background-size without other vendor prefixes. If someone wants to port Stylish to Fennec, it’d be a start to a prettier Google Maps experience for many of our users!

The Maps devs at some point realized they had all this nice code written to handle mouse events for their setup, and wanted to reuse it as much as possible. Ideally, touchevents and mouseevents would be interchangable and things would just work, but this is the system we’re kinda stuck with now. So the Maps team built functions that take touch events, convert them to mouse events, and rebroadcast them:

var sendConvertedTouch = function (eventType, touchEvent) {
  if (1 != b.changedTouches.length) return k;
    var mouseevent = document.createEvent("MouseEvents"),
             touch = b.changedTouches[0];
    c.initMouseEvent(eventType, true, true, window, true,
                     touch.screenX, touch.screenY,
                     touch.clientX, touch.clientY,
                     l, l, l, l, 0, null);
    c.translated = true;
    touch.target.dispatchEvent(mousevent);
  }
}

I doubt this is crazy performant, but it works! I’m guessing there are similar hacks all around the webs. This makes sending mouseup/down/move events simple.

It takes a bit more work to actually fire clicks when the user taps. There’s numerous tools and utilities out there to help with these things. But in this case the team decided to do their own thing. A super simplified version of the result looks something like:

var dragging = false;
document.addEventListener("touchmove", function(event) {
  dragging = true;
}, false);

document.addEventListener("touchend", function(event) {
  if (!dragging)
    sendConvertedTouch("click", event);
  dragging = false;
}, false);

(in their case, dragging actually holds a count of the number of taps).

This fails in current builds of Fennec. Why? Well essentially we don’t do a whole lot of filtering on the input we send to content. The system sends us a touch event, we send it on to content (we do some event coalescing if our internal event handling/drawing is slow the entire system doesn’t get backed up, but that’s about it). But touches on a screen actually jitter quite a bit especially on hdpi devices. Even when it feels like your finger is completely stationary, you’re probably still sending tons of touchmove events to the pages you’re viewing. So even though your finger hasn’t moved far, we send a touchmove event and maps refuses to click.

In addition, Fennec actually supports some of the Level 2 draft for touch events, which includes things like force and radius (NOTE: This spec is still in development and is likely to change, so use those with caution). So if a user presses on a single spot on the screen and then continues pressing harder and harder, we fire touchmove events for all those force/radius changes even though the users finger is still at (approximately) the same place.

All of that means the above code doesn’t actually work for us unless we filter out at least this very first touchmove event and don’t fire it until the user has actually moved their finger a significant distance on the screen. For now, that’s what we’ve done to make maps work for our users.

The correct code should check for some minimum distance after the initial touchstart event. If you get a touchmove that’s further away than that, you’re dragging! Ideally you’d have access to the device dpi here and you could use units of mm or something, but I can’t find a webapi for device dpi (yet?). This post is already getting too long, so I’ll leave that as an exercise to the reader.

“tap” events with some sort of number indicating how many taps have occurred in approximately the same place would probably be a useful addition to the touch events spec. (I noticed the other day that we actually have an internal MozGestureTap event that isn’t implemented in Fennec and isn’t exposed to web content (yet)). For now the EASIEST way to get around some of these problems is to use something like zepto, jquerymobile, or pointer.js which abstract this and other useful “gestures” for you.

About these ads

Responses

  1. The next version of Stylish will add support for Firefox for Android.

  2. […] [Bug 771099] Unable double tap zoom on maps.google.com – blogged about this here […]


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Categories

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: