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:
@@ -6,6 +6,7 @@ import {
|
||||
screenDeltaToSvg,
|
||||
zoomAtPoint,
|
||||
recentreOn,
|
||||
clampPan,
|
||||
DEFAULT_VIEW,
|
||||
DEFAULT_ZOOM,
|
||||
LEGIBLE_ZOOM,
|
||||
@@ -145,3 +146,23 @@ describe('recentreOn', () => {
|
||||
expect(recentreOn(node, base, { x: 0, y: 0, z: 0.4 }, false).z).toBe(0.4);
|
||||
});
|
||||
});
|
||||
|
||||
describe('clampPan', () => {
|
||||
// Base frame is 1000 x 800.
|
||||
it('forbids panning when the whole tree fits (z <= 1)', () => {
|
||||
expect(clampPan({ x: 200, y: -100, z: 1 }, 1000, 800)).toEqual({ x: 0, y: 0, z: 1 });
|
||||
expect(clampPan({ x: 50, y: 50, z: 0.5 }, 1000, 800)).toEqual({ x: 0, y: 0, z: 0.5 });
|
||||
});
|
||||
|
||||
it('allows panning up to the edge when zoomed in (no infinite scroll)', () => {
|
||||
// At z=2 the viewBox is 500 wide → limit = (1000 - 500) / 2 = 250.
|
||||
expect(clampPan({ x: 1000, y: 0, z: 2 }, 1000, 800).x).toBe(250);
|
||||
expect(clampPan({ x: -1000, y: 0, z: 2 }, 1000, 800).x).toBe(-250);
|
||||
// Vertical limit at z=2: (800 - 400) / 2 = 200.
|
||||
expect(clampPan({ x: 0, y: 999, z: 2 }, 1000, 800).y).toBe(200);
|
||||
});
|
||||
|
||||
it('leaves an in-range pan untouched', () => {
|
||||
expect(clampPan({ x: 100, y: -50, z: 2 }, 1000, 800)).toEqual({ x: 100, y: -50, z: 2 });
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user