diff --git a/.changeset/weak-shoes-remember.md b/.changeset/weak-shoes-remember.md new file mode 100644 index 00000000..9c531a77 --- /dev/null +++ b/.changeset/weak-shoes-remember.md @@ -0,0 +1,5 @@ +--- +"@zoom-image/core": minor +--- + +Add support for preventing zoom on single touch ✨ diff --git a/packages/core/src/createZoomImageWheel.ts b/packages/core/src/createZoomImageWheel.ts index 95d9700a..ee597619 100644 --- a/packages/core/src/createZoomImageWheel.ts +++ b/packages/core/src/createZoomImageWheel.ts @@ -7,6 +7,7 @@ export type ZoomImageWheelOptions = { wheelZoomRatio?: number dblTapAnimationDuration?: number initialState?: Partial + shouldZoomOnSingleTouch?: () => boolean } /* The delta values are not consistent across browsers. @@ -37,6 +38,8 @@ const defaultInitialState: ZoomImageWheelState = { currentRotation: 0, } +const defaultShouldZoomOnSingleTouch = () => true + export function createZoomImageWheel(container: HTMLElement, options: ZoomImageWheelOptions = {}) { const sourceImgElement = getSourceImage(container) const finalOptions: Required = { @@ -44,6 +47,7 @@ export function createZoomImageWheel(container: HTMLElement, options: ZoomImageW wheelZoomRatio: options.wheelZoomRatio || 0.1, dblTapAnimationDuration: options.dblTapAnimationDuration || 300, initialState: { ...defaultInitialState, ...options.initialState }, + shouldZoomOnSingleTouch: options.shouldZoomOnSingleTouch || defaultShouldZoomOnSingleTouch, } const store = createStore(finalOptions.initialState as ZoomImageWheelState) @@ -204,14 +208,14 @@ export function createZoomImageWheel(container: HTMLElement, options: ZoomImageW function _handlePointerMove(event: PointerEvent) { event.preventDefault() const { clientX, clientY, pointerId } = event - for (const [cachedPointerid] of pointerMap.entries()) { - if (cachedPointerid === pointerId) { - pointerMap.set(cachedPointerid, { x: clientX, y: clientY }) + for (const [cachedPointerId] of pointerMap.entries()) { + if (cachedPointerId === pointerId) { + pointerMap.set(cachedPointerId, { x: clientX, y: clientY }) } } - if (pointerMap.size === 1) { - const { currentZoom, currentRotation } = store.getState() + const { currentZoom, currentRotation } = store.getState() + if (pointerMap.size === 1 && currentZoom !== 1) { const isDimensionSwitched = checkDimensionSwitched() const normalizedClientX = isDimensionSwitched ? clientY : clientX const normalizedClientY = isDimensionSwitched ? clientX : clientY @@ -256,7 +260,6 @@ export function createZoomImageWheel(container: HTMLElement, options: ZoomImageW function animateZoom(touchCoordinate: { x: number; y: number }) { // the `touchCoordinate` should be relative to the container - const currentState = store.getState() animationState.startTimestamp = null @@ -338,8 +341,9 @@ export function createZoomImageWheel(container: HTMLElement, options: ZoomImageW } function _handleTouchMove(event: TouchEvent) { - event.preventDefault() + if (finalOptions.shouldZoomOnSingleTouch()) event.preventDefault() if (event.touches.length === 2) { + event.preventDefault() const currentTwoPositions = [...event.touches].map((t) => ({ x: t.clientX, y: t.clientY })) as [ PointerPosition, PointerPosition, @@ -357,6 +361,7 @@ export function createZoomImageWheel(container: HTMLElement, options: ZoomImageW } function _handlePointerDown(event: PointerEvent) { + if (event.pointerType === "touch" && !finalOptions.shouldZoomOnSingleTouch()) return event.preventDefault() if (pointerMap.size === 2) { return diff --git a/packages/docs/src/api/createZoomImageWheel.md b/packages/docs/src/api/createZoomImageWheel.md index 1dcf8171..bcf65ff5 100644 --- a/packages/docs/src/api/createZoomImageWheel.md +++ b/packages/docs/src/api/createZoomImageWheel.md @@ -46,6 +46,9 @@ type ZoomImageWheelOptions = { // Partial or full initial state // useful for storing previous zoomed state and re-initialize it on load initialState?: Partial + + // Predicate function to determine if zoom should be triggered on a single touch action + shouldZoomOnSingleTouch?: () => boolean } function createZoomImageWheel( diff --git a/packages/docs/src/components/HomePageShow.vue b/packages/docs/src/components/HomePageShow.vue index 2e78c835..b8c6994e 100644 --- a/packages/docs/src/components/HomePageShow.vue +++ b/packages/docs/src/components/HomePageShow.vue @@ -114,7 +114,9 @@ watch( } if (zoomType.value === "wheel") { - createZoomImageWheel(imageWheelContainerRef.value as HTMLDivElement) + createZoomImageWheel(imageWheelContainerRef.value as HTMLDivElement, { + shouldZoomOnSingleTouch: () => false, + }) } if (zoomType.value === "move") { diff --git a/size.json b/size.json index 5efa1e9c..722406b1 100644 --- a/size.json +++ b/size.json @@ -1,6 +1,6 @@ { "@zoom-image/core": { - "createZoomImageWheel": "2.1 KB", + "createZoomImageWheel": "2.17 KB", "createZoomImageHover": "1.28 KB", "createZoomImageMove": "1 KB", "createZoomImageClick": "943 B", @@ -9,45 +9,45 @@ "makeCalculatePercentage": "153 B" }, "@zoom-image/react": { - "useZoomImageWheel": "2.38 KB", + "useZoomImageWheel": "2.45 KB", "useZoomImageHover": "1.56 KB", "useZoomImageMove": "1.26 KB", "useZoomImageClick": "1.18 KB" }, "@zoom-image/preact": { - "useZoomImageWheel": "3.27 KB", + "useZoomImageWheel": "3.33 KB", "useZoomImageHover": "2.46 KB", "useZoomImageMove": "2.19 KB", "useZoomImageClick": "2.11 KB" }, "@zoom-image/qwik": { - "useZoomImageWheel": "2.52 KB", - "useZoomImageHover": "1.68 KB", - "useZoomImageMove": "1.38 KB", - "useZoomImageClick": "1.3 KB" + "useZoomImageWheel": "2.58 KB", + "useZoomImageHover": "1.67 KB", + "useZoomImageMove": "1.37 KB", + "useZoomImageClick": "1.29 KB" }, "@zoom-image/solid": { - "useZoomImageWheel": "3.56 KB", + "useZoomImageWheel": "3.62 KB", "useZoomImageHover": "2.75 KB", "useZoomImageMove": "2.48 KB", "useZoomImageClick": "2.4 KB" }, "@zoom-image/svelte": { - "useZoomImageWheel": "2.57 KB", + "useZoomImageWheel": "2.64 KB", "useZoomImageHover": "1.75 KB", "useZoomImageMove": "1.48 KB", "useZoomImageClick": "1.39 KB" }, "@zoom-image/vue": { - "useZoomImageWheel": "2.36 KB", + "useZoomImageWheel": "2.42 KB", "useZoomImageHover": "1.54 KB", "useZoomImageMove": "1.25 KB", "useZoomImageClick": "1.17 KB" }, "@zoom-image/angular": { - "ZoomImageClickService": "3.97 KB", - "ZoomImageHoverService": "3.97 KB", - "ZoomImageMoveService": "3.97 KB", - "ZoomImageWheelService": "3.97 KB" + "ZoomImageClickService": "4.04 KB", + "ZoomImageHoverService": "4.04 KB", + "ZoomImageMoveService": "4.04 KB", + "ZoomImageWheelService": "4.04 KB" } }