feat(media): two-finger pinch zoom in the media viewer (#164)

Zoom on a phone was only the +/- buttons; the pinch people try first did
nothing. usePinchZoom tracks a two-finger gesture on the viewer's media area
and scales from the zoom it started at (clamped 1×–5×, snapping back to
exactly 1× when released near it, so one-finger swipe navigation re-arms).
The area gets touch-action: none so the browser doesn't zoom the page
instead. One-finger swipe and pan already ignore multi-touch.

Verified on an emulated Pixel 7 with CDP two-point touch: spread 80→200 px
gives 250 %, closing to 120 px gives 150 %, closing fully returns to 100 %,
and a one-finger swipe afterwards still moves 2/2 → 1/2.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPmy3tPq869XDW4njjVaKA
This commit is contained in:
Lotus CI
2026-09-24 21:33:13 -04:00
co-authored by Claude Opus 5.5
parent c1661e48fa
commit 39589b40f3
3 changed files with 115 additions and 2 deletions
+20
View File
@@ -0,0 +1,20 @@
import { test } from 'node:test';
import assert from 'node:assert/strict';
import { pinchZoom } from './usePinchZoom';
test('pinchZoom scales with finger distance', () => {
assert.equal(pinchZoom(100, 1, 200), 2);
assert.equal(pinchZoom(100, 2, 150), 3);
assert.equal(pinchZoom(200, 3, 100), 1.5);
});
test('pinchZoom clamps to 1×–5×', () => {
assert.equal(pinchZoom(100, 1, 1000), 5);
assert.equal(pinchZoom(100, 1, 20), 1);
});
test('pinchZoom snaps near 1× and ignores a zero start', () => {
assert.equal(pinchZoom(100, 1, 105), 1);
assert.equal(pinchZoom(100, 2, 52), 1);
assert.equal(pinchZoom(0, 1.7, 120), 1.7);
});