zoooom 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +215 -0
- package/dist/Zoooom-BypxEt9q.d.cts +128 -0
- package/dist/Zoooom-BypxEt9q.d.ts +128 -0
- package/dist/index.cjs +775 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +6 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +770 -0
- package/dist/index.js.map +1 -0
- package/dist/joystick.cjs +504 -0
- package/dist/joystick.cjs.map +1 -0
- package/dist/joystick.d.cts +31 -0
- package/dist/joystick.d.ts +31 -0
- package/dist/joystick.js +502 -0
- package/dist/joystick.js.map +1 -0
- package/dist/zoooom-full.iife.global.js +1272 -0
- package/dist/zoooom-full.iife.global.js.map +1 -0
- package/dist/zoooom.iife.global.js +773 -0
- package/dist/zoooom.iife.global.js.map +1 -0
- package/package.json +66 -0
- package/src/core/Zoooom.ts +294 -0
- package/src/core/constants.ts +29 -0
- package/src/core/loader.ts +129 -0
- package/src/core/transform.ts +146 -0
- package/src/full.ts +8 -0
- package/src/iife-entry.ts +2 -0
- package/src/iife-full-entry.ts +3 -0
- package/src/index.ts +11 -0
- package/src/input/gesture.ts +49 -0
- package/src/input/keyboard.ts +60 -0
- package/src/input/mouse.ts +54 -0
- package/src/input/touch.ts +111 -0
- package/src/input/wheel.ts +65 -0
- package/src/joystick/dom.ts +81 -0
- package/src/joystick/index.ts +2 -0
- package/src/joystick/joystick.ts +280 -0
- package/src/styles/core.ts +101 -0
- package/src/styles/joystick.ts +213 -0
- package/src/types.ts +118 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/core/constants.ts","../src/core/transform.ts","../src/core/loader.ts","../src/input/mouse.ts","../src/input/touch.ts","../src/input/wheel.ts","../src/input/keyboard.ts","../src/input/gesture.ts","../src/styles/core.ts","../src/core/Zoooom.ts","../src/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/** Default pan distance for keyboard arrows (px) */\nexport const PAN_STEP = 50;\n\n/** Zoom multiplier per discrete step (>1 zooms in) */\nexport const ZOOM_FACTOR = 1.5;\n\n/** Minimum zoom scale (allows slight zoom-out) */\nexport const MIN_SCALE = 0.8;\n\n/** Multiplier beyond native resolution for max zoom */\nexport const OVERSCALE_FACTOR = 2;\n\n/** Friction coefficient for momentum — velocity *= this each frame */\nexport const VELOCITY_DAMPING = 0.85;\n\n/** Sensitivity for trackpad continuous zoom */\nexport const TRACKPAD_SENSITIVITY = 0.002;\n\n/** Joystick panning zone radius (px) */\nexport const JOYSTICK_RADIUS = 60;\n\n/** Center deadzone as fraction of radius */\nexport const JOYSTICK_DEADZONE = 0.1;\n\n/** Maximum joystick panning speed (px/frame) */\nexport const MAX_JOYSTICK_SPEED = 10;\n\n/** Dwell timeout before joystick activates (ms) */\nexport const DWELL_TIMEOUT = 100;\n","import { MIN_SCALE, OVERSCALE_FACTOR, VELOCITY_DAMPING } from './constants.js';\nimport type { ZoooomState, ZoooomElements } from '../types.js';\n\n/**\n * Apply the current transform state to the image element.\n * Uses translate(calc(-50% + Xpx), calc(-50% + Ypx)) scale(S) for center-anchored zoom.\n */\nexport function updateTransform(state: ZoooomState, elements: ZoooomElements): void {\n if (!elements.image) return;\n elements.image.style.transform =\n `translate(calc(-50% + ${state.translateX}px), calc(-50% + ${state.translateY}px)) scale(${state.scale})`;\n}\n\n/**\n * Animate momentum after pan release — velocity decays per frame via damping.\n * Skipped when reduced motion is preferred.\n */\nexport function animateMovement(\n state: ZoooomState,\n elements: ZoooomElements,\n onPan?: (x: number, y: number) => void,\n): void {\n if (state.reducedMotion) {\n state.velocityX = 0;\n state.velocityY = 0;\n return;\n }\n\n state.isAnimating = true;\n\n function step() {\n state.translateX += state.velocityX;\n state.translateY += state.velocityY;\n state.velocityX *= VELOCITY_DAMPING;\n state.velocityY *= VELOCITY_DAMPING;\n\n updateTransform(state, elements);\n onPan?.(state.translateX, state.translateY);\n\n if (Math.abs(state.velocityX) > 0.1 || Math.abs(state.velocityY) > 0.1) {\n requestAnimationFrame(step);\n } else {\n state.velocityX = 0;\n state.velocityY = 0;\n state.isAnimating = false;\n }\n }\n\n requestAnimationFrame(step);\n}\n\n/** Center the image in the container (reset translate without changing scale) */\nexport function centerImage(state: ZoooomState, elements: ZoooomElements): void {\n if (!elements.container || !elements.image) return;\n state.translateX = 0;\n state.translateY = 0;\n updateTransform(state, elements);\n}\n\n/** Reset view to scale=1, centered, zero velocity */\nexport function resetView(state: ZoooomState, elements: ZoooomElements): void {\n state.scale = 1;\n state.translateX = 0;\n state.translateY = 0;\n state.velocityX = 0;\n state.velocityY = 0;\n updateTransform(state, elements);\n}\n\n/**\n * Calculate the maximum zoom scale from the image's natural vs displayed dimensions.\n * Allows zooming to full native resolution * overscale factor.\n */\nexport function calculateMaxScale(\n elements: ZoooomElements,\n overscaleFactor: number = OVERSCALE_FACTOR,\n): number {\n if (!elements.image) return 10;\n\n const naturalWidth = elements.image.naturalWidth;\n const naturalHeight = elements.image.naturalHeight;\n const displayWidth = elements.image.clientWidth;\n const displayHeight = elements.image.clientHeight;\n\n if (!naturalWidth || !naturalHeight || !displayWidth || !displayHeight) return 10;\n\n const widthRatio = naturalWidth / displayWidth;\n const heightRatio = naturalHeight / displayHeight;\n const maxScaleFactor = Math.max(widthRatio, heightRatio);\n\n // Higher overscale on mobile for full-res zoom\n const mobileOverscale = window.innerWidth <= 768 ? overscaleFactor * 2 : overscaleFactor;\n return Math.max(maxScaleFactor * mobileOverscale, MIN_SCALE);\n}\n\n/**\n * Zoom toward a specific point (cursor, pinch center, or container center).\n * Maintains the image point under the target coordinate.\n */\nexport function zoomTowardsPoint(\n state: ZoooomState,\n elements: ZoooomElements,\n delta: number,\n pointX?: number,\n pointY?: number,\n onZoom?: (scale: number) => void,\n): void {\n if (!elements.container || !elements.image) return;\n\n const currentScale = state.scale;\n const newScale = Math.max(MIN_SCALE, Math.min(state.scale * delta, state.maxScale));\n\n if (newScale === currentScale) return;\n\n const containerRect = elements.container.getBoundingClientRect();\n const containerCenterX = containerRect.width / 2;\n const containerCenterY = containerRect.height / 2;\n\n const targetX = pointX ?? containerCenterX;\n const targetY = pointY ?? containerCenterY;\n\n // Calculate the image point under the target coordinate\n const imageX = (targetX - containerCenterX - state.translateX) / currentScale;\n const imageY = (targetY - containerCenterY - state.translateY) / currentScale;\n\n // Update translation to keep that point stationary\n state.translateX = targetX - containerCenterX - imageX * newScale;\n state.translateY = targetY - containerCenterY - imageY * newScale;\n\n // Smooth zoom transition (skipped for reduced motion)\n if (!state.reducedMotion) {\n elements.image.style.transition = 'transform 0.2s ease-out';\n }\n\n state.scale = newScale;\n updateTransform(state, elements);\n onZoom?.(state.scale);\n\n if (!state.reducedMotion) {\n setTimeout(() => {\n if (elements.image) {\n elements.image.style.transition = '';\n }\n }, 200);\n }\n}\n","import type { ZoooomState, ZoooomElements, ZoooomResolvedOptions } from '../types.js';\nimport { calculateMaxScale, centerImage } from './transform.js';\n\n/**\n * Create the loading overlay element (spinner + text).\n * Returns null if loading is disabled.\n */\nexport function createLoadingOverlay(\n container: HTMLElement,\n loading: boolean | string,\n): HTMLElement | null {\n if (loading === false) return null;\n\n const overlay = document.createElement('div');\n overlay.className = 'zoooom-loading';\n overlay.setAttribute('role', 'status');\n overlay.setAttribute('aria-label', 'Loading image');\n\n if (typeof loading === 'string') {\n overlay.innerHTML = loading;\n } else {\n overlay.innerHTML = `\n <div class=\"zoooom-spinner\"></div>\n <div class=\"zoooom-loading-text\">Loading...</div>\n `;\n }\n\n container.appendChild(overlay);\n return overlay;\n}\n\n/** Remove the loading overlay from the DOM */\nfunction removeLoadingOverlay(elements: ZoooomElements): void {\n if (elements.loadingOverlay) {\n elements.loadingOverlay.remove();\n elements.loadingOverlay = null;\n }\n}\n\n/**\n * Load an image into the viewer with preloading, loading state, and fade-in.\n * Decoupled from any data source — just takes a URL and alt text.\n */\nexport function loadImage(\n src: string,\n alt: string,\n state: ZoooomState,\n elements: ZoooomElements,\n options: ZoooomResolvedOptions,\n emit: (event: string, ...args: unknown[]) => void,\n): void {\n // Reset transform state\n state.scale = 1;\n state.translateX = 0;\n state.translateY = 0;\n state.velocityX = 0;\n state.velocityY = 0;\n state.isLoaded = false;\n\n // Remove any existing loading overlay\n removeLoadingOverlay(elements);\n\n // Create loading overlay\n elements.loadingOverlay = createLoadingOverlay(elements.container, options.loading);\n\n // Hide image during load\n elements.image.style.opacity = '0';\n elements.image.style.visibility = 'hidden';\n elements.image.setAttribute('aria-hidden', 'true');\n elements.image.setAttribute('alt', '');\n\n // Preload at full resolution\n const preloader = new Image();\n\n preloader.onload = () => {\n elements.image.src = preloader.src;\n\n elements.image.onload = () => {\n removeLoadingOverlay(elements);\n\n // Restore alt text\n elements.image.setAttribute('alt', alt);\n elements.image.removeAttribute('aria-hidden');\n\n // Fade in (or show immediately for reduced motion)\n if (state.reducedMotion) {\n elements.image.style.opacity = '1';\n elements.image.style.visibility = 'visible';\n } else {\n setTimeout(() => {\n elements.image.style.transition = 'opacity 0.3s ease-in-out';\n elements.image.style.opacity = '1';\n elements.image.style.visibility = 'visible';\n\n // Clear transition after fade\n setTimeout(() => {\n elements.image.style.transition = '';\n }, 300);\n }, 50);\n }\n\n // Calculate max scale from actual dimensions\n if (options.maxScale === 'auto') {\n state.maxScale = calculateMaxScale(elements, options.overscaleFactor);\n }\n\n centerImage(state, elements);\n state.isLoaded = true;\n emit('load');\n options.onLoad?.();\n };\n\n elements.image.onerror = () => {\n removeLoadingOverlay(elements);\n const error = new Error(`Failed to load image: ${src}`);\n emit('error', error);\n options.onError?.(error);\n };\n };\n\n preloader.onerror = () => {\n removeLoadingOverlay(elements);\n const error = new Error(`Failed to load image: ${src}`);\n emit('error', error);\n options.onError?.(error);\n };\n\n preloader.src = src;\n}\n","import type { ZoooomState, ZoooomElements, InputCleanup } from '../types.js';\nimport { updateTransform } from '../core/transform.js';\n\n/**\n * Attach mouse drag-to-pan handlers.\n * Returns a cleanup function to remove all listeners.\n */\nexport function attachMouse(\n state: ZoooomState,\n elements: ZoooomElements,\n onPan?: (x: number, y: number) => void,\n): InputCleanup {\n const { container } = elements;\n\n function handleMouseDown(e: MouseEvent) {\n if (e.button !== 0) return; // Left button only\n state.isDragging = true;\n state.startX = e.clientX;\n state.startY = e.clientY;\n container.style.cursor = 'grabbing';\n e.preventDefault();\n }\n\n function handleMouseMove(e: MouseEvent) {\n if (!state.isDragging) return;\n const dx = e.clientX - state.startX;\n const dy = e.clientY - state.startY;\n state.translateX += dx;\n state.translateY += dy;\n state.startX = e.clientX;\n state.startY = e.clientY;\n updateTransform(state, elements);\n onPan?.(state.translateX, state.translateY);\n }\n\n function handleMouseUp() {\n if (state.isDragging) {\n state.isDragging = false;\n container.style.cursor = 'grab';\n }\n }\n\n container.addEventListener('mousedown', handleMouseDown);\n container.addEventListener('mousemove', handleMouseMove);\n container.addEventListener('mouseup', handleMouseUp);\n container.addEventListener('mouseleave', handleMouseUp);\n\n return () => {\n container.removeEventListener('mousedown', handleMouseDown);\n container.removeEventListener('mousemove', handleMouseMove);\n container.removeEventListener('mouseup', handleMouseUp);\n container.removeEventListener('mouseleave', handleMouseUp);\n };\n}\n","import { MIN_SCALE } from '../core/constants.js';\nimport { calculateMaxScale, updateTransform } from '../core/transform.js';\nimport type { ZoooomState, ZoooomElements, InputCleanup } from '../types.js';\n\nfunction getDistance(e: TouchEvent): number {\n const t1 = e.touches[0];\n const t2 = e.touches[1];\n const dx = t1.clientX - t2.clientX;\n const dy = t1.clientY - t2.clientY;\n return Math.sqrt(dx * dx + dy * dy);\n}\n\n/**\n * Attach touch pan and pinch-to-zoom handlers.\n * Returns a cleanup function to remove all listeners.\n */\nexport function attachTouch(\n state: ZoooomState,\n elements: ZoooomElements,\n overscaleFactor: number,\n onPan?: (x: number, y: number) => void,\n onZoom?: (scale: number) => void,\n): InputCleanup {\n const { container } = elements;\n\n function handleTouchStart(e: TouchEvent) {\n if (e.touches.length === 1) {\n state.isDragging = true;\n state.startX = e.touches[0].clientX;\n state.startY = e.touches[0].clientY;\n state.initialDistance = 0;\n } else if (e.touches.length === 2) {\n state.isDragging = false;\n state.initialDistance = getDistance(e);\n state.initialScale = state.scale;\n state.initialTranslateX = state.translateX;\n state.initialTranslateY = state.translateY;\n\n const t1 = e.touches[0];\n const t2 = e.touches[1];\n const rect = container.getBoundingClientRect();\n state.pinchCenter = {\n x: ((t1.clientX + t2.clientX) / 2) - rect.left,\n y: ((t1.clientY + t2.clientY) / 2) - rect.top,\n };\n }\n }\n\n function handleTouchMove(e: TouchEvent) {\n e.preventDefault();\n\n if (e.touches.length === 1 && state.isDragging) {\n const dx = e.touches[0].clientX - state.startX;\n const dy = e.touches[0].clientY - state.startY;\n state.translateX += dx;\n state.translateY += dy;\n state.startX = e.touches[0].clientX;\n state.startY = e.touches[0].clientY;\n updateTransform(state, elements);\n onPan?.(state.translateX, state.translateY);\n } else if (e.touches.length === 2 && state.initialDistance > 0) {\n const currentDistance = getDistance(e);\n const scaleFactor = currentDistance / state.initialDistance;\n const targetScale = state.initialScale * scaleFactor;\n state.maxScale = calculateMaxScale(elements, overscaleFactor);\n const newScale = Math.max(MIN_SCALE, Math.min(targetScale, state.maxScale));\n\n if (newScale === state.scale) return;\n\n const t1 = e.touches[0];\n const t2 = e.touches[1];\n const rect = container.getBoundingClientRect();\n const currentPinchCenter = {\n x: ((t1.clientX + t2.clientX) / 2) - rect.left,\n y: ((t1.clientY + t2.clientY) / 2) - rect.top,\n };\n\n const containerCenterX = rect.width / 2;\n const containerCenterY = rect.height / 2;\n\n const imageX = (state.pinchCenter.x - containerCenterX - state.initialTranslateX) / state.initialScale;\n const imageY = (state.pinchCenter.y - containerCenterY - state.initialTranslateY) / state.initialScale;\n\n state.translateX = currentPinchCenter.x - containerCenterX - imageX * newScale;\n state.translateY = currentPinchCenter.y - containerCenterY - imageY * newScale;\n\n state.scale = newScale;\n updateTransform(state, elements);\n onZoom?.(state.scale);\n\n state.pinchCenter = currentPinchCenter;\n }\n }\n\n function handleTouchEnd() {\n state.isDragging = false;\n state.initialDistance = 0;\n }\n\n container.addEventListener('touchstart', handleTouchStart, { passive: false });\n container.addEventListener('touchmove', handleTouchMove, { passive: false });\n container.addEventListener('touchend', handleTouchEnd);\n container.addEventListener('touchcancel', handleTouchEnd);\n\n return () => {\n container.removeEventListener('touchstart', handleTouchStart);\n container.removeEventListener('touchmove', handleTouchMove);\n container.removeEventListener('touchend', handleTouchEnd);\n container.removeEventListener('touchcancel', handleTouchEnd);\n };\n}\n","import { ZOOM_FACTOR, TRACKPAD_SENSITIVITY } from '../core/constants.js';\nimport { zoomTowardsPoint } from '../core/transform.js';\nimport type { ZoooomState, ZoooomElements, InputCleanup } from '../types.js';\n\n/**\n * Attach wheel/trackpad zoom handler.\n * Detects trackpad vs mouse wheel via delta magnitude.\n * Handles Ctrl+wheel for pinch gestures on Windows/Linux.\n */\nexport function attachWheel(\n state: ZoooomState,\n elements: ZoooomElements,\n zoomFactor: number = ZOOM_FACTOR,\n trackpadSensitivity: number = TRACKPAD_SENSITIVITY,\n onZoom?: (scale: number) => void,\n): InputCleanup {\n const { container } = elements;\n\n function handleWheel(e: WheelEvent) {\n e.preventDefault();\n\n const rect = container.getBoundingClientRect();\n const pointX = e.clientX - rect.left;\n const pointY = e.clientY - rect.top;\n\n let zoomDelta: number;\n\n if (e.ctrlKey) {\n // Pinch gesture (Ctrl+wheel on Windows/Linux)\n zoomDelta = e.deltaY < 0 ? zoomFactor : 1 / zoomFactor;\n } else {\n // Normalize delta based on deltaMode\n let normalizedDelta: number;\n switch (e.deltaMode) {\n case 1: normalizedDelta = e.deltaY * 20; break; // LINE\n case 2: normalizedDelta = e.deltaY * 100; break; // PAGE\n default: normalizedDelta = e.deltaY; // PIXEL\n }\n\n if (Math.abs(normalizedDelta) < 40) {\n // Trackpad — continuous zoom\n zoomDelta = Math.exp(-normalizedDelta * trackpadSensitivity);\n } else {\n // Mouse wheel — discrete steps\n zoomDelta = normalizedDelta > 0 ? 1 / zoomFactor : zoomFactor;\n }\n }\n\n // Manage scroll state class for CSS transition control\n container.classList.add('zoooom-scrolling');\n if (state.wheelTimeout) clearTimeout(state.wheelTimeout);\n state.wheelTimeout = setTimeout(() => {\n container.classList.remove('zoooom-scrolling');\n }, 200);\n\n zoomTowardsPoint(state, elements, zoomDelta, pointX, pointY, onZoom);\n }\n\n container.addEventListener('wheel', handleWheel, { passive: false });\n\n return () => {\n container.removeEventListener('wheel', handleWheel);\n if (state.wheelTimeout) clearTimeout(state.wheelTimeout);\n };\n}\n","import { PAN_STEP, ZOOM_FACTOR } from '../core/constants.js';\nimport { zoomTowardsPoint, animateMovement, resetView } from '../core/transform.js';\nimport type { ZoooomState, ZoooomElements, InputCleanup } from '../types.js';\n\n/**\n * Attach keyboard navigation handlers.\n * Arrows pan, +/- zoom, R resets, Escape is reserved for parent contexts.\n */\nexport function attachKeyboard(\n state: ZoooomState,\n elements: ZoooomElements,\n panStep: number = PAN_STEP,\n zoomFactor: number = ZOOM_FACTOR,\n onPan?: (x: number, y: number) => void,\n onZoom?: (scale: number) => void,\n): InputCleanup {\n const { container } = elements;\n\n function handleKeyDown(e: KeyboardEvent) {\n let handled = true;\n\n switch (e.key) {\n case 'ArrowLeft':\n state.velocityX = panStep * 0.2;\n animateMovement(state, elements, onPan);\n break;\n case 'ArrowRight':\n state.velocityX = -panStep * 0.2;\n animateMovement(state, elements, onPan);\n break;\n case 'ArrowUp':\n state.velocityY = panStep * 0.2;\n animateMovement(state, elements, onPan);\n break;\n case 'ArrowDown':\n state.velocityY = -panStep * 0.2;\n animateMovement(state, elements, onPan);\n break;\n case '+': case '=':\n zoomTowardsPoint(state, elements, zoomFactor, undefined, undefined, onZoom);\n break;\n case '-':\n zoomTowardsPoint(state, elements, 1 / zoomFactor, undefined, undefined, onZoom);\n break;\n case 'r': case 'R':\n resetView(state, elements);\n break;\n default:\n handled = false;\n }\n\n if (handled) e.preventDefault();\n }\n\n container.addEventListener('keydown', handleKeyDown);\n\n return () => {\n container.removeEventListener('keydown', handleKeyDown);\n };\n}\n","import { ZOOM_FACTOR } from '../core/constants.js';\nimport { zoomTowardsPoint } from '../core/transform.js';\nimport type { ZoooomState, ZoooomElements, InputCleanup } from '../types.js';\n\n/**\n * Attach Safari gesture events (gesturestart/change/end).\n * Only activates in Safari where GestureEvent exists.\n */\nexport function attachGesture(\n state: ZoooomState,\n elements: ZoooomElements,\n onZoom?: (scale: number) => void,\n): InputCleanup {\n const { container } = elements;\n\n // Only Safari supports gesture events\n if (!('ongesturestart' in window)) {\n return () => {};\n }\n\n let startScale = 1;\n\n function handleGestureStart(e: Event) {\n e.preventDefault();\n startScale = state.scale;\n }\n\n function handleGestureChange(e: Event) {\n e.preventDefault();\n const ge = e as unknown as { scale: number };\n const rect = container.getBoundingClientRect();\n const delta = ge.scale > 1 ? ZOOM_FACTOR : 1 / ZOOM_FACTOR;\n zoomTowardsPoint(state, elements, delta, rect.width / 2, rect.height / 2, onZoom);\n }\n\n function handleGestureEnd(e: Event) {\n e.preventDefault();\n }\n\n container.addEventListener('gesturestart', handleGestureStart, { passive: false } as EventListenerOptions);\n container.addEventListener('gesturechange', handleGestureChange, { passive: false } as EventListenerOptions);\n container.addEventListener('gestureend', handleGestureEnd, { passive: false } as EventListenerOptions);\n\n return () => {\n container.removeEventListener('gesturestart', handleGestureStart);\n container.removeEventListener('gesturechange', handleGestureChange);\n container.removeEventListener('gestureend', handleGestureEnd);\n };\n}\n","export const ZOOOOM_CSS = `\n[data-zoooom] {\n --zoooom-bg: #000;\n --zoooom-spinner-color: #2196f3;\n --zoooom-spinner-track: rgba(255, 255, 255, 0.3);\n --zoooom-spinner-size: 40px;\n --zoooom-loading-bg: rgba(0, 0, 0, 0.85);\n --zoooom-loading-radius: 10px;\n --zoooom-cursor: grab;\n --zoooom-cursor-active: grabbing;\n --zoooom-transition-speed: 0.2s;\n --zoooom-fade-speed: 0.3s;\n\n position: relative;\n width: 100%;\n height: 100%;\n background: var(--zoooom-bg);\n overflow: hidden;\n touch-action: none;\n cursor: var(--zoooom-cursor);\n user-select: none;\n -webkit-user-select: none;\n}\n\n[data-zoooom]:focus-visible {\n outline: 2px solid var(--zoooom-spinner-color);\n outline-offset: -2px;\n}\n\n[data-zoooom] .zoooom-image {\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%) scale(1);\n max-width: 100%;\n max-height: 100%;\n object-fit: contain;\n user-select: none;\n -webkit-user-select: none;\n pointer-events: none;\n}\n\n[data-zoooom] .zoooom-loading {\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n background: var(--zoooom-loading-bg);\n padding: 20px;\n border-radius: var(--zoooom-loading-radius);\n z-index: 10;\n min-width: 120px;\n}\n\n[data-zoooom] .zoooom-spinner {\n width: var(--zoooom-spinner-size);\n height: var(--zoooom-spinner-size);\n border: 4px solid var(--zoooom-spinner-track);\n border-radius: 50%;\n border-top-color: var(--zoooom-spinner-color);\n animation: zoooom-spin 1s linear infinite;\n}\n\n[data-zoooom] .zoooom-loading-text {\n margin-top: 12px;\n font-size: 14px;\n color: #fff;\n font-family: system-ui, -apple-system, sans-serif;\n}\n\n@keyframes zoooom-spin {\n to { transform: rotate(360deg); }\n}\n\n@media (prefers-reduced-motion: reduce) {\n [data-zoooom] .zoooom-spinner {\n animation: none;\n border-top-color: var(--zoooom-spinner-track);\n border-right-color: var(--zoooom-spinner-color);\n }\n\n [data-zoooom] .zoooom-image {\n transition: none !important;\n }\n}\n`;\n\nlet injected = false;\n\nexport function injectCoreStyles(): void {\n if (injected || typeof document === 'undefined') return;\n const style = document.createElement('style');\n style.setAttribute('data-zoooom-core', '');\n style.textContent = ZOOOOM_CSS;\n document.head.appendChild(style);\n injected = true;\n}\n","import type {\n ZoooomOptions,\n ZoooomResolvedOptions,\n ZoooomState,\n ZoooomElements,\n ZoooomEvent,\n ZoooomEventHandler,\n InputCleanup,\n} from '../types.js';\nimport { MIN_SCALE, ZOOM_FACTOR, OVERSCALE_FACTOR, VELOCITY_DAMPING, PAN_STEP, TRACKPAD_SENSITIVITY } from './constants.js';\nimport { updateTransform, zoomTowardsPoint, resetView, centerImage, calculateMaxScale } from './transform.js';\nimport { loadImage } from './loader.js';\nimport { attachMouse } from '../input/mouse.js';\nimport { attachTouch } from '../input/touch.js';\nimport { attachWheel } from '../input/wheel.js';\nimport { attachKeyboard } from '../input/keyboard.js';\nimport { attachGesture } from '../input/gesture.js';\nimport { injectCoreStyles } from '../styles/core.js';\n\nconst DEFAULTS: ZoooomResolvedOptions = {\n src: '',\n alt: 'Image',\n minScale: MIN_SCALE,\n maxScale: 'auto',\n overscaleFactor: OVERSCALE_FACTOR,\n zoomFactor: ZOOM_FACTOR,\n panStep: PAN_STEP,\n velocityDamping: VELOCITY_DAMPING,\n trackpadSensitivity: TRACKPAD_SENSITIVITY,\n mouse: true,\n touch: true,\n wheel: true,\n keyboard: true,\n loading: true,\n injectStyles: true,\n respectReducedMotion: true,\n};\n\nexport class Zoooom {\n private state: ZoooomState;\n private elements: ZoooomElements;\n private options: ZoooomResolvedOptions;\n private cleanups: InputCleanup[] = [];\n private listeners = new Map<string, Set<ZoooomEventHandler>>();\n private resizeHandler: (() => void) | null = null;\n\n constructor(container: string | HTMLElement, options: ZoooomOptions) {\n this.options = { ...DEFAULTS, ...options } as ZoooomResolvedOptions;\n\n // Inject styles\n if (this.options.injectStyles) {\n injectCoreStyles();\n }\n\n // Resolve container\n const el = typeof container === 'string'\n ? document.querySelector<HTMLElement>(container)\n : container;\n\n if (!el) {\n throw new Error(`Zoooom: container \"${container}\" not found`);\n }\n\n // Warn if container has no dimensions\n if (el.clientWidth === 0 || el.clientHeight === 0) {\n console.warn('Zoooom: container has zero dimensions. The viewer needs explicit width/height.');\n }\n\n // Set up container\n el.setAttribute('data-zoooom', '');\n if (!el.hasAttribute('tabindex')) {\n el.setAttribute('tabindex', '0');\n }\n el.setAttribute('role', 'application');\n el.setAttribute('aria-label', 'Image viewer — use arrow keys to pan, +/- to zoom');\n\n // Create image element\n const img = document.createElement('img');\n img.className = 'zoooom-image';\n img.setAttribute('draggable', 'false');\n el.appendChild(img);\n\n this.elements = { container: el, image: img, loadingOverlay: null };\n\n // Detect reduced motion\n const motionQuery = window.matchMedia('(prefers-reduced-motion: reduce)');\n const reducedMotion = this.options.respectReducedMotion && motionQuery.matches;\n\n // Initialize state\n this.state = {\n scale: 1,\n translateX: 0,\n translateY: 0,\n velocityX: 0,\n velocityY: 0,\n maxScale: typeof this.options.maxScale === 'number' ? this.options.maxScale : 10,\n isDragging: false,\n isAnimating: false,\n isLoaded: false,\n startX: 0,\n startY: 0,\n initialDistance: 0,\n initialScale: 1,\n initialTranslateX: 0,\n initialTranslateY: 0,\n pinchCenter: { x: 0, y: 0 },\n wheelTimeout: null,\n reducedMotion,\n };\n\n // Listen for reduced motion changes\n if (this.options.respectReducedMotion) {\n motionQuery.addEventListener('change', (e) => {\n this.state.reducedMotion = e.matches;\n });\n }\n\n // Attach input handlers\n if (this.options.mouse) {\n this.cleanups.push(attachMouse(this.state, this.elements, this.options.onPan));\n }\n if (this.options.touch) {\n this.cleanups.push(attachTouch(\n this.state, this.elements, this.options.overscaleFactor,\n this.options.onPan, this.options.onZoom,\n ));\n }\n if (this.options.wheel) {\n this.cleanups.push(attachWheel(\n this.state, this.elements, this.options.zoomFactor,\n this.options.trackpadSensitivity, this.options.onZoom,\n ));\n }\n if (this.options.keyboard) {\n this.cleanups.push(attachKeyboard(\n this.state, this.elements, this.options.panStep,\n this.options.zoomFactor, this.options.onPan, this.options.onZoom,\n ));\n }\n this.cleanups.push(attachGesture(this.state, this.elements, this.options.onZoom));\n\n // Resize handler\n this.resizeHandler = () => centerImage(this.state, this.elements);\n window.addEventListener('resize', this.resizeHandler);\n\n // Load initial image\n if (this.options.src) {\n this.load(this.options.src, this.options.alt);\n }\n }\n\n // --- Public state ---\n\n get scale(): number { return this.state.scale; }\n get translateX(): number { return this.state.translateX; }\n get translateY(): number { return this.state.translateY; }\n get isLoaded(): boolean { return this.state.isLoaded; }\n\n // --- Public methods ---\n\n zoomIn(): void {\n zoomTowardsPoint(\n this.state, this.elements, this.options.zoomFactor,\n undefined, undefined, this.options.onZoom,\n );\n this.emit('zoom', this.state.scale);\n }\n\n zoomOut(): void {\n zoomTowardsPoint(\n this.state, this.elements, 1 / this.options.zoomFactor,\n undefined, undefined, this.options.onZoom,\n );\n this.emit('zoom', this.state.scale);\n }\n\n /** \"Enhance.\" */\n enhance(): void {\n this.zoomIn();\n }\n\n zoomTo(scale: number): void {\n const delta = scale / this.state.scale;\n zoomTowardsPoint(this.state, this.elements, delta, undefined, undefined, this.options.onZoom);\n this.emit('zoom', this.state.scale);\n }\n\n zoomToPoint(scale: number, x: number, y: number): void {\n const delta = scale / this.state.scale;\n zoomTowardsPoint(this.state, this.elements, delta, x, y, this.options.onZoom);\n this.emit('zoom', this.state.scale);\n }\n\n panTo(x: number, y: number): void {\n this.state.translateX = x;\n this.state.translateY = y;\n updateTransform(this.state, this.elements);\n this.options.onPan?.(x, y);\n this.emit('pan', x, y);\n }\n\n panBy(dx: number, dy: number): void {\n this.state.translateX += dx;\n this.state.translateY += dy;\n updateTransform(this.state, this.elements);\n this.options.onPan?.(this.state.translateX, this.state.translateY);\n this.emit('pan', this.state.translateX, this.state.translateY);\n }\n\n reset(): void {\n resetView(this.state, this.elements);\n this.emit('reset');\n }\n\n center(): void {\n centerImage(this.state, this.elements);\n }\n\n load(src: string, alt?: string): void {\n loadImage(\n src,\n alt ?? this.options.alt,\n this.state,\n this.elements,\n this.options,\n this.emit.bind(this),\n );\n }\n\n /**\n * Apply velocity directly (used by joystick plugin).\n * Sets velocity and lets the rAF loop handle movement.\n */\n applyVelocity(vx: number, vy: number): void {\n this.state.translateX += vx;\n this.state.translateY += vy;\n updateTransform(this.state, this.elements);\n }\n\n /** Get the internal state (for plugin access) */\n getState(): Readonly<ZoooomState> {\n return this.state;\n }\n\n /** Get the managed elements (for plugin access) */\n getElements(): Readonly<ZoooomElements> {\n return this.elements;\n }\n\n // --- Events ---\n\n on(event: ZoooomEvent, handler: ZoooomEventHandler): void {\n if (!this.listeners.has(event)) {\n this.listeners.set(event, new Set());\n }\n this.listeners.get(event)!.add(handler);\n }\n\n off(event: ZoooomEvent, handler: ZoooomEventHandler): void {\n this.listeners.get(event)?.delete(handler);\n }\n\n emit(event: string, ...args: unknown[]): void {\n this.listeners.get(event)?.forEach(fn => fn(...args));\n }\n\n // --- Lifecycle ---\n\n destroy(): void {\n this.cleanups.forEach(fn => fn());\n this.cleanups = [];\n\n if (this.resizeHandler) {\n window.removeEventListener('resize', this.resizeHandler);\n this.resizeHandler = null;\n }\n\n if (this.state.wheelTimeout) {\n clearTimeout(this.state.wheelTimeout);\n }\n\n // Remove DOM\n this.elements.image.remove();\n if (this.elements.loadingOverlay) {\n this.elements.loadingOverlay.remove();\n }\n this.elements.container.removeAttribute('data-zoooom');\n this.elements.container.removeAttribute('role');\n this.elements.container.removeAttribute('aria-label');\n\n this.listeners.clear();\n this.emit('destroy');\n }\n}\n","export { Zoooom } from './core/Zoooom.js';\nexport type {\n ZoooomOptions,\n ZoooomEvent,\n ZoooomEventHandler,\n JoystickOptions,\n} from './types.js';\n\n// Default export for convenient usage: import Zoooom from 'zoooom'\nimport { Zoooom } from './core/Zoooom.js';\nexport default Zoooom;\n"]}
|
|
@@ -0,0 +1,504 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// src/core/constants.ts
|
|
4
|
+
var JOYSTICK_RADIUS = 60;
|
|
5
|
+
var JOYSTICK_DEADZONE = 0.1;
|
|
6
|
+
var MAX_JOYSTICK_SPEED = 10;
|
|
7
|
+
var DWELL_TIMEOUT = 100;
|
|
8
|
+
|
|
9
|
+
// src/joystick/dom.ts
|
|
10
|
+
function createJoystickDOM(container) {
|
|
11
|
+
const toggle = document.createElement("button");
|
|
12
|
+
toggle.className = "zoooom-joystick-toggle";
|
|
13
|
+
toggle.setAttribute("aria-label", "Toggle navigation joystick");
|
|
14
|
+
toggle.setAttribute("aria-expanded", "false");
|
|
15
|
+
toggle.textContent = "\u2316";
|
|
16
|
+
container.appendChild(toggle);
|
|
17
|
+
const wrap = document.createElement("div");
|
|
18
|
+
wrap.className = "zoooom-joystick-wrap";
|
|
19
|
+
wrap.setAttribute("aria-hidden", "true");
|
|
20
|
+
const disc = document.createElement("div");
|
|
21
|
+
disc.className = "zoooom-disc";
|
|
22
|
+
disc.setAttribute("role", "slider");
|
|
23
|
+
disc.setAttribute("aria-label", "Pan navigation control");
|
|
24
|
+
disc.setAttribute("aria-valuenow", "0");
|
|
25
|
+
disc.setAttribute("aria-valuemin", "0");
|
|
26
|
+
disc.setAttribute("aria-valuemax", "1");
|
|
27
|
+
disc.setAttribute("aria-valuetext", "Center position \u2014 not moving");
|
|
28
|
+
disc.setAttribute("tabindex", "0");
|
|
29
|
+
const innerCircle = document.createElement("div");
|
|
30
|
+
innerCircle.className = "zoooom-inner-circle";
|
|
31
|
+
const zoomOut = document.createElement("div");
|
|
32
|
+
zoomOut.className = "zoooom-zoom-half zoooom-zoom-out";
|
|
33
|
+
zoomOut.setAttribute("role", "button");
|
|
34
|
+
zoomOut.setAttribute("aria-label", "Zoom out");
|
|
35
|
+
zoomOut.setAttribute("tabindex", "0");
|
|
36
|
+
zoomOut.textContent = "\u2212";
|
|
37
|
+
const zoomIn = document.createElement("div");
|
|
38
|
+
zoomIn.className = "zoooom-zoom-half zoooom-zoom-in";
|
|
39
|
+
zoomIn.setAttribute("role", "button");
|
|
40
|
+
zoomIn.setAttribute("aria-label", "Zoom in");
|
|
41
|
+
zoomIn.setAttribute("tabindex", "0");
|
|
42
|
+
zoomIn.textContent = "+";
|
|
43
|
+
innerCircle.appendChild(zoomOut);
|
|
44
|
+
innerCircle.appendChild(zoomIn);
|
|
45
|
+
const arrows = document.createElement("div");
|
|
46
|
+
arrows.className = "zoooom-arrows";
|
|
47
|
+
arrows.setAttribute("aria-hidden", "true");
|
|
48
|
+
for (const dir of ["n", "e", "s", "w"]) {
|
|
49
|
+
const arrow = document.createElement("div");
|
|
50
|
+
arrow.className = `zoooom-arrow zoooom-arrow-${dir}`;
|
|
51
|
+
arrows.appendChild(arrow);
|
|
52
|
+
}
|
|
53
|
+
disc.appendChild(innerCircle);
|
|
54
|
+
disc.appendChild(arrows);
|
|
55
|
+
wrap.appendChild(disc);
|
|
56
|
+
container.appendChild(wrap);
|
|
57
|
+
return { wrap, toggle, disc, innerCircle, zoomIn, zoomOut };
|
|
58
|
+
}
|
|
59
|
+
function destroyJoystickDOM(dom) {
|
|
60
|
+
dom.wrap.remove();
|
|
61
|
+
dom.toggle.remove();
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// src/styles/joystick.ts
|
|
65
|
+
var JOYSTICK_CSS = `
|
|
66
|
+
.zoooom-joystick-wrap {
|
|
67
|
+
position: fixed;
|
|
68
|
+
bottom: 20px;
|
|
69
|
+
left: 50%;
|
|
70
|
+
transform: translateX(-50%);
|
|
71
|
+
z-index: 100;
|
|
72
|
+
touch-action: none;
|
|
73
|
+
opacity: 0;
|
|
74
|
+
pointer-events: none;
|
|
75
|
+
transition: opacity 0.3s ease-out, transform 0.3s ease-out;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
.zoooom-joystick-wrap.visible {
|
|
79
|
+
opacity: 1;
|
|
80
|
+
pointer-events: auto;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
.zoooom-joystick-toggle {
|
|
84
|
+
position: fixed;
|
|
85
|
+
bottom: 20px;
|
|
86
|
+
left: 50%;
|
|
87
|
+
transform: translateX(-50%);
|
|
88
|
+
width: 56px;
|
|
89
|
+
height: 56px;
|
|
90
|
+
border-radius: 50%;
|
|
91
|
+
background: rgba(0, 0, 0, 0.5);
|
|
92
|
+
border: 2px solid rgba(255, 255, 255, 0.6);
|
|
93
|
+
cursor: pointer;
|
|
94
|
+
z-index: 99;
|
|
95
|
+
display: flex;
|
|
96
|
+
align-items: center;
|
|
97
|
+
justify-content: center;
|
|
98
|
+
color: #fff;
|
|
99
|
+
font-size: 20px;
|
|
100
|
+
font-weight: bold;
|
|
101
|
+
backdrop-filter: blur(4px);
|
|
102
|
+
transition: background 0.2s, box-shadow 0.2s;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
.zoooom-joystick-toggle:hover {
|
|
106
|
+
background: rgba(0, 0, 0, 0.7);
|
|
107
|
+
box-shadow: 0 0 12px rgba(0, 0, 0, 0.4);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
.zoooom-joystick-toggle:focus-visible {
|
|
111
|
+
outline: 2px solid #2196f3;
|
|
112
|
+
outline-offset: 2px;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
.zoooom-disc {
|
|
116
|
+
width: 200px;
|
|
117
|
+
height: 200px;
|
|
118
|
+
border-radius: 50%;
|
|
119
|
+
background: rgba(0, 0, 0, 0.4);
|
|
120
|
+
border: 2px solid rgba(255, 255, 255, 0.6);
|
|
121
|
+
position: relative;
|
|
122
|
+
display: flex;
|
|
123
|
+
align-items: center;
|
|
124
|
+
justify-content: center;
|
|
125
|
+
touch-action: none;
|
|
126
|
+
box-shadow: 0 0 10px rgba(0, 0, 0, 0.3);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
.zoooom-disc.active {
|
|
130
|
+
border-color: rgba(255, 255, 255, 0.9);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
.zoooom-inner-circle {
|
|
134
|
+
width: 72px;
|
|
135
|
+
height: 72px;
|
|
136
|
+
border-radius: 50%;
|
|
137
|
+
background: rgba(255, 255, 255, 0.15);
|
|
138
|
+
border: 1px solid rgba(255, 255, 255, 0.5);
|
|
139
|
+
display: flex;
|
|
140
|
+
position: relative;
|
|
141
|
+
z-index: 3;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
.zoooom-zoom-half {
|
|
145
|
+
width: 50%;
|
|
146
|
+
height: 100%;
|
|
147
|
+
display: flex;
|
|
148
|
+
align-items: center;
|
|
149
|
+
justify-content: center;
|
|
150
|
+
cursor: pointer;
|
|
151
|
+
font-weight: bold;
|
|
152
|
+
font-size: 22px;
|
|
153
|
+
color: #fff;
|
|
154
|
+
user-select: none;
|
|
155
|
+
transition: background 0.15s;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
.zoooom-zoom-half:hover {
|
|
159
|
+
background: rgba(255, 255, 255, 0.25);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
.zoooom-zoom-out {
|
|
163
|
+
border-radius: 36px 0 0 36px;
|
|
164
|
+
border-right: 1px solid rgba(255, 255, 255, 0.4);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
.zoooom-zoom-in {
|
|
168
|
+
border-radius: 0 36px 36px 0;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
.zoooom-arrows {
|
|
172
|
+
position: absolute;
|
|
173
|
+
width: 100%;
|
|
174
|
+
height: 100%;
|
|
175
|
+
border-radius: 50%;
|
|
176
|
+
pointer-events: none;
|
|
177
|
+
overflow: hidden;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
.zoooom-arrow {
|
|
181
|
+
position: absolute;
|
|
182
|
+
width: 0;
|
|
183
|
+
height: 0;
|
|
184
|
+
opacity: 0;
|
|
185
|
+
transition: opacity 0.2s;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
.zoooom-arrow-n {
|
|
189
|
+
top: 14px;
|
|
190
|
+
left: 50%;
|
|
191
|
+
transform: translateX(-50%);
|
|
192
|
+
border-left: 10px solid transparent;
|
|
193
|
+
border-right: 10px solid transparent;
|
|
194
|
+
border-bottom: 14px solid rgba(255, 255, 255, 0.7);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
.zoooom-arrow-e {
|
|
198
|
+
top: 50%;
|
|
199
|
+
right: 14px;
|
|
200
|
+
transform: translateY(-50%);
|
|
201
|
+
border-top: 10px solid transparent;
|
|
202
|
+
border-bottom: 10px solid transparent;
|
|
203
|
+
border-left: 14px solid rgba(255, 255, 255, 0.7);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
.zoooom-arrow-s {
|
|
207
|
+
bottom: 14px;
|
|
208
|
+
left: 50%;
|
|
209
|
+
transform: translateX(-50%);
|
|
210
|
+
border-left: 10px solid transparent;
|
|
211
|
+
border-right: 10px solid transparent;
|
|
212
|
+
border-top: 14px solid rgba(255, 255, 255, 0.7);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
.zoooom-arrow-w {
|
|
216
|
+
top: 50%;
|
|
217
|
+
left: 14px;
|
|
218
|
+
transform: translateY(-50%);
|
|
219
|
+
border-top: 10px solid transparent;
|
|
220
|
+
border-bottom: 10px solid transparent;
|
|
221
|
+
border-right: 14px solid rgba(255, 255, 255, 0.7);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
.zoooom-disc.north .zoooom-arrow-n,
|
|
225
|
+
.zoooom-disc.south .zoooom-arrow-s,
|
|
226
|
+
.zoooom-disc.east .zoooom-arrow-e,
|
|
227
|
+
.zoooom-disc.west .zoooom-arrow-w,
|
|
228
|
+
.zoooom-disc.north-east .zoooom-arrow-n,
|
|
229
|
+
.zoooom-disc.north-east .zoooom-arrow-e,
|
|
230
|
+
.zoooom-disc.south-east .zoooom-arrow-s,
|
|
231
|
+
.zoooom-disc.south-east .zoooom-arrow-e,
|
|
232
|
+
.zoooom-disc.south-west .zoooom-arrow-s,
|
|
233
|
+
.zoooom-disc.south-west .zoooom-arrow-w,
|
|
234
|
+
.zoooom-disc.north-west .zoooom-arrow-n,
|
|
235
|
+
.zoooom-disc.north-west .zoooom-arrow-w {
|
|
236
|
+
opacity: 1;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
@media (max-width: 768px) {
|
|
240
|
+
.zoooom-disc {
|
|
241
|
+
width: 140px;
|
|
242
|
+
height: 140px;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
.zoooom-inner-circle {
|
|
246
|
+
width: 56px;
|
|
247
|
+
height: 56px;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
.zoooom-joystick-toggle {
|
|
251
|
+
width: 48px;
|
|
252
|
+
height: 48px;
|
|
253
|
+
font-size: 16px;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
@media (prefers-reduced-motion: reduce) {
|
|
258
|
+
.zoooom-joystick-wrap {
|
|
259
|
+
transition: none;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
.zoooom-arrow {
|
|
263
|
+
transition: none;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
`;
|
|
267
|
+
var injected = false;
|
|
268
|
+
function injectJoystickStyles() {
|
|
269
|
+
if (injected || typeof document === "undefined") return;
|
|
270
|
+
const style = document.createElement("style");
|
|
271
|
+
style.setAttribute("data-zoooom-joystick", "");
|
|
272
|
+
style.textContent = JOYSTICK_CSS;
|
|
273
|
+
document.head.appendChild(style);
|
|
274
|
+
injected = true;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// src/joystick/joystick.ts
|
|
278
|
+
function angleToDirection(angle) {
|
|
279
|
+
if (angle >= -22.5 && angle < 22.5) return "east";
|
|
280
|
+
if (angle >= 22.5 && angle < 67.5) return "south-east";
|
|
281
|
+
if (angle >= 67.5 && angle < 112.5) return "south";
|
|
282
|
+
if (angle >= 112.5 && angle < 157.5) return "south-west";
|
|
283
|
+
if (angle >= 157.5 || angle < -157.5) return "west";
|
|
284
|
+
if (angle >= -157.5 && angle < -112.5) return "north-west";
|
|
285
|
+
if (angle >= -112.5 && angle < -67.5) return "north";
|
|
286
|
+
return "north-east";
|
|
287
|
+
}
|
|
288
|
+
var ZoooomJoystick = class {
|
|
289
|
+
constructor(viewer, options) {
|
|
290
|
+
this.visible = false;
|
|
291
|
+
this.active = false;
|
|
292
|
+
this.animationId = null;
|
|
293
|
+
this.joystickX = 0;
|
|
294
|
+
this.joystickY = 0;
|
|
295
|
+
this.currentDirection = "";
|
|
296
|
+
this.dwellTimer = null;
|
|
297
|
+
this.isDwelling = false;
|
|
298
|
+
this.hideTimer = null;
|
|
299
|
+
this.viewer = viewer;
|
|
300
|
+
this.options = {
|
|
301
|
+
radius: options?.radius ?? JOYSTICK_RADIUS,
|
|
302
|
+
deadzone: options?.deadzone ?? JOYSTICK_DEADZONE,
|
|
303
|
+
maxSpeed: options?.maxSpeed ?? MAX_JOYSTICK_SPEED,
|
|
304
|
+
position: options?.position ?? "bottom-center",
|
|
305
|
+
showToggle: options?.showToggle ?? true,
|
|
306
|
+
dwellTimeout: options?.dwellTimeout ?? DWELL_TIMEOUT
|
|
307
|
+
};
|
|
308
|
+
injectJoystickStyles();
|
|
309
|
+
const elements = this.viewer.getElements();
|
|
310
|
+
this.dom = createJoystickDOM(elements.container);
|
|
311
|
+
this.bindEvents();
|
|
312
|
+
}
|
|
313
|
+
show() {
|
|
314
|
+
this.visible = true;
|
|
315
|
+
this.dom.wrap.classList.add("visible");
|
|
316
|
+
this.dom.wrap.setAttribute("aria-hidden", "false");
|
|
317
|
+
this.dom.toggle.setAttribute("aria-expanded", "true");
|
|
318
|
+
if (this.hideTimer) clearTimeout(this.hideTimer);
|
|
319
|
+
}
|
|
320
|
+
hide() {
|
|
321
|
+
this.visible = false;
|
|
322
|
+
this.dom.wrap.classList.remove("visible");
|
|
323
|
+
this.dom.wrap.setAttribute("aria-hidden", "true");
|
|
324
|
+
this.dom.toggle.setAttribute("aria-expanded", "false");
|
|
325
|
+
this.stopMovement();
|
|
326
|
+
this.clearDirection();
|
|
327
|
+
}
|
|
328
|
+
destroy() {
|
|
329
|
+
this.hide();
|
|
330
|
+
this.stopMovement();
|
|
331
|
+
if (this.dwellTimer) clearTimeout(this.dwellTimer);
|
|
332
|
+
if (this.hideTimer) clearTimeout(this.hideTimer);
|
|
333
|
+
destroyJoystickDOM(this.dom);
|
|
334
|
+
}
|
|
335
|
+
bindEvents() {
|
|
336
|
+
const { toggle, disc, zoomIn, zoomOut, innerCircle } = this.dom;
|
|
337
|
+
toggle.addEventListener("click", () => {
|
|
338
|
+
if (this.visible) this.hide();
|
|
339
|
+
else this.show();
|
|
340
|
+
});
|
|
341
|
+
toggle.addEventListener("mouseenter", () => this.show());
|
|
342
|
+
toggle.addEventListener("mouseleave", () => {
|
|
343
|
+
this.hideTimer = setTimeout(() => this.hide(), 15e3);
|
|
344
|
+
});
|
|
345
|
+
disc.addEventListener("mousemove", (e) => {
|
|
346
|
+
if (e.target !== disc) return;
|
|
347
|
+
if (!this.isDwelling) {
|
|
348
|
+
this.startDwell(e);
|
|
349
|
+
} else {
|
|
350
|
+
this.handleMove(e);
|
|
351
|
+
}
|
|
352
|
+
});
|
|
353
|
+
disc.addEventListener("mousedown", (e) => {
|
|
354
|
+
if (e.target !== disc) return;
|
|
355
|
+
e.preventDefault();
|
|
356
|
+
this.handleMove(e);
|
|
357
|
+
const moveHandler = (me) => this.handleMove(me);
|
|
358
|
+
document.addEventListener("mousemove", moveHandler);
|
|
359
|
+
document.addEventListener("mouseup", () => {
|
|
360
|
+
document.removeEventListener("mousemove", moveHandler);
|
|
361
|
+
this.stopDwell();
|
|
362
|
+
this.resetJoystick();
|
|
363
|
+
}, { once: true });
|
|
364
|
+
});
|
|
365
|
+
disc.addEventListener("mouseleave", () => {
|
|
366
|
+
this.stopDwell();
|
|
367
|
+
this.resetJoystick();
|
|
368
|
+
});
|
|
369
|
+
disc.addEventListener("touchstart", (e) => {
|
|
370
|
+
if (e.target !== disc) return;
|
|
371
|
+
e.preventDefault();
|
|
372
|
+
this.handleMove(e);
|
|
373
|
+
const moveHandler = (te) => this.handleMove(te);
|
|
374
|
+
document.addEventListener("touchmove", moveHandler, { passive: false });
|
|
375
|
+
document.addEventListener("touchend", () => {
|
|
376
|
+
document.removeEventListener("touchmove", moveHandler);
|
|
377
|
+
this.stopDwell();
|
|
378
|
+
this.resetJoystick();
|
|
379
|
+
}, { once: true });
|
|
380
|
+
}, { passive: false });
|
|
381
|
+
zoomIn.addEventListener("click", (e) => {
|
|
382
|
+
e.stopPropagation();
|
|
383
|
+
this.viewer.zoomIn();
|
|
384
|
+
});
|
|
385
|
+
zoomOut.addEventListener("click", (e) => {
|
|
386
|
+
e.stopPropagation();
|
|
387
|
+
this.viewer.zoomOut();
|
|
388
|
+
});
|
|
389
|
+
innerCircle.addEventListener("mouseenter", () => {
|
|
390
|
+
this.stopDwell();
|
|
391
|
+
this.resetJoystick();
|
|
392
|
+
});
|
|
393
|
+
innerCircle.addEventListener("mousedown", (e) => e.stopPropagation());
|
|
394
|
+
innerCircle.addEventListener("touchstart", (e) => e.stopPropagation(), { passive: false });
|
|
395
|
+
}
|
|
396
|
+
handleMove(event) {
|
|
397
|
+
if (!this.visible) return;
|
|
398
|
+
const clientX = "clientX" in event ? event.clientX : event.touches[0]?.clientX ?? 0;
|
|
399
|
+
const clientY = "clientY" in event ? event.clientY : event.touches[0]?.clientY ?? 0;
|
|
400
|
+
const rect = this.dom.disc.getBoundingClientRect();
|
|
401
|
+
const centerX = rect.left + rect.width / 2;
|
|
402
|
+
const centerY = rect.top + rect.height / 2;
|
|
403
|
+
const distX = clientX - centerX;
|
|
404
|
+
const distY = clientY - centerY;
|
|
405
|
+
const distance = Math.sqrt(distX * distX + distY * distY);
|
|
406
|
+
let normalizedX = distX / this.options.radius;
|
|
407
|
+
let normalizedY = distY / this.options.radius;
|
|
408
|
+
if (distance > this.options.radius) {
|
|
409
|
+
normalizedX = normalizedX * (this.options.radius / distance);
|
|
410
|
+
normalizedY = normalizedY * (this.options.radius / distance);
|
|
411
|
+
}
|
|
412
|
+
if (Math.abs(normalizedX) < this.options.deadzone) normalizedX = 0;
|
|
413
|
+
if (Math.abs(normalizedY) < this.options.deadzone) normalizedY = 0;
|
|
414
|
+
this.joystickX = normalizedX;
|
|
415
|
+
this.joystickY = normalizedY;
|
|
416
|
+
if (distance > this.options.radius * this.options.deadzone) {
|
|
417
|
+
const angle = Math.atan2(distY, distX) * 180 / Math.PI;
|
|
418
|
+
const dir = angleToDirection(angle);
|
|
419
|
+
if (dir !== this.currentDirection) {
|
|
420
|
+
this.clearDirection();
|
|
421
|
+
this.dom.disc.classList.add(dir);
|
|
422
|
+
this.currentDirection = dir;
|
|
423
|
+
}
|
|
424
|
+
} else {
|
|
425
|
+
this.clearDirection();
|
|
426
|
+
}
|
|
427
|
+
if (!this.animationId && (Math.abs(normalizedX) > this.options.deadzone || Math.abs(normalizedY) > this.options.deadzone)) {
|
|
428
|
+
this.active = true;
|
|
429
|
+
this.dom.disc.classList.add("active");
|
|
430
|
+
this.startMovement();
|
|
431
|
+
this.updateAria(normalizedX, normalizedY);
|
|
432
|
+
} else if (Math.abs(normalizedX) <= this.options.deadzone && Math.abs(normalizedY) <= this.options.deadzone) {
|
|
433
|
+
this.active = false;
|
|
434
|
+
this.dom.disc.classList.remove("active");
|
|
435
|
+
this.stopMovement();
|
|
436
|
+
this.dom.disc.setAttribute("aria-valuenow", "0");
|
|
437
|
+
this.dom.disc.setAttribute("aria-valuetext", "Center position \u2014 not moving");
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
startDwell(e) {
|
|
441
|
+
if (this.dwellTimer) clearTimeout(this.dwellTimer);
|
|
442
|
+
this.dwellTimer = setTimeout(() => {
|
|
443
|
+
this.isDwelling = true;
|
|
444
|
+
this.handleMove(e);
|
|
445
|
+
}, this.options.dwellTimeout);
|
|
446
|
+
}
|
|
447
|
+
stopDwell() {
|
|
448
|
+
if (this.dwellTimer) {
|
|
449
|
+
clearTimeout(this.dwellTimer);
|
|
450
|
+
this.dwellTimer = null;
|
|
451
|
+
}
|
|
452
|
+
this.isDwelling = false;
|
|
453
|
+
}
|
|
454
|
+
startMovement() {
|
|
455
|
+
if (this.animationId) return;
|
|
456
|
+
const step = () => {
|
|
457
|
+
if (!this.active) {
|
|
458
|
+
this.stopMovement();
|
|
459
|
+
return;
|
|
460
|
+
}
|
|
461
|
+
const vx = -this.joystickX * this.options.maxSpeed;
|
|
462
|
+
const vy = -this.joystickY * this.options.maxSpeed;
|
|
463
|
+
this.viewer.applyVelocity(vx, vy);
|
|
464
|
+
this.animationId = requestAnimationFrame(step);
|
|
465
|
+
};
|
|
466
|
+
this.animationId = requestAnimationFrame(step);
|
|
467
|
+
}
|
|
468
|
+
stopMovement() {
|
|
469
|
+
if (this.animationId) {
|
|
470
|
+
cancelAnimationFrame(this.animationId);
|
|
471
|
+
this.animationId = null;
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
resetJoystick() {
|
|
475
|
+
this.joystickX = 0;
|
|
476
|
+
this.joystickY = 0;
|
|
477
|
+
this.active = false;
|
|
478
|
+
this.stopMovement();
|
|
479
|
+
this.clearDirection();
|
|
480
|
+
this.dom.disc.classList.remove("active");
|
|
481
|
+
}
|
|
482
|
+
clearDirection() {
|
|
483
|
+
if (this.currentDirection) {
|
|
484
|
+
this.dom.disc.classList.remove(this.currentDirection);
|
|
485
|
+
this.currentDirection = "";
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
updateAria(x, y) {
|
|
489
|
+
const magnitude = Math.sqrt(x * x + y * y);
|
|
490
|
+
this.dom.disc.setAttribute("aria-valuenow", magnitude.toFixed(2));
|
|
491
|
+
let direction;
|
|
492
|
+
if (Math.abs(x) > Math.abs(y)) {
|
|
493
|
+
direction = x > 0 ? "right" : "left";
|
|
494
|
+
} else {
|
|
495
|
+
direction = y > 0 ? "down" : "up";
|
|
496
|
+
}
|
|
497
|
+
const intensity = magnitude > 0.7 ? "fast" : magnitude > 0.3 ? "medium" : "slow";
|
|
498
|
+
this.dom.disc.setAttribute("aria-valuetext", `Moving ${intensity} ${direction}`);
|
|
499
|
+
}
|
|
500
|
+
};
|
|
501
|
+
|
|
502
|
+
exports.ZoooomJoystick = ZoooomJoystick;
|
|
503
|
+
//# sourceMappingURL=joystick.cjs.map
|
|
504
|
+
//# sourceMappingURL=joystick.cjs.map
|