fix: prevent zoom jump when activating precision mode

**Problem:**
When clicking to activate precision mode, there was a huge instantaneous
jump in zoom level. This happened because the spring was targeting the
uncapped zoom value, then immediately jumped to it when precision mode
activated.

**Solution:**
- Cap the spring target (setTargetZoom) when not in precision mode
- When precision mode activates, the target updates to uncapped value
- Spring smoothly animates from capped to uncapped value
- No more instantaneous jump

**How it works:**
1. Not in precision mode: spring targets capped zoom at threshold
2. User clicks to activate precision mode
3. pointerLocked becomes true
4. Next mouse move updates target to uncapped zoom value
5. Spring smoothly animates from threshold to final zoom

**Removed:**
- Deferred capping in viewBox calculation (no longer needed)
- Now using simple zoom value directly in viewBox

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Thomas Hallock
2025-11-23 20:06:28 -06:00
parent b355a3fc8f
commit 9cb3c898ec

View File

@@ -1368,9 +1368,32 @@ export function MapRenderer({
)
}
// Don't cap the target zoom - let the spring animate to full target
// The capping will happen in the viewBox calculation where zoom is actually used
setTargetZoom(adaptiveZoom)
// Cap the target zoom when not in precision mode
// When precision mode activates, the uncapped target will be set
let targetZoomValue = adaptiveZoom
if (!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)) {
// Calculate what the screen pixel ratio would be at this zoom
const magnifiedViewBoxWidth = viewBoxWidth / adaptiveZoom
const magnifierScreenPixelsPerSvgUnit = magnifierWidth / magnifiedViewBoxWidth
const mainMapSvgUnitsPerScreenPixel = viewBoxWidth / svgRect.width
const screenPixelRatio = mainMapSvgUnitsPerScreenPixel * magnifierScreenPixelsPerSvgUnit
// If it exceeds threshold, cap the target zoom
if (screenPixelRatio > PRECISION_MODE_THRESHOLD) {
const maxZoom = PRECISION_MODE_THRESHOLD / (magnifierWidth / svgRect.width)
targetZoomValue = Math.min(adaptiveZoom, maxZoom)
}
}
}
setTargetZoom(targetZoomValue)
setShowMagnifier(true)
setTargetOpacity(1)
setTargetTop(newTop)
@@ -1852,29 +1875,9 @@ export function MapRenderer({
const cursorSvgX = (cursorPosition.x - svgOffsetX) * scaleX + viewBoxX
const cursorSvgY = (cursorPosition.y - svgOffsetY) * scaleY + viewBoxY
// Cap zoom if not in pointer lock mode to prevent excessive screen pixel ratios
// This creates a "pause" at the threshold while the spring continues to its target
let effectiveZoom = zoom
if (!pointerLocked) {
const magnifierWidth = containerRect.width * 0.5
// Calculate what the screen pixel ratio would be at this zoom
const magnifiedViewBoxWidth = viewBoxWidth / zoom
const magnifierScreenPixelsPerSvgUnit = magnifierWidth / magnifiedViewBoxWidth
const mainMapSvgUnitsPerScreenPixel = viewBoxWidth / svgRect.width
const screenPixelRatio =
mainMapSvgUnitsPerScreenPixel * magnifierScreenPixelsPerSvgUnit
// If it exceeds threshold, cap the zoom to stay at threshold
if (screenPixelRatio > PRECISION_MODE_THRESHOLD) {
// Solve for max zoom: ratio = zoom * (magnifierWidth / mainMapWidth)
const maxZoom = PRECISION_MODE_THRESHOLD / (magnifierWidth / svgRect.width)
effectiveZoom = Math.min(zoom, maxZoom)
}
}
// Magnified view: adaptive zoom (using capped value)
const magnifiedWidth = viewBoxWidth / effectiveZoom
const magnifiedHeight = viewBoxHeight / effectiveZoom
// Magnified view: use spring zoom value directly (capping happens at setTargetZoom)
const magnifiedWidth = viewBoxWidth / zoom
const magnifiedHeight = viewBoxHeight / zoom
// Center the magnified viewBox on the cursor
const magnifiedViewBoxX = cursorSvgX - magnifiedWidth / 2