feat(know-your-world): Phase 2 - integrate useMagnifierZoom hook
Integrate the useMagnifierZoom hook into MapRenderer, completing Phase 2 of the refactoring plan. This extracts ~152 lines of zoom animation logic from the component into a reusable hook. **Changes:** Hook Updates: - Updated `useMagnifierZoom` to return spring object instead of `.get()` value - This allows MapRenderer to use `.to()` interpolation for animated values - Return type now `any` (spring object) instead of `number` MapRenderer Reductions: - Replaced ~101 line zoom effect with 7-line effect - Effect now only updates opacity/position/movementMultiplier - Zoom animation with pause/resume handled by hook - Removed all `magnifierSpring.zoom` references (11 occurrences) - Replaced with `zoomSpring` from hook or `getCurrentZoom()` calls - Added TypeScript type annotations for all zoom parameters **Line Count Impact:** - Before Phase 2: 2083 lines - After Phase 2: 1931 lines (-152 lines, -7.3%) - Combined total: 2430 → 1931 lines (-499 lines, -20.5% reduction) **Testing:** - TypeScript: No new errors (only pre-existing test errors) - Formatting: Passed - Linting: No new MapRenderer warnings **Files Modified:** - `hooks/useMagnifierZoom.ts` - Return spring object instead of number - `components/MapRenderer.tsx` - Integrate zoom hook, remove zoom effect **Next Steps:** - User testing of magnifier behavior - Verify zoom animation, pause/resume, capping - Verify pointer lock integration works correctly 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
fb55a1ce53
commit
8ce878d03e
|
|
@ -22,6 +22,7 @@ import {
|
||||||
import { findOptimalZoom } from '../utils/adaptiveZoomSearch'
|
import { findOptimalZoom } from '../utils/adaptiveZoomSearch'
|
||||||
import { useRegionDetection } from '../hooks/useRegionDetection'
|
import { useRegionDetection } from '../hooks/useRegionDetection'
|
||||||
import { usePointerLock } from '../hooks/usePointerLock'
|
import { usePointerLock } from '../hooks/usePointerLock'
|
||||||
|
import { useMagnifierZoom } from '../hooks/useMagnifierZoom'
|
||||||
|
|
||||||
// Debug flag: show technical info in magnifier (dev only)
|
// Debug flag: show technical info in magnifier (dev only)
|
||||||
const SHOW_MAGNIFIER_DEBUG_INFO = process.env.NODE_ENV === 'development'
|
const SHOW_MAGNIFIER_DEBUG_INFO = process.env.NODE_ENV === 'development'
|
||||||
|
|
@ -193,15 +194,13 @@ export function MapRenderer({
|
||||||
smallRegionAreaThreshold: 200,
|
smallRegionAreaThreshold: 200,
|
||||||
})
|
})
|
||||||
|
|
||||||
// State that needs to be available for pointer lock callbacks
|
// State that needs to be available for hooks
|
||||||
const [targetZoom, setTargetZoom] = useState(10)
|
|
||||||
const uncappedAdaptiveZoomRef = useRef<number | null>(null)
|
|
||||||
const cursorPositionRef = useRef<{ x: number; y: number } | null>(null)
|
const cursorPositionRef = useRef<{ x: number; y: number } | null>(null)
|
||||||
const initialCapturePositionRef = useRef<{ x: number; y: number } | null>(null)
|
const initialCapturePositionRef = useRef<{ x: number; y: number } | null>(null)
|
||||||
const [cursorSquish, setCursorSquish] = useState({ x: 1, y: 1 })
|
const [cursorSquish, setCursorSquish] = useState({ x: 1, y: 1 })
|
||||||
const [isReleasingPointerLock, setIsReleasingPointerLock] = useState(false)
|
const [isReleasingPointerLock, setIsReleasingPointerLock] = useState(false)
|
||||||
|
|
||||||
// Pointer lock hook
|
// Pointer lock hook (needed by zoom hook)
|
||||||
const { pointerLocked, requestPointerLock, exitPointerLock } = usePointerLock({
|
const { pointerLocked, requestPointerLock, exitPointerLock } = usePointerLock({
|
||||||
containerRef,
|
containerRef,
|
||||||
onLockAcquired: () => {
|
onLockAcquired: () => {
|
||||||
|
|
@ -213,76 +212,29 @@ export function MapRenderer({
|
||||||
initialCapturePositionRef.current
|
initialCapturePositionRef.current
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
// Note: Zoom update now handled by useMagnifierZoom hook
|
||||||
// Update target zoom to uncapped value
|
|
||||||
if (uncappedAdaptiveZoomRef.current !== null) {
|
|
||||||
console.log(
|
|
||||||
`[Pointer Lock] Updating target zoom to uncapped value: ${uncappedAdaptiveZoomRef.current.toFixed(1)}×`
|
|
||||||
)
|
|
||||||
setTargetZoom(uncappedAdaptiveZoomRef.current)
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
onLockReleased: () => {
|
onLockReleased: () => {
|
||||||
console.log('[Pointer Lock] 🔓 RELEASED - Starting cleanup and zoom recalculation')
|
console.log('[Pointer Lock] 🔓 RELEASED - Starting cleanup')
|
||||||
|
|
||||||
// Reset cursor squish
|
// Reset cursor squish
|
||||||
setCursorSquish({ x: 1, y: 1 })
|
setCursorSquish({ x: 1, y: 1 })
|
||||||
setIsReleasingPointerLock(false)
|
setIsReleasingPointerLock(false)
|
||||||
|
// Note: Zoom recalculation now handled by useMagnifierZoom hook
|
||||||
// Recalculate zoom with capping
|
|
||||||
if (uncappedAdaptiveZoomRef.current !== null && containerRef.current && svgRef.current) {
|
|
||||||
const containerRect = containerRef.current.getBoundingClientRect()
|
|
||||||
const svgRect = svgRef.current.getBoundingClientRect()
|
|
||||||
const magnifierWidth = containerRect.width * 0.5
|
|
||||||
const viewBoxParts = mapData.viewBox.split(' ').map(Number)
|
|
||||||
const viewBoxWidth = viewBoxParts[2]
|
|
||||||
|
|
||||||
if (viewBoxWidth && !Number.isNaN(viewBoxWidth)) {
|
|
||||||
const uncappedZoom = uncappedAdaptiveZoomRef.current
|
|
||||||
const screenPixelRatio = calculateScreenPixelRatio({
|
|
||||||
magnifierWidth,
|
|
||||||
viewBoxWidth,
|
|
||||||
svgWidth: svgRect.width,
|
|
||||||
zoom: uncappedZoom,
|
|
||||||
})
|
|
||||||
|
|
||||||
console.log('[Pointer Lock] Screen pixel ratio check:', {
|
|
||||||
uncappedZoom: uncappedZoom.toFixed(1),
|
|
||||||
screenPixelRatio: screenPixelRatio.toFixed(1),
|
|
||||||
threshold: PRECISION_MODE_THRESHOLD,
|
|
||||||
exceedsThreshold: isAboveThreshold(screenPixelRatio, PRECISION_MODE_THRESHOLD),
|
|
||||||
})
|
|
||||||
|
|
||||||
// If it exceeds threshold, cap the zoom to stay at threshold
|
|
||||||
if (isAboveThreshold(screenPixelRatio, PRECISION_MODE_THRESHOLD)) {
|
|
||||||
const maxZoom = calculateMaxZoomAtThreshold(
|
|
||||||
PRECISION_MODE_THRESHOLD,
|
|
||||||
magnifierWidth,
|
|
||||||
svgRect.width
|
|
||||||
)
|
|
||||||
const cappedZoom = Math.min(uncappedZoom, maxZoom)
|
|
||||||
console.log(
|
|
||||||
`[Pointer Lock] ✅ Capping zoom: ${uncappedZoom.toFixed(1)}× → ${cappedZoom.toFixed(1)}× (threshold: ${PRECISION_MODE_THRESHOLD} px/px)`
|
|
||||||
)
|
|
||||||
setTargetZoom(cappedZoom)
|
|
||||||
} else {
|
|
||||||
console.log(
|
|
||||||
`[Pointer Lock] ℹ️ No capping needed - zoom ${uncappedZoom.toFixed(1)}× is below threshold`
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
console.log('[Pointer Lock] ⚠️ Cannot recalculate zoom - invalid viewBoxWidth')
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
console.log('[Pointer Lock] ⚠️ Cannot recalculate zoom - missing refs:', {
|
|
||||||
hasContainer: !!containerRef.current,
|
|
||||||
hasSvg: !!svgRef.current,
|
|
||||||
hasUncappedZoom: uncappedAdaptiveZoomRef.current !== null,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Magnifier zoom hook
|
||||||
|
const { targetZoom, setTargetZoom, zoomSpring, getCurrentZoom, uncappedAdaptiveZoomRef } =
|
||||||
|
useMagnifierZoom({
|
||||||
|
containerRef,
|
||||||
|
svgRef,
|
||||||
|
viewBox: mapData.viewBox,
|
||||||
|
threshold: PRECISION_MODE_THRESHOLD,
|
||||||
|
pointerLocked,
|
||||||
|
initialZoom: 10,
|
||||||
|
})
|
||||||
|
|
||||||
const [svgDimensions, setSvgDimensions] = useState({ width: 1000, height: 500 })
|
const [svgDimensions, setSvgDimensions] = useState({ width: 1000, height: 500 })
|
||||||
const [cursorPosition, setCursorPosition] = useState<{ x: number; y: number } | null>(null)
|
const [cursorPosition, setCursorPosition] = useState<{ x: number; y: number } | null>(null)
|
||||||
const [showMagnifier, setShowMagnifier] = useState(false)
|
const [showMagnifier, setShowMagnifier] = useState(false)
|
||||||
|
|
@ -371,15 +323,10 @@ export function MapRenderer({
|
||||||
}
|
}
|
||||||
|
|
||||||
// Animated spring values for smooth transitions
|
// Animated spring values for smooth transitions
|
||||||
// Different fade speeds: fast fade-in (100ms), slow fade-out (1000ms)
|
// Note: Zoom animation is now handled by useMagnifierZoom hook
|
||||||
// Zoom: smooth, slower animation with gentle easing
|
// This spring only handles: opacity, position, and movement multiplier
|
||||||
// Position: medium speed (300ms)
|
|
||||||
// Movement multiplier: gradual transitions for smooth cursor dampening
|
|
||||||
const springRef = useSpringRef()
|
|
||||||
const [magnifierSpring, magnifierApi] = useSpring(
|
const [magnifierSpring, magnifierApi] = useSpring(
|
||||||
() => ({
|
() => ({
|
||||||
ref: springRef,
|
|
||||||
zoom: targetZoom,
|
|
||||||
opacity: targetOpacity,
|
opacity: targetOpacity,
|
||||||
top: targetTop,
|
top: targetTop,
|
||||||
left: targetLeft,
|
left: targetLeft,
|
||||||
|
|
@ -390,126 +337,27 @@ export function MapRenderer({
|
||||||
? { duration: 100 } // Fade in: 0.1 seconds
|
? { duration: 100 } // Fade in: 0.1 seconds
|
||||||
: { duration: 1000 } // Fade out: 1 second
|
: { duration: 1000 } // Fade out: 1 second
|
||||||
}
|
}
|
||||||
if (key === 'zoom') {
|
|
||||||
// Zoom: very slow, smooth animation (4x longer than before)
|
|
||||||
// Lower tension + higher mass = longer, more gradual transitions
|
|
||||||
return { tension: 30, friction: 30, mass: 4 }
|
|
||||||
}
|
|
||||||
if (key === 'movementMultiplier') {
|
if (key === 'movementMultiplier') {
|
||||||
// Movement multiplier: smooth but responsive transitions
|
// Movement multiplier: smooth but responsive transitions
|
||||||
// Faster than zoom so cursor responsiveness changes quickly but not jarring
|
|
||||||
return { tension: 180, friction: 26 }
|
return { tension: 180, friction: 26 }
|
||||||
}
|
}
|
||||||
// Position: medium speed
|
// Position: medium speed
|
||||||
return { tension: 200, friction: 25 }
|
return { tension: 200, friction: 25 }
|
||||||
},
|
},
|
||||||
// onChange removed - was flooding console with animation frames
|
|
||||||
}),
|
}),
|
||||||
[]
|
[targetOpacity, targetTop, targetLeft, smallestRegionSize]
|
||||||
)
|
)
|
||||||
|
|
||||||
// Update spring values when targets change
|
// Note: Zoom animation with pause/resume is now handled by useMagnifierZoom hook
|
||||||
// Handle pausing zoom animation when hitting threshold
|
// This effect only updates the remaining spring properties: opacity, position, movement multiplier
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const currentZoom = magnifierSpring.zoom.get()
|
magnifierApi.start({
|
||||||
const zoomIsAnimating = Math.abs(currentZoom - targetZoom) > 0.01
|
opacity: targetOpacity,
|
||||||
|
top: targetTop,
|
||||||
console.log('[Zoom Effect] Running with state:', {
|
left: targetLeft,
|
||||||
currentZoom: currentZoom.toFixed(1),
|
movementMultiplier: getMovementMultiplier(smallestRegionSize),
|
||||||
targetZoom: targetZoom.toFixed(1),
|
|
||||||
zoomIsAnimating,
|
|
||||||
pointerLocked,
|
|
||||||
})
|
})
|
||||||
|
}, [targetOpacity, targetTop, targetLeft, smallestRegionSize, magnifierApi])
|
||||||
// Check if CURRENT zoom is at/above the threshold (zoom is capped)
|
|
||||||
const currentIsAtThreshold =
|
|
||||||
!pointerLocked &&
|
|
||||||
containerRef.current &&
|
|
||||||
svgRef.current &&
|
|
||||||
(() => {
|
|
||||||
const containerRect = containerRef.current.getBoundingClientRect()
|
|
||||||
const svgRect = svgRef.current.getBoundingClientRect()
|
|
||||||
const magnifierWidth = containerRect.width * 0.5
|
|
||||||
const viewBoxParts = mapData.viewBox.split(' ').map(Number)
|
|
||||||
const viewBoxWidth = viewBoxParts[2]
|
|
||||||
|
|
||||||
if (!viewBoxWidth || Number.isNaN(viewBoxWidth)) return false
|
|
||||||
|
|
||||||
const screenPixelRatio = calculateScreenPixelRatio({
|
|
||||||
magnifierWidth,
|
|
||||||
viewBoxWidth,
|
|
||||||
svgWidth: svgRect.width,
|
|
||||||
zoom: currentZoom,
|
|
||||||
})
|
|
||||||
|
|
||||||
return isAboveThreshold(screenPixelRatio, PRECISION_MODE_THRESHOLD)
|
|
||||||
})()
|
|
||||||
|
|
||||||
// Check if TARGET zoom would be at/above the threshold
|
|
||||||
const targetIsAtThreshold =
|
|
||||||
!pointerLocked &&
|
|
||||||
containerRef.current &&
|
|
||||||
svgRef.current &&
|
|
||||||
(() => {
|
|
||||||
const containerRect = containerRef.current.getBoundingClientRect()
|
|
||||||
const svgRect = svgRef.current.getBoundingClientRect()
|
|
||||||
const magnifierWidth = containerRect.width * 0.5
|
|
||||||
const viewBoxParts = mapData.viewBox.split(' ').map(Number)
|
|
||||||
const viewBoxWidth = viewBoxParts[2]
|
|
||||||
|
|
||||||
if (!viewBoxWidth || Number.isNaN(viewBoxWidth)) return false
|
|
||||||
|
|
||||||
const screenPixelRatio = calculateScreenPixelRatio({
|
|
||||||
magnifierWidth,
|
|
||||||
viewBoxWidth,
|
|
||||||
svgWidth: svgRect.width,
|
|
||||||
zoom: targetZoom,
|
|
||||||
})
|
|
||||||
|
|
||||||
return isAboveThreshold(screenPixelRatio, PRECISION_MODE_THRESHOLD)
|
|
||||||
})()
|
|
||||||
|
|
||||||
console.log('[Zoom Effect] Threshold checks:', {
|
|
||||||
currentIsAtThreshold,
|
|
||||||
targetIsAtThreshold,
|
|
||||||
shouldPause: currentIsAtThreshold && zoomIsAnimating && targetIsAtThreshold,
|
|
||||||
})
|
|
||||||
|
|
||||||
// Pause conditions:
|
|
||||||
// 1. Currently at threshold AND animating toward even higher zoom (would exceed threshold more)
|
|
||||||
// 2. OR: Currently at threshold and target is also at threshold (should stay paused)
|
|
||||||
const shouldPause = currentIsAtThreshold && zoomIsAnimating && targetIsAtThreshold
|
|
||||||
|
|
||||||
if (shouldPause) {
|
|
||||||
// Pause the zoom animation - we're waiting for precision mode
|
|
||||||
console.log('[Zoom] ⏸️ Pausing at threshold - waiting for precision mode')
|
|
||||||
magnifierApi.pause()
|
|
||||||
} else {
|
|
||||||
// Update spring values and ensure it's not paused
|
|
||||||
// This will resume if we were paused and now target is below threshold (zooming out)
|
|
||||||
if (currentIsAtThreshold && !targetIsAtThreshold) {
|
|
||||||
console.log('[Zoom] ▶️ Resuming - target zoom is below threshold (zooming out)')
|
|
||||||
}
|
|
||||||
console.log('[Zoom] 🎬 Starting/updating animation to targetZoom:', targetZoom.toFixed(1))
|
|
||||||
magnifierApi.start({
|
|
||||||
zoom: targetZoom,
|
|
||||||
opacity: targetOpacity,
|
|
||||||
top: targetTop,
|
|
||||||
left: targetLeft,
|
|
||||||
movementMultiplier: getMovementMultiplier(smallestRegionSize),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}, [
|
|
||||||
targetZoom,
|
|
||||||
targetOpacity,
|
|
||||||
targetTop,
|
|
||||||
targetLeft,
|
|
||||||
smallestRegionSize,
|
|
||||||
pointerLocked,
|
|
||||||
mapData.viewBox,
|
|
||||||
magnifierApi,
|
|
||||||
magnifierSpring.zoom,
|
|
||||||
])
|
|
||||||
|
|
||||||
const [labelPositions, setLabelPositions] = useState<RegionLabelPosition[]>([])
|
const [labelPositions, setLabelPositions] = useState<RegionLabelPosition[]>([])
|
||||||
const [smallRegionLabelPositions, setSmallRegionLabelPositions] = useState<
|
const [smallRegionLabelPositions, setSmallRegionLabelPositions] = useState<
|
||||||
|
|
@ -1438,7 +1286,7 @@ export function MapRenderer({
|
||||||
{/* Magnifier region indicator on main map */}
|
{/* Magnifier region indicator on main map */}
|
||||||
{showMagnifier && cursorPosition && svgRef.current && containerRef.current && (
|
{showMagnifier && cursorPosition && svgRef.current && containerRef.current && (
|
||||||
<animated.rect
|
<animated.rect
|
||||||
x={magnifierSpring.zoom.to((zoom) => {
|
x={zoomSpring.to((zoom: number) => {
|
||||||
const containerRect = containerRef.current!.getBoundingClientRect()
|
const containerRect = containerRef.current!.getBoundingClientRect()
|
||||||
const svgRect = svgRef.current!.getBoundingClientRect()
|
const svgRect = svgRef.current!.getBoundingClientRect()
|
||||||
const viewBoxParts = mapData.viewBox.split(' ').map(Number)
|
const viewBoxParts = mapData.viewBox.split(' ').map(Number)
|
||||||
|
|
@ -1454,7 +1302,7 @@ export function MapRenderer({
|
||||||
const magnifiedWidth = viewBoxWidth / zoom
|
const magnifiedWidth = viewBoxWidth / zoom
|
||||||
return cursorSvgX - magnifiedWidth / 2
|
return cursorSvgX - magnifiedWidth / 2
|
||||||
})}
|
})}
|
||||||
y={magnifierSpring.zoom.to((zoom) => {
|
y={zoomSpring.to((zoom: number) => {
|
||||||
const containerRect = containerRef.current!.getBoundingClientRect()
|
const containerRect = containerRef.current!.getBoundingClientRect()
|
||||||
const svgRect = svgRef.current!.getBoundingClientRect()
|
const svgRect = svgRef.current!.getBoundingClientRect()
|
||||||
const viewBoxParts = mapData.viewBox.split(' ').map(Number)
|
const viewBoxParts = mapData.viewBox.split(' ').map(Number)
|
||||||
|
|
@ -1470,12 +1318,12 @@ export function MapRenderer({
|
||||||
const magnifiedHeight = viewBoxHeight / zoom
|
const magnifiedHeight = viewBoxHeight / zoom
|
||||||
return cursorSvgY - magnifiedHeight / 2
|
return cursorSvgY - magnifiedHeight / 2
|
||||||
})}
|
})}
|
||||||
width={magnifierSpring.zoom.to((zoom) => {
|
width={zoomSpring.to((zoom: number) => {
|
||||||
const viewBoxParts = mapData.viewBox.split(' ').map(Number)
|
const viewBoxParts = mapData.viewBox.split(' ').map(Number)
|
||||||
const viewBoxWidth = viewBoxParts[2] || 1000
|
const viewBoxWidth = viewBoxParts[2] || 1000
|
||||||
return viewBoxWidth / zoom
|
return viewBoxWidth / zoom
|
||||||
})}
|
})}
|
||||||
height={magnifierSpring.zoom.to((zoom) => {
|
height={zoomSpring.to((zoom: number) => {
|
||||||
const viewBoxParts = mapData.viewBox.split(' ').map(Number)
|
const viewBoxParts = mapData.viewBox.split(' ').map(Number)
|
||||||
const viewBoxHeight = viewBoxParts[3] || 1000
|
const viewBoxHeight = viewBoxParts[3] || 1000
|
||||||
return viewBoxHeight / zoom
|
return viewBoxHeight / zoom
|
||||||
|
|
@ -1694,8 +1542,8 @@ export function MapRenderer({
|
||||||
width: '50%',
|
width: '50%',
|
||||||
aspectRatio: '2/1',
|
aspectRatio: '2/1',
|
||||||
// High zoom (>60x) gets gold border, normal zoom gets blue border
|
// High zoom (>60x) gets gold border, normal zoom gets blue border
|
||||||
border: magnifierSpring.zoom.to(
|
border: zoomSpring.to(
|
||||||
(zoom) =>
|
(zoom: number) =>
|
||||||
zoom > HIGH_ZOOM_THRESHOLD
|
zoom > HIGH_ZOOM_THRESHOLD
|
||||||
? `4px solid ${isDark ? '#fbbf24' : '#f59e0b'}` // gold-400/gold-500
|
? `4px solid ${isDark ? '#fbbf24' : '#f59e0b'}` // gold-400/gold-500
|
||||||
: `3px solid ${isDark ? '#60a5fa' : '#3b82f6'}` // blue-400/blue-600
|
: `3px solid ${isDark ? '#60a5fa' : '#3b82f6'}` // blue-400/blue-600
|
||||||
|
|
@ -1704,7 +1552,7 @@ export function MapRenderer({
|
||||||
overflow: 'hidden',
|
overflow: 'hidden',
|
||||||
pointerEvents: 'none',
|
pointerEvents: 'none',
|
||||||
zIndex: 100,
|
zIndex: 100,
|
||||||
boxShadow: magnifierSpring.zoom.to((zoom) =>
|
boxShadow: zoomSpring.to((zoom: number) =>
|
||||||
zoom > HIGH_ZOOM_THRESHOLD
|
zoom > HIGH_ZOOM_THRESHOLD
|
||||||
? '0 10px 40px rgba(251, 191, 36, 0.4), 0 0 20px rgba(251, 191, 36, 0.2)' // Gold glow
|
? '0 10px 40px rgba(251, 191, 36, 0.4), 0 0 20px rgba(251, 191, 36, 0.2)' // Gold glow
|
||||||
: '0 10px 40px rgba(0, 0, 0, 0.5)'
|
: '0 10px 40px rgba(0, 0, 0, 0.5)'
|
||||||
|
|
@ -1714,7 +1562,7 @@ export function MapRenderer({
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<animated.svg
|
<animated.svg
|
||||||
viewBox={magnifierSpring.zoom.to((zoom) => {
|
viewBox={zoomSpring.to((zoom: number) => {
|
||||||
// Calculate magnified viewBox centered on cursor
|
// Calculate magnified viewBox centered on cursor
|
||||||
const containerRect = containerRef.current!.getBoundingClientRect()
|
const containerRect = containerRef.current!.getBoundingClientRect()
|
||||||
const svgRect = svgRef.current!.getBoundingClientRect()
|
const svgRect = svgRef.current!.getBoundingClientRect()
|
||||||
|
|
@ -1761,7 +1609,7 @@ export function MapRenderer({
|
||||||
const viewBoxWidth = viewBoxParts[2]
|
const viewBoxWidth = viewBoxParts[2]
|
||||||
if (!viewBoxWidth || Number.isNaN(viewBoxWidth)) return 'none'
|
if (!viewBoxWidth || Number.isNaN(viewBoxWidth)) return 'none'
|
||||||
|
|
||||||
const currentZoom = magnifierSpring.zoom.get()
|
const currentZoom = getCurrentZoom()
|
||||||
const screenPixelRatio = calculateScreenPixelRatio({
|
const screenPixelRatio = calculateScreenPixelRatio({
|
||||||
magnifierWidth,
|
magnifierWidth,
|
||||||
viewBoxWidth,
|
viewBoxWidth,
|
||||||
|
|
@ -1866,7 +1714,7 @@ export function MapRenderer({
|
||||||
|
|
||||||
if (!viewBoxWidth || Number.isNaN(viewBoxWidth)) return null
|
if (!viewBoxWidth || Number.isNaN(viewBoxWidth)) return null
|
||||||
|
|
||||||
const currentZoom = magnifierSpring.zoom.get()
|
const currentZoom = getCurrentZoom()
|
||||||
const screenPixelRatio = calculateScreenPixelRatio({
|
const screenPixelRatio = calculateScreenPixelRatio({
|
||||||
magnifierWidth,
|
magnifierWidth,
|
||||||
viewBoxWidth,
|
viewBoxWidth,
|
||||||
|
|
@ -1996,7 +1844,7 @@ export function MapRenderer({
|
||||||
}}
|
}}
|
||||||
data-element="magnifier-label"
|
data-element="magnifier-label"
|
||||||
>
|
>
|
||||||
{magnifierSpring.zoom.to((z) => {
|
{zoomSpring.to((z: number) => {
|
||||||
const multiplier = magnifierSpring.movementMultiplier.get()
|
const multiplier = magnifierSpring.movementMultiplier.get()
|
||||||
|
|
||||||
// When in pointer lock mode, show "Precision mode active" notice
|
// When in pointer lock mode, show "Precision mode active" notice
|
||||||
|
|
@ -2052,7 +1900,7 @@ export function MapRenderer({
|
||||||
const viewBoxWidth = viewBoxParts[2]
|
const viewBoxWidth = viewBoxParts[2]
|
||||||
if (!viewBoxWidth || Number.isNaN(viewBoxWidth)) return null
|
if (!viewBoxWidth || Number.isNaN(viewBoxWidth)) return null
|
||||||
|
|
||||||
const currentZoom = magnifierSpring.zoom.get()
|
const currentZoom = getCurrentZoom()
|
||||||
const screenPixelRatio = calculateScreenPixelRatio({
|
const screenPixelRatio = calculateScreenPixelRatio({
|
||||||
magnifierWidth,
|
magnifierWidth,
|
||||||
viewBoxWidth,
|
viewBoxWidth,
|
||||||
|
|
|
||||||
|
|
@ -34,8 +34,8 @@ export interface UseMagnifierZoomReturn {
|
||||||
targetZoom: number
|
targetZoom: number
|
||||||
/** Set the target zoom level */
|
/** Set the target zoom level */
|
||||||
setTargetZoom: (zoom: number) => void
|
setTargetZoom: (zoom: number) => void
|
||||||
/** The animated spring value for zoom */
|
/** The animated spring value for zoom (spring object, not a number) */
|
||||||
zoomSpring: number
|
zoomSpring: any // Spring value that can be used with animated.div
|
||||||
/** Get the current animated zoom value */
|
/** Get the current animated zoom value */
|
||||||
getCurrentZoom: () => number
|
getCurrentZoom: () => number
|
||||||
/** Reference to the uncapped adaptive zoom (for pointer lock transitions) */
|
/** Reference to the uncapped adaptive zoom (for pointer lock transitions) */
|
||||||
|
|
@ -233,7 +233,7 @@ export function useMagnifierZoom(options: UseMagnifierZoomOptions): UseMagnifier
|
||||||
return {
|
return {
|
||||||
targetZoom,
|
targetZoom,
|
||||||
setTargetZoom,
|
setTargetZoom,
|
||||||
zoomSpring: magnifierSpring.zoom.get(),
|
zoomSpring: magnifierSpring.zoom, // Return the spring object, not .get()
|
||||||
getCurrentZoom: () => magnifierSpring.zoom.get(),
|
getCurrentZoom: () => magnifierSpring.zoom.get(),
|
||||||
uncappedAdaptiveZoomRef,
|
uncappedAdaptiveZoomRef,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue