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 and check that state in the move listener. But 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 = new Vector4(
event.clientX,
canvas.height - 1 - event.clientY,
0,
1
);
}
function onPointerDown(event: PointerEvent) {
const mousePixel = new Vector4(
event.clientX,
canvas.height - 1 - event.clientY,
0,
1
);
}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.
This code constructs a Vector4. In a moment, we'll need a 4-component vector in order to locate the mouse in the scene, but we don't have a class for it yet. Make one by copying the Vector3 class. Add a w component and a getter. Adapt the other methods accordingly. We also need a way to transform a Vector4 by a matrix.
Add this method to your Matrix4 class.
In this and future code, we use the term mouse as shorthand for any pointing device, not just a mouse.