Input · 10
Pointer position
指针坐标 · pointerTurn a pointer into local pixels and coordinates relative to the element centre.
A tilting card needs to know which side the pointer occupies. Raw screen pixels make the same relative position produce a different tilt after a resize. Convert local position to −1…1 and damp the angle over time. Track press separately so touch and mouse get the same feedback.
Formula
nx = 2·(clientX − left)/width − 1 ny = 2·(clientY − top)/height − 1 angleX ← damp(angleX, −ny·maxAngle, λ, dt)
Parameters
| This page | Mature libraries | |
|---|---|---|
| nx, ny | −1…1 | —Normalize by the element, not the window. |
| λ | 10 /s | —Events update the target; the frame loop smooths the display. |
Doing it well
- Read the element bounds in each event and convert
clientX/Yto local coordinates.www-motion/rewrite/liquid-type/app.js:4 - Treat press and position as separate signals: the border can react instantly while tilt follows smoothly.
- Use Pointer Events and pointer capture to keep a touch gesture coherent; clear press on cancel too.
In our cases
68 studies use pointer position or press. liquid-type converts mouse coordinates to stage-local coordinates for glyph motion; many galleries pass press state to drag arbitration.
Used in 68 cases
01Momentum Slider02Drag Gallery03Formation04Cardwall05Riffle06Revolver07Rotunda08Iris09Lens Bloom10Frieze11Aperture12Folio13Cinema Reel14Stream Showcase15Hardback16Picasso Ecommerce17Assembly18Fathom
+50 more
19Emulsion20Portfolio Photographer21Reverie22Boutique23Nova24Alive25Shape Trail26Membrane27Liquid Type28Filament29Pixels30Cipher31Console32Cutout33Crema34Aster35Spring36Benchmark37Amour38Fluid Gallery40Atelier41Desk 9542Isle43Pantheon44Atlas45Aurora46Rooster Rush47Overworld48Moments49Burst50Dimension51Morph52Meow53Relay54Lumen55Serene56Fleur57Atrium58Skyward59Helix60Crate61Prism62Flashlight63Viewfinder64Sand65Arrange66Tunnel67Reel68Type69Loupe
Reference implementation · basics.js
/**
* Live pointer state relative to an element: pixels, plus -1..1 centred coordinates that
* survive resizes. Reads happen in events; consumers read the object in their frame loop.
*/
export function trackPointer(el) {
const s = { x: 0, y: 0, nx: 0, ny: 0, inside: false, down: false, type: 'mouse' };
const set = e => {
const r = el.getBoundingClientRect();
s.x = e.clientX - r.left; s.y = e.clientY - r.top;
s.nx = (s.x / r.width) * 2 - 1; s.ny = (s.y / r.height) * 2 - 1; s.type = e.pointerType;
};
const on = (t, f) => el.addEventListener(t, f);
on('pointerenter', e => { s.inside = true; set(e); });
on('pointermove', set);
on('pointerleave', () => { s.inside = false; });
on('pointerdown', e => { s.down = true; set(e); });
on('pointerup', () => { s.down = false; });
on('pointercancel', () => { s.down = false; s.inside = false; });
return s;
}