commit fd33863fdd93fac480711a5eab7252c5aac268e9
parent c70ab1274ba9a9ae4b1270a574937edbd97b518d
Author: Hunter
Date:   Wed, 22 Oct 2025 14:45:57 -0400

refactor/clean up

Diffstat:
Mscript.js | 214++++++++++++++++++++++++++++++-------------------------------------------------
1 file changed, 81 insertions(+), 133 deletions(-)

diff --git a/script.js b/script.js @@ -253,6 +253,33 @@ function clampPan(imageData) { imageData.panY = Math.max(-maxPanY, Math.min(maxPanY, imageData.panY)); } +// Helper to transform screen-space coordinates to image coordinate space (accounting for rotation) +function rotatePoint(x, y, angleDegrees) { + const angle = -angleDegrees * Math.PI / 180; + const cos = Math.cos(angle); + const sin = Math.sin(angle); + return { + x: x * cos - y * sin, + y: x * sin + y * cos + }; +} + +// Helper to apply pan adjustment based on screen delta +function applyPanDelta(imageData, screenDeltaX, screenDeltaY) { + const rotated = rotatePoint(screenDeltaX, screenDeltaY, imageData.rotation); + const totalScale = imageData.baseScale * imageData.userScale; + imageData.panX += rotated.x / totalScale; + imageData.panY += rotated.y / totalScale; +} + +// Helper to calculate zoom-centered pan adjustment +function adjustPanForZoom(imageData, cursorX, cursorY, oldTotalScale, newTotalScale) { + const rotated = rotatePoint(cursorX, cursorY, imageData.rotation); + const scaleDiff = 1/newTotalScale - 1/oldTotalScale; + imageData.panX += rotated.x * scaleDiff; + imageData.panY += rotated.y * scaleDiff; +} + function updateImagePosition(img) { const cellSize = getCellSize(); img.container.style.left = img.xCell * cellSize.width + 'px'; @@ -328,10 +355,7 @@ function setupImageHandlers(imageData) { // Unified pointer start handler function handlePointerStart(clientX, clientY, isTouch = false) { // Clear any existing timers from other images - if (longPressTimer) { - clearTimeout(longPressTimer); - longPressTimer = null; - } + clearDragState(); // Lock cursor to move during drag operation document.body.style.cursor = 'move'; @@ -380,17 +404,8 @@ function setupImageHandlers(imageData) { // Two fingers - prepare for pinch/pan e.preventDefault(); - // Cancel any ongoing drag and long press timer - if (longPressTimer) { - clearTimeout(longPressTimer); - longPressTimer = null; - } - if (dragState) { - dragState.image.container.classList.remove('dragging'); - dragState = null; - document.body.style.cursor = ''; - document.body.classList.remove('dragging'); - } + // Cancel any ongoing drag + clearDragState(); const touch1 = e.touches[0]; const touch2 = e.touches[1]; @@ -453,16 +468,10 @@ function setupImageHandlers(imageData) { const newUserScale = Math.max(1, Math.min(5, touchState.initialScale * scaleFactor)); const newTotalScale = imageData.baseScale * newUserScale; - // Transform cursor position to account for rotation - const angle = -imageData.rotation * Math.PI / 180; - const cos = Math.cos(angle); - const sin = Math.sin(angle); - const rotatedCursorX = cursorX * cos - cursorY * sin; - const rotatedCursorY = cursorX * sin + cursorY * cos; - - // Calculate new pan to keep zoom centered on pinch point - imageData.panX = rotatedCursorX * (1/newTotalScale - 1/oldTotalScale) + touchState.lastPanX; - imageData.panY = rotatedCursorY * (1/newTotalScale - 1/oldTotalScale) + touchState.lastPanY; + // Restore previous pan state and apply zoom adjustment + imageData.panX = touchState.lastPanX; + imageData.panY = touchState.lastPanY; + adjustPanForZoom(imageData, cursorX, cursorY, oldTotalScale, newTotalScale); imageData.userScale = newUserScale; clampPan(imageData); @@ -470,21 +479,9 @@ function setupImageHandlers(imageData) { touchState.lastPanY = imageData.panY; } else { // Two-finger pan - const deltaX = centerMoveX; - const deltaY = centerMoveY; - - // Transform deltas to account for image rotation - const angle = -imageData.rotation * Math.PI / 180; - const cos = Math.cos(angle); - const sin = Math.sin(angle); - const rotatedDeltaX = deltaX * cos - deltaY * sin; - const rotatedDeltaY = deltaX * sin + deltaY * cos; - - // Convert screen-space delta to pre-scale image space - // Note: positive delta means moving fingers right/down, which should move image right/down - const totalScale = imageData.baseScale * imageData.userScale; - imageData.panX = touchState.lastPanX + rotatedDeltaX / totalScale; - imageData.panY = touchState.lastPanY + rotatedDeltaY / totalScale; + imageData.panX = touchState.lastPanX; + imageData.panY = touchState.lastPanY; + applyPanDelta(imageData, centerMoveX, centerMoveY); clampPan(imageData); touchState.lastPanX = imageData.panX; @@ -622,49 +619,39 @@ function setupImageHandlers(imageData) { handlePointerStart(e.clientX, e.clientY); }); - // Resizing + // Resizing - unified handler for mouse and touch + function startResize(clientX, clientY, direction, cursorStyle = null) { + if (cursorStyle) { + document.body.style.cursor = cursorStyle; + } + document.body.classList.add('resizing'); + + resizeState = { + image: imageData, + direction: direction, + startX: clientX, + startY: clientY, + startXCell: imageData.xCell, + startYCell: imageData.yCell, + startWidthCells: imageData.widthCells, + startHeightCells: imageData.heightCells + }; + container.classList.add('resizing'); + } + container.querySelectorAll('.resize-handle').forEach(handle => { handle.addEventListener('mousedown', (e) => { e.preventDefault(); e.stopPropagation(); - - // Get the cursor style from the handle and apply it to the body const cursorStyle = window.getComputedStyle(handle).cursor; - document.body.style.cursor = cursorStyle; - document.body.classList.add('resizing'); - - resizeState = { - image: imageData, - direction: handle.dataset.direction, - startX: e.clientX, - startY: e.clientY, - startXCell: imageData.xCell, - startYCell: imageData.yCell, - startWidthCells: imageData.widthCells, - startHeightCells: imageData.heightCells - }; - container.classList.add('resizing'); + startResize(e.clientX, e.clientY, handle.dataset.direction, cursorStyle); }); - // Touch support for resize handles handle.addEventListener('touchstart', (e) => { e.preventDefault(); e.stopPropagation(); - const touch = e.touches[0]; - document.body.classList.add('resizing'); - - resizeState = { - image: imageData, - direction: handle.dataset.direction, - startX: touch.clientX, - startY: touch.clientY, - startXCell: imageData.xCell, - startYCell: imageData.yCell, - startWidthCells: imageData.widthCells, - startHeightCells: imageData.heightCells - }; - container.classList.add('resizing'); + startResize(touch.clientX, touch.clientY, handle.dataset.direction); }, { passive: false }); }); @@ -680,8 +667,8 @@ function setupImageHandlers(imageData) { if (e.ctrlKey) { // Zoom at cursor position const rect = container.getBoundingClientRect(); - const cursorX = e.clientX - rect.left - rect.width / 2; // Screen pixels from center - const cursorY = e.clientY - rect.top - rect.height / 2; // Screen pixels from center + const cursorX = e.clientX - rect.left - rect.width / 2; + const cursorY = e.clientY - rect.top - rect.height / 2; const oldUserScale = imageData.userScale; const oldTotalScale = imageData.baseScale * oldUserScale; @@ -689,44 +676,13 @@ function setupImageHandlers(imageData) { const newUserScale = Math.max(1, Math.min(5, oldUserScale * (1 + zoomDelta))); const newTotalScale = imageData.baseScale * newUserScale; - // 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; + adjustPanForZoom(imageData, cursorX, cursorY, oldTotalScale, newTotalScale); imageData.userScale = newUserScale; - // Clamp after zooming to prevent whitespace clampPan(imageData); } else { // Pan (two-finger scroll on macOS trackpad) - // Transform deltas to account for image rotation - const angle = -imageData.rotation * Math.PI / 180; // Negative because we want screen-to-image transform - const cos = Math.cos(angle); - const sin = Math.sin(angle); - - // Rotate the delta vector by the negative of the image rotation - const rotatedDeltaX = e.deltaX * cos - e.deltaY * sin; - const rotatedDeltaY = e.deltaX * sin + e.deltaY * cos; - - // Convert screen-space delta to pre-scale image space - const totalScale = imageData.baseScale * imageData.userScale; - imageData.panX -= rotatedDeltaX / totalScale; - imageData.panY -= rotatedDeltaY / totalScale; - - // Clamp pan to prevent whitespace + applyPanDelta(imageData, -e.deltaX, -e.deltaY); clampPan(imageData); } @@ -734,6 +690,20 @@ function setupImageHandlers(imageData) { }, { passive: false }); } +// Helper to clear drag state and timers +function clearDragState() { + if (longPressTimer) { + clearTimeout(longPressTimer); + longPressTimer = null; + } + if (dragState) { + dragState.image.container.classList.remove('dragging'); + dragState = null; + document.body.style.cursor = ''; + document.body.classList.remove('dragging'); + } +} + function handleMove(clientX, clientY) { if (dragState) { const dx = clientX - dragState.startX; @@ -742,19 +712,9 @@ function handleMove(clientX, clientY) { if (dragState.isPanMode) { // Pan mode - move the image within its container const imageData = dragState.image; - - // Transform deltas to account for image rotation - const angle = -imageData.rotation * Math.PI / 180; - const cos = Math.cos(angle); - const sin = Math.sin(angle); - const rotatedDeltaX = dx * cos - dy * sin; - const rotatedDeltaY = dx * sin + dy * cos; - - // Convert screen-space delta to pre-scale image space - const totalScale = imageData.baseScale * imageData.userScale; - imageData.panX = dragState.initialPanX + rotatedDeltaX / totalScale; - imageData.panY = dragState.initialPanY + rotatedDeltaY / totalScale; - + imageData.panX = dragState.initialPanX; + imageData.panY = dragState.initialPanY; + applyPanDelta(imageData, dx, dy); clampPan(imageData); updateImagePosition(imageData); } else { @@ -831,22 +791,10 @@ function handleMove(clientX, clientY) { } function handleEnd() { - if (longPressTimer) { - clearTimeout(longPressTimer); - longPressTimer = null; - } - if (dragState) { - dragState.image.container.classList.remove('dragging'); - dragState.image.container.style.opacity = ''; - dragState = null; - // Restore the default cursor - document.body.style.cursor = ''; - document.body.classList.remove('dragging'); - } + clearDragState(); if (resizeState) { resizeState.image.container.classList.remove('resizing'); resizeState = null; - // Restore the default cursor document.body.style.cursor = ''; document.body.classList.remove('resizing'); }