worldmap-viewer

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

master

raw · 3770 bytes

// touch.mjs — two-finger gestures on a phone: the map follows the fingers (twist turns it with them, a spread zooms
// in about them, both fingers up tilt the globe); flat maps pinch about the fingers.
export const viewport = { width: 390, height: 844 };
const helpers = `const a = window.mapViewer, c = document.querySelector('#map canvas'), r = c.getBoundingClientRect();
  const ev = (t, id, x, y) => c.dispatchEvent(new PointerEvent(t, { clientX: r.left + x, clientY: r.top + y, bubbles: true,
    pointerId: id, pointerType: 'touch', isPrimary: id === 11, button: 0, buttons: t === 'pointerup' ? 0 : 1 }));
  const gesture = (from, to, steps = 10) => {   // from/to: [[x, y], [x, y]] for fingers 11 and 12
    ev('pointerdown', 11, ...from[0]); ev('pointerdown', 12, ...from[1]);
    for (let s = 1; s <= steps; s++) for (const [k, id] of [[0, 11], [1, 12]])
      ev('pointermove', id, from[k][0] + (to[k][0] - from[k][0]) * s / steps, from[k][1] + (to[k][1] - from[k][1]) * s / steps);
    ev('pointerup', 11, ...to[0]); ev('pointerup', 12, ...to[1]);
  };
  const W = r.width, H = r.height, cx = W / 2, cy = H / 2;`;
export const steps = [
  { name: 'boots', wait: 5000, expect: v => v, js: `window.mapViewer.setView('globe'); return !!window.mapViewer.view.pose;` },
  { name: 'twist clockwise 30° (about the view centre): a ground point turns ~30° clockwise with the fingers', expect: v => Math.abs(v - 30) < 6,
    js: `${helpers}
      a.view.setPose({ ...a.view.pose, alt: undefined, pitch: 0, range: a.view.pose.range });
      const p = a.view.pick(cx + 60, cy, false); const s0 = a.view.toScreen(p.lat, p.lon);
      const R = 80, rad = d => d * Math.PI / 180, at = d => [cx + R * Math.cos(rad(d)), cy + R * Math.sin(rad(d))];
      gesture([at(180), at(0)], [at(210), at(30)]);
      const s1 = a.view.toScreen(p.lat, p.lon);
      return (Math.atan2(s1.y - cy, s1.x - cx) - Math.atan2(s0.y - cy, s0.x - cx)) * 180 / Math.PI;` },
  { name: 'spread to twice the distance: zoomed in ~2× (range halves)', expect: v => Math.abs(v - 0.5) < 0.08,
    js: `${helpers} const r0 = a.view.pose.range; gesture([[cx - 40, cy], [cx + 40, cy]], [[cx - 80, cy], [cx + 80, cy]]); return a.view.pose.range / r0;` },
  { name: 'both fingers up: the globe tilts toward the horizon', expect: v => v > 5,
    js: `${helpers} a.view.setPose({ ...a.view.pose, range: a.view.pose.range * 0.02, pitch: 0 }); const p0 = a.view.pose.pitch; gesture([[cx - 60, cy + 100], [cx + 60, cy + 100]], [[cx - 60, cy], [cx + 60, cy]]); return a.view.pose.pitch - p0;` },
  { name: 'one finger after a pinch keeps panning (no jump, no stuck drag)', expect: v => v.moved && !v.jump,
    js: `${helpers} a.view.setPose({ ...a.view.pose, pitch: 0 });
      const before = { ...a.view.pose };
      ev('pointerdown', 11, cx - 40, cy); ev('pointerdown', 12, cx + 40, cy); ev('pointermove', 12, cx + 45, cy); ev('pointerup', 12, cx + 45, cy);
      const mid = { ...a.view.pose };
      ev('pointermove', 11, cx - 40, cy + 30); ev('pointerup', 11, cx - 40, cy + 30);
      const after = a.view.pose;
      return { moved: Math.abs(after.lat - mid.lat) > 1e-4, jump: Math.abs(after.lat - mid.lat) > 20 };` },
  { name: 'flat map: spread zooms 2× about the fingers (the point between them stays put)', expect: v => Math.abs(v.z - 2) < 0.1 && v.drift < 3,
    js: `${helpers} a.setView('equal_earth'); a.view.setState({ lat: 0, lon: 0, zoom: 2 });
      const z0 = a.view.getState().zoom, mx = cx + 30, my = cy + 20, p = a.view.pick(mx, my, false);
      gesture([[mx - 40, my], [mx + 40, my]], [[mx - 80, my], [mx + 80, my]]);
      const s = a.view.toScreen(p.lat, p.lon), z = a.view.getState().zoom / z0; a.setView('globe');
      return { z, drift: Math.hypot(s.x - mx, s.y - my) };` },
];