commit c063a5e974ce874b241f291266d63251c0b6a33f
parent 37451a3629aa7d7e9baed88a404fcee9ff003715
Author: Hunter
Date:   Mon, 20 Oct 2025 13:39:59 -0400

fix zoom-at-cursor when image is rotated

Diffstat:
Mscript.js | 26+++++++++++++++++---------
1 file changed, 17 insertions(+), 9 deletions(-)

diff --git a/script.js b/script.js @@ -458,15 +458,23 @@ function setupImageHandlers(imageData) { const newUserScale = Math.max(1, Math.min(5, oldUserScale * (1 + zoomDelta))); const newTotalScale = imageData.baseScale * newUserScale; - // Convert cursor position to pre-scale image space - const cursorXInImageSpace = cursorX / oldTotalScale; - const cursorYInImageSpace = cursorY / oldTotalScale; - - // Adjust pan to keep the point under cursor fixed during zoom - // Pan is in pre-scale image space, so we work entirely in that space - const scaleRatio = newTotalScale / oldTotalScale; - imageData.panX = cursorXInImageSpace * (1 - scaleRatio) + imageData.panX * scaleRatio; - imageData.panY = cursorYInImageSpace * (1 - scaleRatio) + imageData.panY * scaleRatio; + // Transform cursor position to account for rotation + // The CSS transform applies rotation before pan, so we need to rotate the cursor position + // into the image's coordinate system (inverse rotation) + const angle = -imageData.rotation * Math.PI / 180; // Negative for inverse rotation + const cos = Math.cos(angle); + const sin = Math.sin(angle); + const rotatedCursorX = cursorX * cos - cursorY * sin; + const rotatedCursorY = cursorX * sin + cursorY * cos; + + // The point under the cursor in pre-scale image space is: + // imagePoint = (cursorScreen / oldTotalScale) - panX + // We want: (imagePoint + newPanX) * newTotalScale = cursorScreen + // So: newPanX = (cursorScreen / newTotalScale) - imagePoint + // = (cursorScreen / newTotalScale) - (cursorScreen / oldTotalScale - panX) + // = cursorScreen * (1/newTotalScale - 1/oldTotalScale) + panX + imageData.panX = rotatedCursorX * (1/newTotalScale - 1/oldTotalScale) + imageData.panX; + imageData.panY = rotatedCursorY * (1/newTotalScale - 1/oldTotalScale) + imageData.panY; imageData.userScale = newUserScale; // Clamp after zooming to prevent whitespace