[Important Update: After this article was published, Pointer Events were disabled in Firefox Nightly because of a stability bug. They will be re-enabled after this bug is fixed. You can still test Pointer Events in Firefox by setting dom.w3c_pointer_events.enabled
to “true” in about:config
.]
This past February Pointer Events became a W3C Recommendation. In the intervening time Microsoft Open Tech and Mozilla have been working together to implement the specification. As consumers continue to expand the range of devices that are used to explore the web with different input mechanisms such as touch, pen or mouse, it is important to provide a unified API that developers can use within their applications. In this effort we have just reached a major milestone: Pointer events are now available in Firefox Nightly. We are very excited about this effort which represents a great deal of cooperation across several browser vendors in an effort to produce a high quality industry standard API with growing support.
Be sure to download Firefox Nightly and give it a try and give us your feedback on the implementation either using the dev-platform mailing list or the mozilla.dev.platform group. If you have feedback on the specification please send those to public-pointer-events@w3.org.
The intent of this specification is to expand the open web to support a variety of input mechanisms beyond the mouse, while maintaining compatibility with most web-based content, which is built around mouse events. The API is designed to create one solution that will handle a variety of input devices, with a focus on pointing devices (mouse, pens, and touch). The pointer is defined in the spec as a hardware-agnostic device that can target a specific set of screen coordinates. Pointer events are intentionally similar to the current set of events associated with mouse events.
In the current Nightly build, pointer events for mouse input are now supported. Additionally, if you’re using Windows, once you’ve set two preferences, touch events can be enabled now. The first property, Async Pan & Zoom (APZ) is enabled by setting the layers.async-pan-zoom.enabled
Firefox configuration preference to true. The dom.w3c_touch_events.enabled
should also be enabled by setting this value to 1 in your preferences.
This post covers some of the basic features of the new API.
Using the Pointer API
Before getting started with the Pointer API, it’s important to test whether your current browser supports the API. This can be done with code similar to this example:
if (window.PointerEvent) {
.....
}else{
// use mouse events
}
The Pointer API provides support for pointerdown, pointerup, pointercancel, pointermove, pointerover, pointerout, gotpointercapture,
and lostpointercapture
events. Most of these should be familiar to you if you have coded event handling for mouse input before. For example, if you need a web app to move an image around a canvas when touched or clicked on, you can use the following code:
function DragImage() {
var imageGrabbed = false;
var ctx;
var cnv;
var myImage;
var x = 0;
var y = 0;
var rect;
this.imgMoveEvent = function(evt) {
if (imageGrabbed) {
ctx.clearRect(0, 0, cnv.width, cnv.height);
x = evt.clientX - rect.left;
y = evt.clientY - rect.top;
ctx.drawImage(myImage, x, y, 30, 30);
}
}
this.imgDownEvent = function(evt) {
//Could use canvas hit regions
var xcl = evt.clientX - rect.left;
var ycl = evt.clientY - rect.top;
if (xcl > x && xcl < x + 30 && ycl > y && ycl < y + 30) {
imageGrabbed = true;
}
}
this.imgUpEvent = function(evt) {
imageGrabbed = false;
}
this.initDragExample = function() {
if (window.PointerEvent) {
cnv = document.getElementById("myCanvas");
ctx = cnv.getContext('2d');
rect = cnv.getBoundingClientRect();
x = 0;
y = 0;
myImage = new Image();
myImage.onload = function() {
ctx.drawImage(myImage, 0, 0, 30, 30);
};
myImage.src = 'images/ff.jpg';
cnv.addEventListener("pointermove", this.imgMoveEvent, false);
cnv.addEventListener("pointerdown", this.imgDownEvent, false);
cnv.addEventListener("pointerup", this.imgUpEvent, false);
}
}
}
PointerCapture
events are used when there’s the possibility that a pointer device could leave the region of an existing element while tracking the event. For example, suppose you’re using a slider and your finger slips off the actual element –you’ll want to continue to track the pointer movements. You can set PointerCapture
by using code similar to this:
var myElement = document.getElementById("myelement");
myelement.addEventListener("pointerdown", function(e) {
if (this.setPointerCapture) {
//specify the id of the point to capture
this.setPointerCapture(e.pointerId);
}
}, false);
This code guarantees that you still get pointermove
events, even if you leave the region of myelement
. If you do not set the PointerCapture
, the pointer move events will not be called for the containing element once your pointer leaves its area. You can also release the capture by calling releasePointerCapture
. The browser does this automatically when a pointerup
or pointercancel
event occurs.
The Pointer Event interface
The PointerEvent
interface extends the MouseEvent
interface and provides a few additional properties. These properties include pointerId, width, height, pressure, tiltX, tiltY, pointerType
and isPrimary
.
The pointerId
property provides a unique id for the pointer that initiates the event. The height
and width
properties provide respective values in CSS pixels for the contact geometry of the pointer. When the pointer happens to be a mouse, these values are set to 0. The pressure
property contains a floating point value from 0 to 1 to indicate the amount of pressure applied by the pointer, where 0 is the lowest and 1 is the highest. For pointers that do not support pressure, the value is set to 0.5.
The tiltY
property contains the angle value between the X-Z planes of the pointer and the screen and ranges between -90 and 90 degrees. This property is most useful when using a stylus pen for pointer operations. A value of 0 degrees would indicate the pointer touched the surface at an exact perpendicular angle with respect to the Y-axis. Likewise the tiltX
property contains the angle between the Y-Z planes.
The pointType
property contains the device type represented by the pointer. Currently this value will be set to mouse, touch, pen, unknown
or an empty string.
var myElement = document.getElementById("myelement");
myElement.addEventListener("pointerdown", function(e) {
switch(e.pointerType) {
case "mouse":
console.log("Mouse Pointer");
break;
case "pen":
console.log("Pen Pointer");
break;
case "touch":
console.log("Touch Pointer");
break;
default:
console.log("Unknown Pointer");
}
}, false);
The isPrimary
property is either true
or false
and indicates whether the pointer is the primary pointer. A primary pointer property is required when supporting multiple touch points to provide multi-touch input and gesture support. Currently this property will be set to true
for each specific pointer type (mouse, touch, pen) when the pointer first makes contact with an element that is tracking pointer events. If you are using one touch point and a mouse pointer simultaneously both will be set to true
. The isPrimary
property will be set to false
for an event if a different pointer is already active with the same pointerType
.
var myElement = document.getElementById("myelement");
myelement.addEventListener("pointerdown", function(e) {
if( e.pointerType == "touch" ){
if( e.isPrimary ){
//first touch
}else{
//handle multi-touch
}
}
}, false);
Handling multi-touch
As stated earlier, touch pointers are currently implemented only for Firefox Nightly running on Windows with layers.async-pan-zoom.enabled
and dom.w3c_touch_events.enabled
preferences enabled. You can check to see whether multi-touch is supported with the following code.
if( window.maxTouchPoints && window.maxTouchPoints > 1 ){
//supports multi-touch
}
Some browsers provide default functionality for certain touch interactions such as scrolling with a swipe gesture, or using a pinch gesture for zoom control. When these default actions are used, the events for the pointer will not be fired. To better support different applications, Firefox Nightly supports the CSS property touch-action
. This property can be set to auto, none, pan-x, pan-y
, and manipulation
. Setting this property to auto
will not change any default behaviors of the browser when using touch events. To disable all of the default behaviors and allow your content to handle all touch input using pointer events instead, you can set this value to none
. Setting this value to either pan-x
or pan-y
invokes all pointer events when not panning/scrolling in a given direction. For instance, pan-x
will invoke pointer event handlers when not panning/scrolling in the horizontal direction. When the property is set to manipulation
, pointer events are fired if panning/scrolling or manipulating the zoom are not occurring.
// Very Simplistic pinch detector with little error detection,
// using only x coordinates of a pointer event
// Currently active pointers
var myPointers = [];
var lastDif = -1;
function myPointerDown(evt) {
myPointers.push(evt);
this.setPointerCapture(evt.pointerId);
console.log("current pointers down = " + myPointers.length);
}
//remove touch point from array when touch is released
function myPointerUp(evt) {
// Remove pointer from array
for (var i = 0; i < myPointers.length; i++) {
if (myPointers[i].pointerId == evt.pointerId) {
myPointers.splice(i, 1);
break;
}
}
console.log("current pointers down = " + myPointers.length);
if (myPointers.length < 2) {
lastDif = -1;
}
}
//check for a pinch using only the first two touchpoints
function myPointerMove(evt) {
// Update pointer position.
for (var i = 0; i < myPointers.length; i++) { if (evt.pointerId = myPointers[i].pointerId) { myPointers[i] = evt; break; } } if (myPointers.length >= 2) {
// Detect pinch gesture.
var curDif = Math.abs(myPointers[0].clientX - myPointers[1].clientX);
if (lastDif > 0) {
if (curDif > lastDif) { console.log("Zoom in"); }
if (curDif < lastDif) { console.log("Zoom out"); }
}
lastDif = curDif;
}
}
You can test the example code here. For some great examples of the Pointer Events API in action, see Patrick H. Lauke’s collection of Touch and Pointer Events experiments on GitHub. Patrick is a member of the W3C Pointer Events Working Group, the W3C Touch Events Community Group, and Senior Accessibility Consultant for The Paciello Group.
Conclusion
In this post we covered some of the basics that are currently implemented in Firefox Nightly. To track the progress of this API, check out the Gecko Touch Wiki page. You can also follow along on the main feature bug and be sure to report any issues you find while testing the new Pointer API.
17 comments