feat(stammbaum): touch/mouse/wheel pan & pinch zoom gestures (#692)

Add a panZoomGestures action: one-finger/left-button drag pans, two-finger
pinch and Ctrl+wheel zoom around the centroid, plain wheel pans. Pan is
edge-clamped via clampPan (no infinite scroll), a real drag suppresses the
trailing node click, and inertia decays after release unless prefers-reduced-
motion. Canvas container switches from native scroll to overflow-hidden.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Marcel
2026-05-29 16:45:18 +02:00
parent da1984b916
commit c8931071ba
6 changed files with 335 additions and 1 deletions

View File

@@ -113,6 +113,20 @@ export function zoomAtPoint(
};
}
/**
* Clamp the pan offset so the canvas cannot be dragged off the edge (US-PAN-001
* AC4 — no infinite scroll). The pannable range on each axis is half the
* difference between the base frame and the (smaller) zoomed viewBox; when the
* whole tree fits (z ≤ 1) the range collapses to zero, so the view stays centred.
*/
export function clampPan(state: PanZoomState, baseW: number, baseH: number): PanZoomState {
const clampAxis = (pan: number, base: number) => {
const limit = Math.max(0, (base - base / state.z) / 2);
return Math.min(limit, Math.max(-limit, pan)) || 0; // normalise -0 → 0
};
return { x: clampAxis(state.x, baseW), y: clampAxis(state.y, baseH), z: state.z };
}
/**
* Pan so a node sits at the viewBox centre (US-PAN-005). Because the viewBox
* centre is `baseCentre + pan` independent of zoom, centring is a pure pan: