Pointer Events Working Group update / TPAC 2023

W3C

Pointer Events Working Group update

changes and additions in Pointer Events Level 3

Patrick H. Lauke / TPAC 2023

What are Pointer Events

To better address devices with different input types, this specification defines a more abstract form of input, called a pointer.

A higher level event model based on mouse events, but also covering pen, touch, and other (future) pointing devices.

Latest stable recommendation: Pointer Events Level 2 (4 April 2019)

Work has since been ongoing towards Pointer Events Level 3 (Editor's draft August 2023)

Pointer Events Level 3

Beyond clarifications for undocumented cases/scenarios that came out of implementation experience and developer adoption, Level 3 includes a few new features:

pointerrawupdate

The problem

For performance reasons, user agents MAY delay the dispatch of pointermove events (as they already do for mousemove).

For very fast pointer movements, user agents will generally "coalesce" individual small movements into a single pointermove event.

While good for performance, this can be problematic for scenarios where authors want to accurately track high-frequency pointer movements – for instance, drawing applications.

Demo: basic drawing application using pointermove

pointerrawupdate

The new pointerrawupdate event aims help make these applications work smoother.

Compared to the pointermove event, user agents SHOULD dispatch pointerrawupdate as soon as possible.

Demo: basic drawing application using pointerrawupdate

pointerrawupdate

May have a performance impact – authors should keep code executed in response to pointerrawupdate to a minimum (e.g. just store coordinates)

Note: even though pointerrawupdate should fire as soon as possible, the user agent may still coalesce a few individual events/changes.

getCoalescedEvents()

The problem

For very fast pointer movements, user agents will generally "coalesce" individual small movements into a single
pointermove or pointerrawupdate event

Again, for certain applications, authors may want access to all the separate events that were coalesced by the user agent

getCoalescedEvents()

The new getCoalescedEvents() method gives authors access to all the raw position/property changes that were coalesced into an event

Best of both worlds – allows for increased granularity, without incurring additional performance penalties:

foo.addEventListener("pointermove", (e)=> {
    if (e.getCoalescedEvents) {
        for (let ec of e.getCoalescedEvents()) {
            // access the coalesced event properties
            // like clientX/clientY (more granular)
        }
    } else {
        // fallback: use the pointermove event's
        // properties instead
    }
});

Demo: basic drawing application using pointermove and getCoalescedEvents()

getPredictedEvents()

The problem

Even with the use of pointerrawupdate and getCoalescedEvents(), certain applications – such as drawing applications – may still exhibit perceived latency.

There will always be a gap, no matter how small, between an event being dispatched and the application reacting to it.

getPredictedEvents()

Some user agents have built-in algorithms which, after a series of confirmed pointer movements, can predict likely future movements.

The new getPredictedEvents() method gives authors access to these predicted events

These can be helpful in scenarios like drawing applications: draw ahead to predicted positions to reduce perceived latency (but discard these speculative/predicted points when the real points are received).

foo.addEventListener("pointermove", (e)=> {

    // regular processing of the event,
    // and/or any coalesced events

    if (e.getPredictedEvents) {
        for (let ep of e.getPredictedEvents()) {
            // do something with the predicted events,
            // such as speculatively drawing ahead
        }
    }
});

Demo: basic drawing application using pointermove and getPredictedEvents()

Out of scope

Both getCoalescedEvents() and getPredictedEvents() only define the methods/API to access coalesced and predicted events

The Pointer Events specification itself does not define how events are coalesced or predicted – this is left up to individual implementations (operating system / user agent dependent)

altitudeAngle / azimuthAngle

The problem

The original Pointer Events specification defined tiltX and tiltY properties to convey the orientation of a stylus

These properties are, admittedly, not very intuitive for developers

Illustrations visualising tiltX and tiltY, in the context of a tablet with a stylus

tiltX: The plane angle (in degrees, in the range of [-90,90]) between the Y-Z plane and the plane containing both the transducer (e.g. pen/stylus) axis and the Y axis …

tiltY: The plane angle … between the X-Z plane and the plane containing both the transducer … axis and the X axis …

altitudeAngle / azimuthAngle

Pointer Events Level 3 "borrows" the altitudeAngle and azimuthAngle properties from Touch Events
(introduced when Apple expanded Touch Events to support Pencil on iPad)

Illustrations visualising altitudeAngle and azimuthAngle, in the context of a tablet with a stylus

altitudeAngle: The altitude (in radians) of the transducer (e.g. pen/stylus), in the range [0,π/2] — where 0 is parallel to the surface (X-Y plane), and π/2 is perpendicular to the surface. …

azimuthAngle: The azimuth angle (in radians) of the transducer …, in the range [0, 2π] — where 0 represents a transducer whose cap is pointing in the direction of increasing X values (point to "3 o'clock" if looking straight down) on the X-Y plane, and the values progressively increase when going clockwise …

altitudeAngle / azimuthAngle

User agents MUST provide both the classic tiltX/tiltY and the new altitudeAngle/azimuthAngle properties

The specification includes algorithm for converting between the two sets

Demo: pen tracker (using WebGL)

Bonus:
click / auxclick / contextmenu

Redefining events in UI Events

The specification redefines click, auxclick, and contextmenu as Pointer Events

This change is already in the latest UI Events working draft

Possible new application: determine what type of input caused one of these events to be fired using the pointerType property

Demo: basic button with pointerType check

Thank you…

Currently working on last few blockers for Level 3 and Web Platform Tests

Hoping to go to Candidate Recommendation (CR) shortly

Creative Commons: Attribution Non-Commercial Share-Alike