From e73b59d510863338181338a3d2439a414a2ec15a Mon Sep 17 00:00:00 2001 From: Thomas Hallock Date: Sun, 23 Nov 2025 20:58:24 -0600 Subject: [PATCH] fix: resume zoom animation when target drops below threshold MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixed issue where zoom animation would stay paused even when auto-zoom wanted to zoom back out below the precision mode threshold. **Problem:** - Animation paused when hitting 20 px/px threshold - If user moved to larger region, target zoom would drop - But animation stayed paused, not resuming to zoom out **Solution:** - Check both CURRENT zoom and TARGET zoom thresholds - Only pause if: current at threshold AND target also at/above threshold - Resume if: current at threshold BUT target below threshold (zooming out) **Pause conditions:** - `shouldPause = currentIsAtThreshold && zoomIsAnimating && targetIsAtThreshold` - If current is at threshold but target is safe → resume and zoom out - If current is at threshold and target also at threshold → stay paused - Added console log when resuming due to zoom-out **User experience:** - Zoom pauses at threshold when trying to zoom IN further - Zoom resumes smoothly when auto-zoom wants to zoom OUT - No stuck animations - always responsive to target changes 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../components/MapRenderer.tsx | 38 +++++++++++++++++-- 1 file changed, 34 insertions(+), 4 deletions(-) diff --git a/apps/web/src/arcade-games/know-your-world/components/MapRenderer.tsx b/apps/web/src/arcade-games/know-your-world/components/MapRenderer.tsx index 38a11f31..ff36bb32 100644 --- a/apps/web/src/arcade-games/know-your-world/components/MapRenderer.tsx +++ b/apps/web/src/arcade-games/know-your-world/components/MapRenderer.tsx @@ -380,12 +380,11 @@ export function MapRenderer({ const currentZoom = magnifierSpring.zoom.get() const zoomIsAnimating = Math.abs(currentZoom - targetZoom) > 0.01 - // Check if we're at the threshold (zoom is capped) - const isAtThreshold = + // Check if CURRENT zoom is at/above the threshold (zoom is capped) + const currentIsAtThreshold = !pointerLocked && containerRef.current && svgRef.current && - zoomIsAnimating && (() => { const containerRect = containerRef.current.getBoundingClientRect() const svgRect = svgRef.current.getBoundingClientRect() @@ -403,12 +402,43 @@ export function MapRenderer({ return screenPixelRatio >= PRECISION_MODE_THRESHOLD })() - if (isAtThreshold) { + // 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 || isNaN(viewBoxWidth)) return false + + const magnifiedViewBoxWidth = viewBoxWidth / targetZoom + const magnifierScreenPixelsPerSvgUnit = magnifierWidth / magnifiedViewBoxWidth + const mainMapSvgUnitsPerScreenPixel = viewBoxWidth / svgRect.width + const screenPixelRatio = mainMapSvgUnitsPerScreenPixel * magnifierScreenPixelsPerSvgUnit + + return screenPixelRatio >= PRECISION_MODE_THRESHOLD + })() + + // 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)') + } magnifierApi.start({ zoom: targetZoom, opacity: targetOpacity,