aboutsummaryrefslogtreecommitdiffziptar.gz
path: root/viewer/js/gestures.js
diff options
context:
space:
mode:
Diffstat (limited to 'viewer/js/gestures.js')
-rw-r--r--viewer/js/gestures.js42
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; }
+}