Skip to content

Commit a75e859

Browse files
committed
feat(zoom): add experimental pinch zoom implementation (fix #380)
1 parent 4a5b631 commit a75e859

File tree

4 files changed

+38
-21
lines changed

4 files changed

+38
-21
lines changed

docs/plugins/zoom.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ Zoom plugin adds the following `Lightbox` properties.
116116
&nbsp;&nbsp;keyboardMoveDistance?: number;<br />
117117
&nbsp;&nbsp;wheelZoomDistanceFactor?: number;<br />
118118
&nbsp;&nbsp;pinchZoomDistanceFactor?: number;<br />
119+
&nbsp;&nbsp;pinchZoomV4?: boolean;<br />
119120
&nbsp;&nbsp;scrollToZoom?: boolean;<br />
120121
&#125;
121122
</td>
@@ -131,7 +132,8 @@ Zoom plugin adds the following `Lightbox` properties.
131132
<li>`doubleClickMaxStops` - maximum number of zoom-in stops via double-click or double-tap</li>
132133
<li>`keyboardMoveDistance` - keyboard move distance</li>
133134
<li>`wheelZoomDistanceFactor` - wheel zoom distance factor</li>
134-
<li>`pinchZoomDistanceFactor` - pinch zoom distance factor</li>
135+
<li>`pinchZoomDistanceFactor` - pinch zoom distance factor (deprecated)</li>
136+
<li>`pinchZoomV4` - if `true`, enables the experimental pinch zoom implementation slated for v4</li>
135137
<li>`scrollToZoom` - if `true`, enables image zoom via scroll gestures for mouse and trackpad users</li>
136138
</ul>
137139
<p>

src/plugins/zoom/hooks/useZoomSensors.ts

Lines changed: 31 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import { useZoomState } from "./useZoomState.js";
1414
import { usePointerEvents } from "../../../hooks/usePointerEvents.js";
1515

1616
function distance(pointerA: React.MouseEvent, pointerB: React.MouseEvent) {
17-
return ((pointerA.clientX - pointerB.clientX) ** 2 + (pointerA.clientY - pointerB.clientY) ** 2) ** 0.5;
17+
return Math.hypot(pointerA.clientX - pointerB.clientX, pointerA.clientY - pointerB.clientY);
1818
}
1919

2020
function scaleZoom(value: number, delta: number, factor = 100, clamp = 2) {
@@ -34,7 +34,11 @@ export function useZoomSensors(
3434
) {
3535
const activePointers = React.useRef<React.PointerEvent[]>([]);
3636
const lastPointerDown = React.useRef(0);
37-
const pinchZoomDistance = React.useRef<number>(undefined);
37+
const pinchZoom = React.useRef<{
38+
previousDistance: number;
39+
initialDistance: number;
40+
initialZoom: number;
41+
}>(undefined);
3842

3943
const { globalIndex } = useLightboxState();
4044
const { getOwnerWindow } = useDocumentContext();
@@ -48,6 +52,7 @@ export function useZoomSensors(
4852
doubleClickDelay,
4953
doubleClickMaxStops,
5054
pinchZoomDistanceFactor,
55+
pinchZoomV4,
5156
} = useZoomProps();
5257

5358
const translateCoordinates = React.useCallback(
@@ -175,7 +180,12 @@ export function useZoomSensors(
175180
replacePointer(event);
176181

177182
if (pointers.length === 2) {
178-
pinchZoomDistance.current = distance(pointers[0], pointers[1]);
183+
const initialDistance = distance(pointers[0], pointers[1]);
184+
pinchZoom.current = {
185+
previousDistance: initialDistance,
186+
initialDistance: Math.max(initialDistance, 1), // prevent potential division by zero
187+
initialZoom: zoom,
188+
};
179189
}
180190
});
181191

@@ -184,25 +194,27 @@ export function useZoomSensors(
184194

185195
const activePointer = pointers.find((p) => p.pointerId === event.pointerId);
186196

187-
if (pointers.length === 2 && pinchZoomDistance.current) {
197+
if (pointers.length === 2 && pinchZoom.current) {
188198
event.stopPropagation();
189199

190200
replacePointer(event);
191201

192202
const currentDistance = distance(pointers[0], pointers[1]);
193-
const delta = currentDistance - pinchZoomDistance.current;
194-
195-
if (Math.abs(delta) > 0) {
196-
changeZoom(
197-
scaleZoom(zoom, delta, pinchZoomDistanceFactor),
198-
true,
199-
...pointers
200-
.map((x) => translateCoordinates(x))
201-
.reduce((acc, coordinate) => coordinate.map((x, i) => acc[i] + x / 2)),
202-
);
203-
204-
pinchZoomDistance.current = currentDistance;
205-
}
203+
204+
// TODO v4: remove `pinchZoomV4` flag and make this the default behavior
205+
const targetZoom = pinchZoomV4
206+
? (pinchZoom.current.initialZoom / pinchZoom.current.initialDistance) * currentDistance
207+
: scaleZoom(zoom, currentDistance - pinchZoom.current.previousDistance, pinchZoomDistanceFactor);
208+
209+
changeZoom(
210+
targetZoom,
211+
true,
212+
...pointers
213+
.map((x) => translateCoordinates(x))
214+
.reduce((acc, coordinate) => coordinate.map((x, i) => acc[i] + x / 2)),
215+
);
216+
217+
pinchZoom.current.previousDistance = currentDistance;
206218

207219
return;
208220
}
@@ -225,7 +237,7 @@ export function useZoomSensors(
225237
const pointers = activePointers.current;
226238

227239
if (pointers.length === 2 && pointers.find((p) => p.pointerId === event.pointerId)) {
228-
pinchZoomDistance.current = undefined;
240+
pinchZoom.current = undefined;
229241
}
230242

231243
clearPointer(event);
@@ -238,7 +250,7 @@ export function useZoomSensors(
238250
pointers.splice(0, pointers.length);
239251

240252
lastPointerDown.current = 0;
241-
pinchZoomDistance.current = undefined;
253+
pinchZoom.current = undefined;
242254
}, []);
243255

244256
usePointerEvents(subscribeSensors, onPointerDown, onPointerMove, onPointerUp, disabled);

src/plugins/zoom/index.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,10 @@ declare module "../../types.js" {
2525
keyboardMoveDistance?: number;
2626
/** wheel zoom distance factor */
2727
wheelZoomDistanceFactor?: number;
28-
/** pinch zoom distance factor */
28+
/** @deprecated - pinch zoom distance factor */
2929
pinchZoomDistanceFactor?: number;
30+
/** if `true`, enables the experimental pinch zoom implementation slated for v4 */
31+
pinchZoomV4?: boolean;
3032
/** if `true`, enables image zoom via scroll gestures for mouse and trackpad users */
3133
scrollToZoom?: boolean;
3234
};

src/plugins/zoom/props.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ export const defaultZoomProps = {
1010
keyboardMoveDistance: 50,
1111
wheelZoomDistanceFactor: 100,
1212
pinchZoomDistanceFactor: 100,
13+
pinchZoomV4: false,
1314
scrollToZoom: false,
1415
};
1516

0 commit comments

Comments
 (0)