@zoom-image/core
Version:
A core implementation of zoom image
1 lines • 7.32 kB
Source Map (JSON)
{"version":3,"sources":["../src/createZoomImageMove.ts"],"names":[],"mappings":"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","sourcesContent":["import { createStore } from \"@namnode/store\"\nimport { imageLoader } from \"./imageLoader\"\nimport { ZoomedImgStatus } from \"./types\"\nimport { disableScroll, enableScroll, getSourceImage } from \"./utils\"\n\nexport type ZoomImageMoveOptions = {\n zoomFactor?: number\n zoomImageSource?: string\n // @deprecated\n disableScrollLock?: boolean\n disabledContextMenu?: boolean\n zoomImageProps?: {\n alt?: string\n className?: string\n }\n}\n\nexport type ZoomImageMoveState = {\n zoomedImgStatus: ZoomedImgStatus\n}\n\nexport function createZoomImageMove(container: HTMLElement, options: ZoomImageMoveOptions = {}) {\n let activePointerId: number | null = null\n const sourceImgElement = getSourceImage(container)\n const finalOptions: Omit<Required<ZoomImageMoveOptions>, \"zoomImageProps\" | \"disableScrollLock\"> = {\n zoomFactor: options.zoomFactor ?? 4,\n zoomImageSource: options.zoomImageSource ?? sourceImgElement.src,\n disabledContextMenu: options.disabledContextMenu ?? false,\n }\n\n const { disabledContextMenu, zoomFactor, zoomImageSource } = finalOptions\n\n const store = createStore<ZoomImageMoveState>({\n zoomedImgStatus: \"idle\",\n })\n\n const zoomedImg = document.createElement(\"img\")\n options.zoomImageProps?.alt && (zoomedImg.alt = options.zoomImageProps.alt)\n options.zoomImageProps?.className && (zoomedImg.className = options.zoomImageProps.className)\n zoomedImg.style.maxWidth = \"none\"\n zoomedImg.style.position = \"absolute\"\n zoomedImg.style.top = \"0\"\n zoomedImg.style.left = \"0\"\n\n // Credit to https://stackoverflow.com/questions/19587555/disable-default-save-image-option-in-mobile/19590365#19590365\n if (disabledContextMenu) {\n zoomedImg.style[\"-webkit-user-select\"] = \"none\"\n zoomedImg.style[\"-webkit-touch-callout\"] = \"none\"\n zoomedImg.oncontextmenu = () => false\n }\n\n const checkValidPointer = (event: PointerEvent) => {\n return activePointerId && event.pointerId === activePointerId\n }\n\n function handlePointerEnter(event: PointerEvent) {\n if (activePointerId === null) {\n activePointerId = event.pointerId\n container.appendChild(zoomedImg)\n zoomedImg.style.display = \"block\"\n const zoomedImgWidth = sourceImgElement.clientWidth * zoomFactor\n const zoomedImgHeight = sourceImgElement.clientHeight * zoomFactor\n zoomedImg.style.width = `${zoomedImgWidth}px`\n zoomedImg.style.height = `${zoomedImgHeight}px`\n imageLoader.createZoomImage(zoomedImg, zoomImageSource, store)\n\n processZoom(event)\n\n event.pointerType !== \"mouse\" && disableScroll()\n }\n }\n\n function handlePointerMove(event: PointerEvent) {\n if (checkValidPointer(event)) {\n processZoom(event)\n }\n }\n\n function resetZoomedImg(event: PointerEvent) {\n if (checkValidPointer(event)) {\n container.contains(zoomedImg) && container.removeChild(zoomedImg)\n zoomedImg.style.display = \"none\"\n zoomedImg.style.transform = \"none\"\n enableScroll()\n activePointerId = null\n }\n }\n\n const calculatePositionX = (newPositionX: number) => {\n const width = container.clientWidth\n if (newPositionX > 0) return 0\n if (newPositionX + width * zoomFactor < width) return -width * (zoomFactor - 1)\n return newPositionX\n }\n\n const calculatePositionY = (newPositionY: number) => {\n const height = container.clientHeight\n if (newPositionY > 0) return 0\n if (newPositionY + height * zoomFactor < height) return -height * (zoomFactor - 1)\n return newPositionY\n }\n\n function processZoom(event: PointerEvent) {\n zoomedImg.style.display = \"block\"\n\n const containerRect = container.getBoundingClientRect()\n const zoomPointX = event.clientX - containerRect.left\n const zoomPointY = event.clientY - containerRect.top\n\n const currentPositionX = calculatePositionX(-zoomPointX * zoomFactor + zoomPointX)\n const currentPositionY = calculatePositionY(-zoomPointY * zoomFactor + zoomPointY)\n zoomedImg.style.transform = `translate(${currentPositionX}px, ${currentPositionY}px)`\n }\n\n const controller = new AbortController()\n const { signal } = controller\n container.addEventListener(\"pointerenter\", handlePointerEnter, { signal })\n container.addEventListener(\"pointermove\", handlePointerMove, { signal })\n container.addEventListener(\"pointerleave\", resetZoomedImg, { signal })\n container.addEventListener(\n \"touchstart\",\n (event) => {\n disabledContextMenu && event.preventDefault()\n },\n { signal },\n )\n\n return {\n cleanup: () => {\n controller.abort()\n container.contains(zoomedImg) && container.removeChild(zoomedImg)\n store.cleanup()\n },\n subscribe: store.subscribe,\n getState: store.getState,\n }\n}\n"]}