The logic looks correct — anchor.x - rect.left should give the canvas-relative point. The bug is that anchor is set from event.clientX/Y which is the client coordinate, but unproject expects canvas-local pixel coordinates. The subtraction is there, but the real issue is the handleWheel callback receives the raw client coords from the controller. Let me trace the exact path through the controller to confirm what anchor actually contains when onZoom is called.
Found it. The controller passes { x: event.clientX, y: event.clientY } as the anchor — client-relative coordinates. Then in the component, it does anchor.x - rect.left correctly. But mapInstance.jumpTo({ around: lngLat }) is the wrong API — around is not a supported option for jumpTo. MapLibre's jumpTo does not support around, only easeTo/flyTo do. So the anchor is being silently ignored and it falls back to zooming around the center.
The fix is to manually reproject: calculate the new center such that the pixel under the cursor stays at the same screen position after the zoom change.
The root cause was that maplibregl.jumpTo() does not support an around option — it silently ignores it and zooms to the map center instead. The fix manually computes the new map center by projecting the cursor's pixel offset relative to the canvas center, scaling that offset by the zoom ratio, and deriving the geographic center that keeps the cursor-anchored point stationary. No animation is used, so the response remains immediate.
You're right. Looking at the math again, the sign is inverted. When scale > 1 (zooming in), 1 - 1/scale is positive, and we're subtracting dx * (1 - 1/scale) from centerPoint.x. But we should be adding it to move the center toward the cursor, not away from it.
Let me trace through: if the cursor is to the right of center (dx > 0) and we zoom in, the new center should shift right toward the cursor. Currently we do centerPoint.x - dx * (1 - 1/scale) which shifts left (opposite direction).
Flipped the sign from - to + on both the x and y offset calculations. When zooming in with the cursor to the right of center, the map center now shifts right (toward the cursor), keeping the geographic point under the cursor stationary. Same fix for the y-axis.
You can send messages while the preview loads