Skip to content

Commit 1db1ff1

Browse files
🖱️
1 parent e1ad3fc commit 1db1ff1

5 files changed

Lines changed: 77 additions & 14 deletions

File tree

‎.changeset/olive-moons-shave.md‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---
2+
"@zoom-image/core": minor
3+
---
4+
5+
Expose a `maxWheelDelta` option on `createZoomImageWheel` to configure how far a single wheel event zooms
6+
7+
The wheel delta was previously clamped to a hardcoded `0.5`. That value is now the default of the new `maxWheelDelta`
8+
option, so consumers can slow zooming down or speed it up to suit their input devices.

‎packages/core/src/createZoomImageWheel.ts‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { clamp, computeZoomGesture, disableScroll, enableScroll, makeMaybeCallFu
44

55
export type ZoomImageWheelOptions = {
66
maxZoom?: number
7+
maxWheelDelta?: number
78
wheelZoomRatio?: number
89
dblTapAnimationDuration?: number
910
initialState?: Partial<ZoomImageWheelStateUpdate>
@@ -15,7 +16,7 @@ export type ZoomImageWheelOptions = {
1516
* We need to normalize them to a consistent value.
1617
* https://developer.mozilla.org/en-US/docs/Web/API/WheelEvent/deltaY
1718
*/
18-
const ZOOM_DELTA = 0.5
19+
const DEFAULT_MAX_WHEEL_DELTA = 0.5
1920

2021
export type ZoomImageWheelState = {
2122
currentRotation: number
@@ -45,6 +46,7 @@ const defaultShouldZoomOnSingleTouch = () => true
4546
export function createZoomImageWheel(container: HTMLElement, options: ZoomImageWheelOptions = {}) {
4647
const finalOptions: Required<ZoomImageWheelOptions> = {
4748
maxZoom: options.maxZoom || 4,
49+
maxWheelDelta: options.maxWheelDelta || DEFAULT_MAX_WHEEL_DELTA,
4850
wheelZoomRatio: options.wheelZoomRatio || 0.1,
4951
dblTapAnimationDuration: options.dblTapAnimationDuration || 300,
5052
initialState: { ...defaultInitialState, ...options.initialState },
@@ -218,7 +220,7 @@ export function createZoomImageWheel(container: HTMLElement, options: ZoomImageW
218220
return
219221
}
220222

221-
const delta = -clamp(event.deltaY, -ZOOM_DELTA, ZOOM_DELTA)
223+
const delta = -clamp(event.deltaY, -finalOptions.maxWheelDelta, finalOptions.maxWheelDelta)
222224
processZoomWheel({ delta, x: event.clientX, y: event.clientY })
223225
updateZoom()
224226

‎packages/core/test/createZoomImageWheel.test.ts‎

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,32 @@
11
import { render, screen } from "@testing-library/vue"
22
import userEvent from "@testing-library/user-event"
33
import TestImageZoomWheel from "./components/TestImageZoomWheel.vue"
4-
import { it } from "vitest"
4+
import { afterEach, it } from "vitest"
5+
import { createZoomImageWheel } from "../src"
6+
import type { ZoomImageWheelOptions } from "../src"
7+
8+
let cleanupZoom: (() => void) | null = null
9+
10+
function renderZoomWheel(options: ZoomImageWheelOptions = {}) {
11+
const container = document.createElement("div")
12+
container.appendChild(document.createElement("img"))
13+
document.body.appendChild(container)
14+
15+
const zoomImageWheel = createZoomImageWheel(container, options)
16+
cleanupZoom = zoomImageWheel.cleanup
17+
18+
function scroll(deltaY: number) {
19+
container.dispatchEvent(new WheelEvent("wheel", { deltaY, cancelable: true }))
20+
}
21+
22+
return { scroll, getZoom: () => zoomImageWheel.getState().currentZoom }
23+
}
24+
25+
afterEach(() => {
26+
cleanupZoom?.()
27+
cleanupZoom = null
28+
document.body.innerHTML = ""
29+
})
530

631
describe("createZoomImageWheel function", () => {
732
it("should display zoomed image on hover source image", async () => {
@@ -25,4 +50,28 @@ describe("createZoomImageWheel function", () => {
2550
const child = zoomTarget.children[0]
2651
expect(child.tagName).toBe("DIV")
2752
})
53+
54+
it("should zoom by the default wheel delta when none is given", () => {
55+
const { scroll, getZoom } = renderZoomWheel()
56+
57+
scroll(-100)
58+
59+
expect(getZoom()).toBe(1.05)
60+
})
61+
62+
it("should zoom by a smaller step when maxWheelDelta is lowered", () => {
63+
const { scroll, getZoom } = renderZoomWheel({ maxWheelDelta: 0.05 })
64+
65+
scroll(-100)
66+
67+
expect(getZoom()).toBe(1.005)
68+
})
69+
70+
it("should zoom by a larger step when maxWheelDelta is raised", () => {
71+
const { scroll, getZoom } = renderZoomWheel({ maxWheelDelta: 1 })
72+
73+
scroll(-100)
74+
75+
expect(getZoom()).toBe(1.1)
76+
})
2877
})

‎packages/docs/src/api/createZoomImageWheel.md‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@ type ZoomImageWheelOptions = {
3838
// Maximum zoom scale, default is 4
3939
maxZoom?: number
4040

41+
// Upper bound applied to the delta of a wheel event, default is 0.5
42+
// Lower it to zoom slower on each scroll, raise it to zoom faster
43+
maxWheelDelta?: number
44+
4145
// Zoom ratio when scrolling, default is 0.1
4246
wheelZoomRatio?: number
4347

‎size.json‎

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"@zoom-image/core": {
3-
"createZoomImageWheel": "2.16 KB",
3+
"createZoomImageWheel": "2.18 KB",
44
"createZoomImageHover": "1.28 KB",
55
"createZoomImageMove": "1.01 KB",
66
"createZoomImageClick": "943 B",
@@ -9,45 +9,45 @@
99
"makeCalculatePercentage": "153 B"
1010
},
1111
"@zoom-image/react": {
12-
"useZoomImageWheel": "2.45 KB",
12+
"useZoomImageWheel": "2.47 KB",
1313
"useZoomImageHover": "1.56 KB",
1414
"useZoomImageMove": "1.28 KB",
1515
"useZoomImageClick": "1.18 KB"
1616
},
1717
"@zoom-image/preact": {
18-
"useZoomImageWheel": "3.32 KB",
18+
"useZoomImageWheel": "3.34 KB",
1919
"useZoomImageHover": "2.46 KB",
2020
"useZoomImageMove": "2.19 KB",
2121
"useZoomImageClick": "2.1 KB"
2222
},
2323
"@zoom-image/qwik": {
24-
"useZoomImageWheel": "2.58 KB",
24+
"useZoomImageWheel": "2.61 KB",
2525
"useZoomImageHover": "1.67 KB",
2626
"useZoomImageMove": "1.38 KB",
2727
"useZoomImageClick": "1.29 KB"
2828
},
2929
"@zoom-image/solid": {
30-
"useZoomImageWheel": "3.65 KB",
30+
"useZoomImageWheel": "3.68 KB",
3131
"useZoomImageHover": "2.76 KB",
3232
"useZoomImageMove": "2.52 KB",
3333
"useZoomImageClick": "2.43 KB"
3434
},
3535
"@zoom-image/svelte": {
36-
"useZoomImageWheel": "2.9 KB",
36+
"useZoomImageWheel": "2.92 KB",
3737
"useZoomImageHover": "2.02 KB",
3838
"useZoomImageMove": "1.75 KB",
3939
"useZoomImageClick": "1.66 KB"
4040
},
4141
"@zoom-image/vue": {
42-
"useZoomImageWheel": "2.42 KB",
42+
"useZoomImageWheel": "2.44 KB",
4343
"useZoomImageHover": "1.54 KB",
4444
"useZoomImageMove": "1.26 KB",
4545
"useZoomImageClick": "1.17 KB"
4646
},
4747
"@zoom-image/angular": {
48-
"ZoomImageClickService": "4.12 KB",
49-
"ZoomImageHoverService": "4.12 KB",
50-
"ZoomImageMoveService": "4.12 KB",
51-
"ZoomImageWheelService": "4.12 KB"
48+
"ZoomImageClickService": "4.15 KB",
49+
"ZoomImageWheelService": "4.15 KB",
50+
"ZoomImageHoverService": "4.15 KB",
51+
"ZoomImageMoveService": "4.15 KB"
5252
}
5353
}

0 commit comments

Comments
 (0)