Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 | /** * Pointer Lock Hook * * Manages pointer lock state and event listeners for precision mode. * When pointer lock is active, the cursor is hidden and mouse movements * are captured as relative deltas instead of absolute positions. */ import { useState, useEffect, useRef, type RefObject } from 'react' export interface UsePointerLockOptions { /** The container element to lock the pointer to */ containerRef: RefObject<HTMLDivElement> /** Whether precision mode is available on this device */ canUsePrecisionMode: boolean /** Callback when pointer lock is acquired */ onLockAcquired?: () => void /** Callback when pointer lock is released */ onLockReleased?: () => void } export interface UsePointerLockReturn { /** Whether pointer lock is currently active */ pointerLocked: boolean /** Request pointer lock (activate precision mode) */ requestPointerLock: () => void /** Exit pointer lock (deactivate precision mode) */ exitPointerLock: () => void } /** * Custom hook for managing pointer lock state. * * Handles pointer lock events and provides methods to request/exit * pointer lock mode. The pointer lock API is used for precision mode * in the magnifier, allowing fine-grained cursor control. * * @param options - Configuration options * @returns Pointer lock state and control methods */ export function usePointerLock(options: UsePointerLockOptions): UsePointerLockReturn { const { containerRef, canUsePrecisionMode, onLockAcquired, onLockReleased } = options const [pointerLocked, setPointerLocked] = useState(false) // Track previous lock state for detecting transitions const prevLockedRef = useRef(false) // Auto-exit pointer lock when precision mode becomes unavailable // (e.g., when switching to mobile device toolbar in Chrome DevTools) useEffect(() => { if (!canUsePrecisionMode && document.pointerLockElement) { console.log('[usePointerLock] Precision mode unavailable - exiting pointer lock') document.exitPointerLock() } }, [canUsePrecisionMode]) // Set up pointer lock event listeners useEffect(() => { const handlePointerLockChange = () => { const isLocked = document.pointerLockElement === containerRef.current console.log('[usePointerLock] Pointer lock change event:', { isLocked, pointerLockElement: document.pointerLockElement, containerElement: containerRef.current, elementsMatch: document.pointerLockElement === containerRef.current, }) setPointerLocked(isLocked) // Call callbacks on state transitions const wasLocked = prevLockedRef.current if (isLocked && !wasLocked) { console.log('[usePointerLock] 🔒 Pointer lock ACQUIRED') onLockAcquired?.() } else if (!isLocked && wasLocked) { console.log('[usePointerLock] 🔓 Pointer lock RELEASED') onLockReleased?.() } prevLockedRef.current = isLocked } const handlePointerLockError = () => { console.error('[usePointerLock] ❌ Failed to acquire pointer lock') setPointerLocked(false) } document.addEventListener('pointerlockchange', handlePointerLockChange) document.addEventListener('pointerlockerror', handlePointerLockError) console.log('[usePointerLock] Event listeners attached') return () => { document.removeEventListener('pointerlockchange', handlePointerLockChange) document.removeEventListener('pointerlockerror', handlePointerLockError) console.log('[usePointerLock] Event listeners removed') } }, [containerRef, onLockAcquired, onLockReleased]) // Release pointer lock when component unmounts useEffect(() => { return () => { if (document.pointerLockElement) { console.log('[usePointerLock] Component unmounting - releasing pointer lock') document.exitPointerLock() } } }, []) const requestPointerLock = () => { // Don't request pointer lock if precision mode is not available if (!canUsePrecisionMode) { console.log('[usePointerLock] Precision mode not available - skipping pointer lock request') return } if (containerRef.current && !pointerLocked) { console.log('[usePointerLock] Requesting pointer lock') containerRef.current.requestPointerLock() } } const exitPointerLock = () => { if (pointerLocked) { console.log('[usePointerLock] Exiting pointer lock') document.exitPointerLock() } } return { pointerLocked, requestPointerLock, exitPointerLock, } } |