worldmap-viewer

git clone https://git.godosa.eu/worldmap-viewer

master

raw · 2581 bytes

// 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; }
}