Pointer Events

How to 3D

Chapter 8: Interaction

Pointer Events

A keyboard is a blunt instrument. It has no location in the scene, so its events apply either to some previously focused object or to the entire scene. For more localized interaction, we use a pointing device like a mouse. The browser provides mousedown, mousemove, and mouseup events. These require a mouse; they won't be triggered by a stylus or a finger. To respond to inputs from any pointing device, we listen for pointerdown, pointermove, and pointerup events.

We register listeners for down and up events like this:

async function initialize() {
  // ...

  window.addEventListener('pointerdown', onPointerDown);
  window.addEventListener('pointerup', onPointerUp);
}

function onPointerDown(event: PointerEvent) {
}

function onPointerUp(event: PointerEvent) {
}
async function initialize() {
  // ...

  window.addEventListener('pointerdown', onPointerDown);
  window.addEventListener('pointerup', onPointerUp);
}

function onPointerDown(event: PointerEvent) {
}

function onPointerUp(event: PointerEvent) {
}

Move events are handled differently. The browser calls a move listener any time the pointer moves, not just when a button is held down. Usually we only care about active click-and-drag events, and we don't want to take action on passive movement. There are a couple of ways to filter out the passive movement events. We could use a global variable to record the button state. A cleaner method is to add the listener only after a down event and remove it after an up event:

function onPointerDown(event: PointerEvent) {
  window.addEventListener('pointermove', onPointerMove);
}

function onPointerUp(event: PointerEvent) {
  window.removeEventListener('pointermove', onPointerMove);
}

function onPointerMove(event: PointerEvent) {
}
function onPointerDown(event: PointerEvent) {
  window.addEventListener('pointermove', onPointerMove);
}

function onPointerUp(event: PointerEvent) {
  window.removeEventListener('pointermove', onPointerMove);
}

function onPointerMove(event: PointerEvent) {
}

When a pointer event occurs, we query its pixel coordinates like this:

function onPointerDown(event: PointerEvent) {
  const mousePixel = [
    event.clientX,
    canvas.height - event.clientY,
  ];
}
function onPointerDown(event: PointerEvent) {
  const mousePixel = [
    event.clientX,
    canvas.height - event.clientY,
  ];
}

Note that we take the complement of the y-coordinate with respect to the canvas height. This action corrects for the browser's and WebGL's mismatched coordinate systems. In the browser's pixel space, in which clientX and clientY are defined, the origin is at the top-left corner with the y-axis pointing down. In WebGL's pixel space, the origin is at the bottom-left with the y-axis pointing up.

In this and future code, we use the term mouse as shorthand for any pointing device, not just a mouse.

← Triggering AnimationsUntransforming the Mouse →