UNPKG

@zoom-image/core

Version:
1 lines 24.2 kB
{"version":3,"sources":["../src/createZoomImageWheel.ts"],"names":[],"mappings":"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","sourcesContent":["import { createStore } from \"@namnode/store\"\nimport type { PointerPosition } from \"./utils\"\nimport { clamp, computeZoomGesture, disableScroll, enableScroll, getSourceImage, makeMaybeCallFunction } from \"./utils\"\n\nexport type ZoomImageWheelOptions = {\n maxZoom?: number\n wheelZoomRatio?: number\n dblTapAnimationDuration?: number\n initialState?: Partial<ZoomImageWheelStateUpdate>\n shouldZoomOnSingleTouch?: () => boolean\n}\n\n/* The delta values are not consistent across browsers.\n * We need to normalize them to a consistent value.\n * https://developer.mozilla.org/en-US/docs/Web/API/WheelEvent/deltaY\n */\nconst ZOOM_DELTA = 0.5\n\nexport type ZoomImageWheelState = {\n currentRotation: number\n currentZoom: number\n enable: boolean\n currentPositionX: number\n currentPositionY: number\n}\n\nexport type ZoomImageWheelStateUpdate = Partial<{\n enable: boolean\n currentZoom: number\n currentRotation: number\n}>\n\nconst defaultInitialState: ZoomImageWheelState = {\n currentZoom: 1,\n enable: true,\n currentPositionX: 0,\n currentPositionY: 0,\n currentRotation: 0,\n}\n\nconst defaultShouldZoomOnSingleTouch = () => true\n\nexport function createZoomImageWheel(container: HTMLElement, options: ZoomImageWheelOptions = {}) {\n const sourceImgElement = getSourceImage(container)\n const finalOptions: Required<ZoomImageWheelOptions> = {\n maxZoom: options.maxZoom || 4,\n wheelZoomRatio: options.wheelZoomRatio || 0.1,\n dblTapAnimationDuration: options.dblTapAnimationDuration || 300,\n initialState: { ...defaultInitialState, ...options.initialState },\n shouldZoomOnSingleTouch: options.shouldZoomOnSingleTouch || defaultShouldZoomOnSingleTouch,\n }\n\n const store = createStore<ZoomImageWheelState>(finalOptions.initialState as ZoomImageWheelState)\n\n const checkDimensionSwitched = () => {\n return [90, 270].includes(store.getState().currentRotation % 360)\n }\n\n const calculatePositionX = (newPositionX: number, currentZoom: number) => {\n if (newPositionX > 0) return 0\n\n const width = container.clientWidth\n if (newPositionX + width * currentZoom < width) return -width * (currentZoom - 1)\n return newPositionX\n }\n\n const calculatePositionY = (newPositionY: number, currentZoom: number) => {\n if (newPositionY > 0) return 0\n\n const height = container.clientHeight\n if (newPositionY + height * currentZoom < height) return -height * (currentZoom - 1)\n return newPositionY\n }\n\n const updateStateOnNewZoom = (currentZoom: number) => {\n const zoomPointX = container.clientWidth / 2\n const zoomPointY = container.clientHeight / 2\n const isDimensionSwitched = checkDimensionSwitched()\n const currentState = store.getState()\n\n const zoomX = isDimensionSwitched ? currentState.currentPositionY : currentState.currentPositionX\n const zoomY = isDimensionSwitched ? currentState.currentPositionX : currentState.currentPositionY\n\n const zoomTargetX = (zoomPointX - zoomX) / currentState.currentZoom\n const zoomTargetY = (zoomPointY - zoomY) / currentState.currentZoom\n\n store.setState({\n currentZoom,\n currentPositionX: calculatePositionX(-zoomTargetX * currentZoom + zoomPointX, currentZoom),\n currentPositionY: calculatePositionY(-zoomTargetY * currentZoom + zoomPointY, currentZoom),\n })\n }\n\n // last pair of coordinates of a touch with two fingers\n let prevTwoPositions: [PointerPosition, PointerPosition] | null = null\n let enabledScroll = true\n const pointerMap = new Map<number, PointerPosition>()\n\n let lastPositionX = 0\n let lastPositionY = 0\n let startX = 0\n let startY = 0\n\n container.style.overflow = \"hidden\"\n sourceImgElement.style.transformOrigin = \"0 0\"\n\n function updateZoom() {\n const currentState = store.getState()\n sourceImgElement.style.transform = `translate(${currentState.currentPositionX}px, ${currentState.currentPositionY}px) scale(${currentState.currentZoom})`\n container.style.rotate = `${currentState.currentRotation}deg`\n }\n\n function setState(newState: ZoomImageWheelStateUpdate) {\n store.batch(() => {\n const currentState = store.getState()\n if (typeof newState.enable === \"boolean\" && newState.enable !== currentState.enable) {\n store.setState({\n enable: newState.enable,\n })\n\n if (!newState.enable) {\n return\n }\n }\n\n if (typeof newState.currentRotation === \"number\") {\n const newCurrentRotation = newState.currentRotation\n store.setState({\n currentRotation: newCurrentRotation,\n })\n }\n\n if (typeof newState.currentZoom === \"number\" && newState.currentZoom !== currentState.currentZoom) {\n const newCurrentZoom = clamp(newState.currentZoom, 1, finalOptions.maxZoom)\n\n if (newCurrentZoom === currentState.currentZoom) {\n return\n }\n\n updateStateOnNewZoom(newCurrentZoom)\n }\n })\n\n updateZoom()\n }\n\n function processZoomWheel({ delta, x, y }: { delta: number; x: number; y: number }) {\n const containerRect = container.getBoundingClientRect()\n const currentState = store.getState()\n const isDimensionSwitched = checkDimensionSwitched()\n\n let zoomPointX = -1\n let zoomPointY = -1\n\n switch (currentState.currentRotation % 360) {\n case 0:\n zoomPointX = x - containerRect.left\n zoomPointY = y - containerRect.top\n break\n case 90:\n zoomPointX = Math.abs(x - containerRect.right)\n zoomPointY = Math.abs(y - containerRect.top)\n break\n case 180:\n zoomPointX = Math.abs(x - containerRect.right)\n zoomPointY = Math.abs(y - containerRect.bottom)\n break\n case 270:\n zoomPointX = Math.abs(x - containerRect.left)\n zoomPointY = Math.abs(y - containerRect.bottom)\n break\n }\n\n const zoomX = isDimensionSwitched ? currentState.currentPositionY : currentState.currentPositionX\n const zoomY = isDimensionSwitched ? currentState.currentPositionX : currentState.currentPositionY\n\n const zoomTargetX = (zoomPointX - zoomX) / currentState.currentZoom\n const zoomTargetY = (zoomPointY - zoomY) / currentState.currentZoom\n\n const newCurrentZoom = clamp(\n currentState.currentZoom + delta * finalOptions.wheelZoomRatio * currentState.currentZoom,\n 1,\n finalOptions.maxZoom,\n )\n\n const newX = calculatePositionX(-zoomTargetX * newCurrentZoom + zoomPointX, newCurrentZoom)\n const newY = calculatePositionY(-zoomTargetY * newCurrentZoom + zoomPointY, newCurrentZoom)\n\n store.setState({\n currentZoom: newCurrentZoom,\n currentPositionX: isDimensionSwitched ? newY : newX,\n currentPositionY: isDimensionSwitched ? newX : newY,\n })\n }\n\n function updatePositionsForSinglePointerFlow() {\n if (pointerMap.size === 1) {\n const { x, y } = pointerMap.values().next().value as PointerPosition\n const isDimensionSwitched = checkDimensionSwitched()\n startX = isDimensionSwitched ? y : x\n startY = isDimensionSwitched ? x : y\n }\n\n const currentState = store.getState()\n lastPositionX = currentState.currentPositionX\n lastPositionY = currentState.currentPositionY\n }\n\n function _handleWheel(event: WheelEvent) {\n event.preventDefault()\n\n if (store.getState().currentZoom === finalOptions.maxZoom && event.deltaY < 0) {\n return\n }\n\n const delta = -clamp(event.deltaY, -ZOOM_DELTA, ZOOM_DELTA)\n processZoomWheel({ delta, x: event.clientX, y: event.clientY })\n updateZoom()\n\n updatePositionsForSinglePointerFlow()\n }\n\n function _handlePointerMove(event: PointerEvent) {\n event.preventDefault()\n const { clientX, clientY, pointerId } = event\n for (const [cachedPointerId] of pointerMap.entries()) {\n if (cachedPointerId === pointerId) {\n pointerMap.set(cachedPointerId, { x: clientX, y: clientY })\n }\n }\n\n const { currentZoom, currentRotation } = store.getState()\n if (pointerMap.size === 1 && currentZoom !== 1) {\n const isDimensionSwitched = checkDimensionSwitched()\n const normalizedClientX = isDimensionSwitched ? clientY : clientX\n const normalizedClientY = isDimensionSwitched ? clientX : clientY\n\n let offsetX = -1\n let offsetY = -1\n\n switch (currentRotation % 360) {\n case 0:\n offsetX = normalizedClientX - startX\n offsetY = normalizedClientY - startY\n break\n case 90:\n offsetX = normalizedClientX - startX\n offsetY = startY - normalizedClientY\n break\n case 180:\n offsetX = startX - normalizedClientX\n offsetY = startY - normalizedClientY\n break\n case 270:\n offsetX = startX - normalizedClientX\n offsetY = normalizedClientY - startY\n break\n }\n\n store.setState({\n currentPositionX: calculatePositionX(lastPositionX + offsetX, currentZoom),\n currentPositionY: calculatePositionY(lastPositionY + offsetY, currentZoom),\n })\n updateZoom()\n }\n }\n\n const animationState = {\n startTimestamp: null as DOMHighResTimeStamp | null,\n // the state at the start of the zoom animation\n start: { x: 0, y: 0, zoom: 0 },\n // the target state at the end of the zoom animation\n target: { x: 0, y: 0, zoom: 0 },\n }\n\n function animateZoom(touchCoordinate: { x: number; y: number }) {\n // the `touchCoordinate` should be relative to the container\n const currentState = store.getState()\n\n animationState.startTimestamp = null\n animationState.start = {\n x: currentState.currentPositionX,\n y: currentState.currentPositionY,\n zoom: currentState.currentZoom,\n }\n\n if (currentState.currentZoom > 1) {\n animationState.target = {\n x: 0,\n y: 0,\n zoom: 1,\n }\n } else {\n animationState.target = {\n zoom: finalOptions.maxZoom,\n x: touchCoordinate.x * (1 - finalOptions.maxZoom),\n y: touchCoordinate.y * (1 - finalOptions.maxZoom),\n }\n }\n\n function lerp(a: number, b: number, t: number): number {\n return a * (1 - t) + b * t\n }\n\n function frame(timestamp: DOMHighResTimeStamp) {\n if (animationState.startTimestamp === null) {\n animationState.startTimestamp = timestamp\n }\n\n // interpolation parameter that linearly goes from 0 to 1 during the animation\n let t = (timestamp - animationState.startTimestamp) / finalOptions.dblTapAnimationDuration\n if (t > 1) {\n t = 1\n }\n\n store.setState({\n currentPositionX: lerp(animationState.start.x, animationState.target.x, t),\n currentPositionY: lerp(animationState.start.y, animationState.target.y, t),\n currentZoom: lerp(animationState.start.zoom, animationState.target.zoom, t),\n })\n updateZoom()\n\n if (t < 1) {\n requestAnimationFrame(frame)\n }\n }\n\n requestAnimationFrame(frame)\n }\n\n // These variables are used for zooming on double tap\n let touchTimer: NodeJS.Timeout | null = null\n const durationBetweenTap = 300\n\n function _handleTouchStart(event: TouchEvent) {\n if (event.touches.length > 1) {\n return\n }\n\n if (touchTimer === null) {\n touchTimer = setTimeout(() => {\n touchTimer = null\n }, durationBetweenTap)\n } else {\n clearTimeout(touchTimer)\n touchTimer = null\n\n const rect = container.getBoundingClientRect()\n const touch = event.touches[0]\n animateZoom({\n x: touch.clientX - rect.left,\n y: touch.clientY - rect.top,\n })\n return\n }\n }\n\n function _handleTouchMove(event: TouchEvent) {\n if (finalOptions.shouldZoomOnSingleTouch()) event.preventDefault()\n if (event.touches.length > 1) {\n event.preventDefault()\n const currentTwoPositions = [...event.touches].map((t) => ({ x: t.clientX, y: t.clientY })) as [\n PointerPosition,\n PointerPosition,\n ]\n\n if (prevTwoPositions !== null) {\n const { scale, center } = computeZoomGesture(prevTwoPositions, currentTwoPositions)\n processZoomWheel({ delta: Math.log(scale) / finalOptions.wheelZoomRatio, ...center })\n }\n // Store the current two pointer positions for the next move event\n prevTwoPositions = currentTwoPositions\n updateZoom()\n return\n }\n }\n\n function _handlePointerDown(event: PointerEvent) {\n if (event.pointerType === \"touch\" && !finalOptions.shouldZoomOnSingleTouch()) return\n event.preventDefault()\n if (pointerMap.size === 2) {\n return\n }\n\n if (enabledScroll) {\n disableScroll()\n enabledScroll = false\n }\n\n const { clientX, clientY, pointerId } = event\n\n const currentState = store.getState()\n lastPositionX = currentState.currentPositionX\n lastPositionY = currentState.currentPositionY\n\n const isDimensionSwitched = checkDimensionSwitched()\n startX = isDimensionSwitched ? clientY : clientX\n startY = isDimensionSwitched ? clientX : clientY\n pointerMap.set(pointerId, { x: clientX, y: clientY })\n }\n\n function _handlePointerUp(event: PointerEvent) {\n event.preventDefault()\n pointerMap.delete(event.pointerId)\n\n // Reset the distance as soon as one of the pointers is released\n if (pointerMap.size < 2) {\n prevTwoPositions = null\n }\n\n if (pointerMap.size === 0 && !enabledScroll) {\n enableScroll()\n enabledScroll = true\n }\n\n updatePositionsForSinglePointerFlow()\n }\n\n function _handlePointerLeave(event: PointerEvent) {\n event.preventDefault()\n pointerMap.delete(event.pointerId)\n prevTwoPositions = null\n if (!enabledScroll) {\n enableScroll()\n enabledScroll = true\n }\n }\n\n function checkZoomEnabled() {\n return store.getState().enable\n }\n\n const handleWheel = makeMaybeCallFunction(checkZoomEnabled, _handleWheel)\n const handlePointerDown = makeMaybeCallFunction(checkZoomEnabled, _handlePointerDown)\n const handlePointerLeave = makeMaybeCallFunction(checkZoomEnabled, _handlePointerLeave)\n const handlePointerMove = makeMaybeCallFunction(checkZoomEnabled, _handlePointerMove)\n const handlePointerUp = makeMaybeCallFunction(checkZoomEnabled, _handlePointerUp)\n const handleTouchStart = makeMaybeCallFunction(checkZoomEnabled, _handleTouchStart)\n const handleTouchMove = makeMaybeCallFunction(checkZoomEnabled, _handleTouchMove)\n\n const controller = new AbortController()\n const { signal } = controller\n container.addEventListener(\"wheel\", handleWheel, { signal })\n container.addEventListener(\"touchstart\", handleTouchStart, { signal })\n container.addEventListener(\"touchmove\", handleTouchMove, { signal })\n container.addEventListener(\"pointerdown\", handlePointerDown, { signal })\n container.addEventListener(\"pointerleave\", handlePointerLeave, { signal })\n container.addEventListener(\"pointermove\", handlePointerMove, { signal })\n container.addEventListener(\"pointerup\", handlePointerUp, { signal })\n container.addEventListener(\n \"touchend\",\n () => {\n enabledScroll = true\n enableScroll()\n },\n { signal },\n )\n\n // Kick things off in case we have initial zoom other than 1\n if (store.getState().currentZoom !== defaultInitialState.currentZoom) {\n updateStateOnNewZoom(store.getState().currentZoom)\n updateZoom()\n }\n\n return {\n cleanup() {\n controller.abort()\n store.cleanup()\n },\n subscribe: store.subscribe,\n setState,\n getState: store.getState,\n }\n}\n"]}