Skip to content

Commit e2b0df4

Browse files
authored
feat: Support for simultaneous pinching and zooming (#475)
- added pinch logic for touch events in ZoomPanPinch class
1 parent 8dacc27 commit e2b0df4

2 files changed

Lines changed: 60 additions & 6 deletions

File tree

src/core/instance.core.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,9 @@ export class ZoomPanPinch {
5454

5555
public mounted = true;
5656

57+
public pinchLastCenterX: number | null = null;
58+
public pinchLastCenterY: number | null = null;
59+
5760
public transformState: ReactZoomPanPinchState;
5861
public setup: LibrarySetup;
5962
public observer?: ResizeObserver;
@@ -180,7 +183,9 @@ export class ZoomPanPinch {
180183
const currentHeight = contentComponent.offsetHeight;
181184

182185
if (currentWidth > 0 || currentHeight > 0) {
183-
this.onInitCallbacks.forEach((callback) => callback(getContext(this)));
186+
this.onInitCallbacks.forEach((callback) =>
187+
callback(getContext(this)),
188+
);
184189
this.setCenter();
185190

186191
this.observer?.disconnect();

src/core/pinch/pinch.logic.ts

Lines changed: 54 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,28 @@ import {
77
calculateTouchMidPoint,
88
getTouchDistance,
99
} from "./pinch.utils";
10-
import { handleCalculateBounds } from "../bounds/bounds.utils";
10+
import {
11+
getMouseBoundedPosition,
12+
handleCalculateBounds,
13+
} from "../bounds/bounds.utils";
1114
import { handleCalculateZoomPositions } from "../zoom/zoom.utils";
15+
import { getPaddingValue } from "../pan/panning.utils";
16+
17+
const getTouchCenter = (event: TouchEvent) => {
18+
let totalX = 0;
19+
let totalY = 0;
20+
// Sum up the positions of all touches
21+
for (let i = 0; i < 2; i++) {
22+
totalX += event.touches[i].clientX;
23+
totalY += event.touches[i].clientY;
24+
}
25+
26+
// Calculate the average position
27+
const x = totalX / 2;
28+
const y = totalY / 2;
29+
30+
return { x, y };
31+
};
1232

1333
export const handlePinchStart = (
1434
contextInstance: ReactZoomPanPinchContext,
@@ -21,16 +41,21 @@ export const handlePinchStart = (
2141
contextInstance.pinchStartScale = contextInstance.transformState.scale;
2242
contextInstance.isPanning = false;
2343

44+
const center = getTouchCenter(event);
45+
contextInstance.pinchLastCenterX = center.x;
46+
contextInstance.pinchLastCenterY = center.y;
47+
2448
handleCancelAnimation(contextInstance);
2549
};
2650

2751
export const handlePinchZoom = (
2852
contextInstance: ReactZoomPanPinchContext,
2953
event: TouchEvent,
3054
): void => {
31-
const { contentComponent, pinchStartDistance } = contextInstance;
55+
const { contentComponent, pinchStartDistance, wrapperComponent } =
56+
contextInstance;
3257
const { scale } = contextInstance.transformState;
33-
const { limitToBounds, centerZoomedOut, zoomAnimation } =
58+
const { limitToBounds, centerZoomedOut, zoomAnimation, alignmentAnimation } =
3459
contextInstance.setup;
3560
const { disabled, size } = zoomAnimation;
3661

@@ -45,7 +70,15 @@ export const handlePinchZoom = (
4570
const currentDistance = getTouchDistance(event);
4671
const newScale = calculatePinchZoom(contextInstance, currentDistance);
4772

48-
if (newScale === scale) return;
73+
const center = getTouchCenter(event);
74+
// pan should be scale invariant.
75+
const panX = center.x - (contextInstance.pinchLastCenterX || 0);
76+
const panY = center.y - (contextInstance.pinchLastCenterY || 0);
77+
78+
if (newScale === scale && 0 == panX && 0 == panY) return;
79+
80+
contextInstance.pinchLastCenterX = center.x;
81+
contextInstance.pinchLastCenterY = center.y;
4982

5083
const bounds = handleCalculateBounds(contextInstance, newScale);
5184

@@ -64,7 +97,23 @@ export const handlePinchZoom = (
6497
contextInstance.pinchMidpoint = midPoint;
6598
contextInstance.lastDistance = currentDistance;
6699

67-
contextInstance.setTransformState(newScale, x, y);
100+
const { sizeX, sizeY } = alignmentAnimation;
101+
const paddingValueX = getPaddingValue(contextInstance, sizeX);
102+
const paddingValueY = getPaddingValue(contextInstance, sizeY);
103+
104+
const newPositionX = x + panX;
105+
const newPositionY = y + panY;
106+
const { x: finalX, y: finalY } = getMouseBoundedPosition(
107+
newPositionX,
108+
newPositionY,
109+
bounds,
110+
limitToBounds,
111+
paddingValueX,
112+
paddingValueY,
113+
wrapperComponent,
114+
);
115+
116+
contextInstance.setTransformState(newScale, finalX, finalY);
68117
};
69118

70119
export const handlePinchStop = (

0 commit comments

Comments
 (0)