diff options
| author | godosa <godosa@godosa.eu> | 2026-10-07 00:14:38 +0200 |
|---|---|---|
| committer | godosa <godosa@godosa.eu> | 2026-10-07 00:14:38 +0200 |
| commit | 3443c1c65e9f1753e1e656b35d08416c1fa298f2 (patch) | |
| tree | 4e43236f460145a4d75d1b4616dcb7aa6ef08f51 /viewer/js/gestures.js | |
| download | worldmap-viewer-3443c1c65e9f1753e1e656b35d08416c1fa298f2.tar.gz worldmap-viewer-3443c1c65e9f1753e1e656b35d08416c1fa298f2.zip | |
worldmap-viewer: initial public history
Diffstat (limited to 'viewer/js/gestures.js')
| -rw-r--r-- | viewer/js/gestures.js | 42 |
1 files changed, 42 insertions, 0 deletions
diff --git a/viewer/js/gestures.js b/viewer/js/gestures.js new file mode 100644 index 0000000..5e8c697 --- /dev/null +++ b/viewer/js/gestures.js @@ -0,0 +1,42 @@ +// gestures.js — two-finger touch: how a pair of fingers moved between two moments. scale > 1: spread (zoom in); +// rot: degrees the pair turned, clockwise on screen (y down) positive; mid / move: their midpoint and how it moved; +// tilt: px both fingers moved vertically together (0 when they went opposite ways, as in a pinch along y). +export function twoFinger(a0, b0, a1, b1) { + const d0 = Math.hypot(b0.x - a0.x, b0.y - a0.y), d1 = Math.hypot(b1.x - a1.x, b1.y - a1.y); + let rot = (Math.atan2(b1.y - a1.y, b1.x - a1.x) - Math.atan2(b0.y - a0.y, b0.x - a0.x)) * 180 / Math.PI; + rot = ((rot + 540) % 360) - 180; // the short way round + const mid = { x: (a1.x + b1.x) / 2, y: (a1.y + b1.y) / 2 }; + const move = { x: mid.x - (a0.x + b0.x) / 2, y: mid.y - (a0.y + b0.y) / 2 }; + const da = a1.y - a0.y, db = b1.y - b0.y; + const tilt = da * db > 0 ? (Math.abs(da) < Math.abs(db) ? da : db) : 0; // the smaller shared move + return { scale: d0 > 0 && d1 > 0 ? d1 / d0 : 1, rot, mid, move, tilt }; +} + +// Touch pointers by id, in element coordinates. Two at once = a gesture. Browsers send one move per finger, so a +// shared vertical move (tilt) is pieced together: each finger's vertical travel adds up until both went the same way. +export class Touches { + constructor() { this.pts = new Map(); this.dy = new Map(); } + get size() { return this.pts.size; } + down(id, at) { this.pts.set(id, at); this.dy.clear(); } + up(id) { this.pts.delete(id); this.dy.clear(); } + pair() { return [...this.pts.values()].slice(0, 2); } + // a pointer moved: the gesture since the last move (null unless two fingers are down) + move(id, at) { + if (!this.pts.has(id)) return null; + const before = this.pair(), was = this.pts.get(id); + this.pts.set(id, at); + if (this.pts.size < 2) return null; + const g = twoFinger(...before, ...this.pair()); + const ids = [...this.pts.keys()].slice(0, 2); + if (!ids.includes(id)) return { ...g, tilt: 0 }; + this.dy.set(id, (this.dy.get(id) ?? 0) + at.y - was.y); + const [a, b] = ids.map(k => this.dy.get(k) ?? 0); + let tilt = 0; + if (a * b > 0) { // both went the same way: the smaller part tilts + tilt = Math.abs(a) < Math.abs(b) ? a : b; + for (const k of ids) this.dy.set(k, this.dy.get(k) - tilt); + } else if (a * b < 0) this.dy.clear(); // opposite ways: a pinch, not a tilt + return { ...g, tilt }; + } + other(id) { for (const [k, v] of this.pts) if (k !== id) return { id: k, at: v }; return null; } +} |
