diff --git a/README.md b/README.md index 9815e6e..d012a21 100644 --- a/README.md +++ b/README.md @@ -264,6 +264,9 @@ Damit es nicht ruppig wirkt: Oberkante geht in die Höhenabfrage ein (`walkHeight` in `main.ts`, `treppeTritte` in `structures.ts`). Ob man hinaufkommt, entscheidet nur die Stufenhöhe (45 cm). Holz-, Lamellen- und Betonwände bleiben Hindernisse. +- In der **Übersicht** dreht Ziehen die Ansicht; gezoomt wird mit dem Mausrad + (auch mit `Strg`, wobei der Seitenzoom des Browsers unterdrückt wird) und am + Handy mit zwei Fingern. Abstand 9 bis 120 m. - Das **Gartentor** öffnet sich, wenn man darauf zugeht, und schliesst sich hinter einem wieder; `E` übersteuert das von Hand. Im Durchgang selbst schliesst es nie. diff --git a/src/controls/walker.ts b/src/controls/walker.ts index c101b36..68a8bfb 100644 --- a/src/controls/walker.ts +++ b/src/controls/walker.ts @@ -145,6 +145,7 @@ export class Walker { let last = { x: 0, y: 0 }; let start = { x: 0, y: 0, t: 0 }; dom.addEventListener('touchstart', (e) => { + if (!this.enabled) return; for (const t of Array.from(e.changedTouches)) { if (touchId === null && t.clientX > window.innerWidth * 0.35) { touchId = t.identifier; @@ -154,6 +155,7 @@ export class Walker { } }, { passive: true }); dom.addEventListener('touchmove', (e) => { + if (!this.enabled) { touchId = null; return; } for (const t of Array.from(e.changedTouches)) { if (t.identifier !== touchId) continue; this.lookDelta.x += (t.clientX - last.x) * 0.006; diff --git a/src/main.ts b/src/main.ts index 6c14c88..ffca5f7 100644 --- a/src/main.ts +++ b/src/main.ts @@ -336,27 +336,77 @@ async function main() { }, }; + const ABSTAND = { nah: 9, fern: 120 }; + const zoome = (faktor: number) => { + orbit.distance = Math.max(ABSTAND.nah, Math.min(ABSTAND.fern, orbit.distance * faktor)); + orbit.apply(); + }; + + // Ziehen dreht, zwei Finger zoomen. Beide Zeiger werden mitgefuehrt, damit + // beim Loslassen eines Fingers nahtlos weitergedreht wird. let dragging = false; let lastPointer = { x: 0, y: 0 }; + const zeiger = new Map(); + let kneifStart = 0; + let kneifAbstand = 0; + const kneifSpanne = () => { + const [a, b] = [...zeiger.values()]; + return Math.hypot(a.x - b.x, a.y - b.y); + }; + canvas.addEventListener('pointerdown', (e) => { if (!orbit.active) return; - dragging = true; - lastPointer = { x: e.clientX, y: e.clientY }; + zeiger.set(e.pointerId, { x: e.clientX, y: e.clientY }); canvas.setPointerCapture(e.pointerId); + if (zeiger.size === 2) { + dragging = false; + kneifAbstand = kneifSpanne(); + kneifStart = orbit.distance; + } else if (zeiger.size === 1) { + dragging = true; + lastPointer = { x: e.clientX, y: e.clientY }; + } }); canvas.addEventListener('pointermove', (e) => { - if (!orbit.active || !dragging) return; + if (!orbit.active || !zeiger.has(e.pointerId)) return; + zeiger.set(e.pointerId, { x: e.clientX, y: e.clientY }); + if (zeiger.size >= 2) { + // Finger auseinander = naeher heran + const jetzt = kneifSpanne(); + if (jetzt > 1 && kneifAbstand > 1) { + orbit.distance = Math.max(ABSTAND.nah, Math.min(ABSTAND.fern, kneifStart * (kneifAbstand / jetzt))); + orbit.apply(); + } + return; + } + if (!dragging) return; orbit.yaw -= (e.clientX - lastPointer.x) * 0.006; orbit.pitch = Math.max(0.12, Math.min(1.45, orbit.pitch + (e.clientY - lastPointer.y) * 0.004)); lastPointer = { x: e.clientX, y: e.clientY }; orbit.apply(); }); - canvas.addEventListener('pointerup', () => { dragging = false; }); + const zeigerWeg = (e: PointerEvent) => { + zeiger.delete(e.pointerId); + if (zeiger.size === 1) { + // Vom Zoomen zurueck aufs Drehen, ohne Sprung + const [rest] = [...zeiger.values()]; + lastPointer = { ...rest }; + dragging = true; + } else if (zeiger.size === 0) { + dragging = false; + } + }; + canvas.addEventListener('pointerup', zeigerWeg); + canvas.addEventListener('pointercancel', zeigerWeg); + + // Mausrad zoomt - auch mit Strg, wo der Browser sonst die Seite vergroessern + // wuerde. Trackpads melden feine Schritte, deshalb wird die Staerke aus dem + // Betrag abgeleitet statt nur aus der Richtung. canvas.addEventListener('wheel', (e) => { if (!orbit.active) return; e.preventDefault(); - orbit.distance = Math.max(14, Math.min(110, orbit.distance * (1 + Math.sign(e.deltaY) * 0.12))); - orbit.apply(); + const schritt = Math.max(-1, Math.min(1, e.deltaY / (e.deltaMode === 1 ? 3 : 120))); + zoome(1 + schritt * 0.14); }, { passive: false }); // -- Umschalten zwischen den Darstellungen -------------------------------- diff --git a/src/ui/hud.ts b/src/ui/hud.ts index 12e6922..84763dd 100644 --- a/src/ui/hud.ts +++ b/src/ui/hud.ts @@ -178,7 +178,8 @@ export function setupHud(opts: HudOptions): Hud {
  • Treppen, Stufen und Sitzmauern sind begehbar. Das Gartentor öffnet sich, wenn man darauf zugeht; E öffnet oder schliesst es von Hand.
  • Am Handy: auf den Boden tippen, links der Joystick, rechts wischen zum Umsehen
  • -
  • Übersicht: ziehen zum Drehen, Mausrad zum Zoomen
  • +
  • Übersicht: ziehen zum Drehen; zoomen mit dem Mausrad (auch mit Strg), + am Handy mit zwei Fingern auf- und zuziehen
  • Die Sonne steht für Abtwil SG astronomisch korrekt