Pinch zoom gestures
Adding gestures to an application can significantly improve the user experience. There are many types of gestures, from the simple single-touch swipe gesture to the more complex multi-touch twist gesture, where the touch points (aka pointers) move in different directions.
This example shows how to detect the pinch/zoom gesture, which uses pointer events
to detect whether the user moves two pointers closer or farther apart from each other.
A live version of this application is available on Github. The source code is available on Github; pull requests and bug reports are welcome.
Example
In this example, you use the pointer events
to simultaneously detect two pointing devices of any type, including fingers, mice, and pens. The pinch in (zoom out ) gesture, which moves the two pointers toward each other, changes the target element's background color to lightblue
. The pinch out (zoom in) gesture, which moves the two pointers away from each other, changes the target element's background color to pink
.
Define touch target
The application uses <div>
to define the pointers' target areas.
<style> div { margin: 0em; padding: 2em; } #target { background: white; border: 1px solid black; } </style>
Global state
Supporting a two-pointer gesture requires preserving a pointer's event state during various event phases. This application uses two global variables to cache the event state.
// Global vars to cache event state var evCache = new Array(); var prevDiff = -1;
Register event handlers
Event handlers are registered for the following pointer events: pointerdown
, pointermove
and pointerup
. The handler for pointerup
is used for the pointercancel
, pointerout
and pointerleave
events since these four events have the same semantics in this application.
function init() { // Install event handlers for the pointer target var el=document.getElementById("target"); el.onpointerdown = pointerdown_handler; el.onpointermove = pointermove_handler; // Use same handler for pointer{up,cancel,out,leave} events since // the semantics for these events - in this app - are the same. el.onpointerup = pointerup_handler; el.onpointercancel = pointerup_handler; el.onpointerout = pointerup_handler; el.onpointerleave = pointerup_handler; }
Pointer down
The pointerdown
event is fired when a pointer (mouse, pen/stylus or touch point on a touchscreen) makes contact with the contact surface. In this application, the event's state must be cached in case this down event is part of a two-pointer pinch/zoom gesture.
function pointerdown_handler(ev) { // The pointerdown event signals the start of a touch interaction. // This event is cached to support 2-finger gestures evCache.push(ev); log("pointerDown", ev); }
Pointer move
The pointermove
event handler detects if a user is invoking a two-pointer pinch/zoom gesture. If two pointers are down, and the distance between the pointers is increasing (signifying a pinch out or zoom in), the element's background color is changed to pink
, and if the distance between the pointers is decreasing (a pinch in or zoom out), the background color is changed to lightblue
. In a more sophisticated application, pinch in or pinch out determination could be used to apply application-specific semantics.
When this event is processed, the target's border is set to dashed
to provide a clear visual indication the element has received a move event.
function pointermove_handler(ev) { // This function implements a 2-pointer horizontal pinch/zoom gesture. // // If the distance between the two pointers has increased (zoom in), // the taget element's background is changed to "pink" and if the // distance is decreasing (zoom out), the color is changed to "lightblue". // // This function sets the target element's border to "dashed" to visually // indicate the pointer's target received a move event. log("pointerMove", ev); ev.target.style.border = "dashed"; // Find this event in the cache and update its record with this event for (var i = 0; i < evCache.length; i++) { if (ev.pointerId == evCache[i].pointerId) { evCache[i] = ev; break; } } // If two pointers are down, check for pinch gestures if (evCache.length == 2) { // Calculate the distance between the two pointers var curDiff = Math.abs(evCache[0].clientX - evCache[1].clientX); if (prevDiff > 0) { if (curDiff > prevDiff) { // The distance between the two pointers has increased log("Pinch moving OUT -> Zoom in", ev); ev.target.style.background = "pink"; } if (curDiff < prevDiff) { // The distance between the two pointers has decreased log("Pinch moving IN -> Zoom out",ev); ev.target.style.background = "lightblue"; } } // Cache the distance for the next move event prevDiff = curDiff; } }
Pointer up
The pointerup
event is fired when a pointer is raised from the contact surface. When this occurs, the event is removed from the event cache and the target element's background color and border are restored to their original values.
In this application, this handler is also used for pointercancel
, pointerleave
and pointerout
events.
function pointerup_handler(ev) { log(ev.type, ev); // Remove this pointer from the cache and reset the target's // background and border remove_event(ev); ev.target.style.background = "white"; ev.target.style.border = "1px solid black"; // If the number of pointers down is less than two then reset diff tracker if (evCache.length < 2) prevDiff = -1; } }
Application UI
The application uses a <div>
element for the touch area and provides buttons to enable logging and to clear the log.
To prevent the browser's default touch behavior from overriding this application's pointer handling, the touch-action
property is applied to the <body>
element.
<body onload="init();" style="touch-action:none"> <div id="target">Touch and Hold with 2 pointers, then pinch in or out.<br/> The background color will change to pink if the pinch is opening (Zoom In) or changes to lightblue if the pinch is closing (Zoom out).</div> <!-- UI for logging/debugging --> <button id="log" onclick="enableLog(event);">Start/Stop event logging</button> <button id="clearlog" onclick="clearLog(event);">Clear the log</button> <p></p> <output></output> </body>
Miscellaneous functions
These functions support the application but aren't directly involved in the event flow.
Cache management
This function helps manage the global event caches evCache
.
function remove_event(ev) { // Remove this event from the target's cache for (var i = 0; i < evCache.length; i++) { if (evCache[i].pointerId == ev.pointerId) { evCache.splice(i, 1); break; } } }
Event logging
These functions are used to send event activity to the application's window (to support debugging and learning about the event flow).
// Log events flag var logEvents = false; // Logging/debugging functions function enableLog(ev) { logEvents = logEvents ? false : true; } function log(prefix, ev) { if (!logEvents) return; var o = document.getElementsByTagName('output')[0]; var s = prefix + ": pointerID = " + ev.pointerId + " ; pointerType = " + ev.pointerType + " ; isPrimary = " + ev.isPrimary; o.innerHTML += s + " "; } function clearLog(event) { var o = document.getElementsByTagName('output')[0]; o.innerHTML = ""; }
See also
- Pointer Events now in Firefox Nightly; Mozilla Hacks; by Matt Brubeck and Jason Weathersby; 2015-Aug-04
- jQuery Pointer Events Polyfill
- Gestures; Google Design Patterns
License
© 2016 Mozilla Contributors
Licensed under the Creative Commons Attribution-ShareAlike License v2.5 or later.
https://developer.mozilla.org/en-us/docs/web/api/pointer_events/pinch_zoom_gestures