From 293bed913598d11027837c2b354d76e144f85efc Mon Sep 17 00:00:00 2001 From: Dennis Postma Date: Fri, 4 Apr 2025 03:55:20 +0200 Subject: [PATCH] Fixes --- src/components/PreviewModal.vue | 179 ++++++++++++------------- src/composables/useSpritesheetStore.ts | 5 +- 2 files changed, 92 insertions(+), 92 deletions(-) diff --git a/src/components/PreviewModal.vue b/src/components/PreviewModal.vue index f3815ff..ab60efe 100644 --- a/src/components/PreviewModal.vue +++ b/src/components/PreviewModal.vue @@ -57,15 +57,7 @@ - + @@ -78,23 +70,17 @@ -
-
- Sprite offset: {{ Math.round(spriteOffset.x) }}px, {{ Math.round(spriteOffset.y) }}px - Click and drag the sprite to move it within the cell +
+ Position: {{ Math.round(spriteOffset.x) }}px, {{ Math.round(spriteOffset.y) }}px (drag to move within cell)
{ - store.applyOffsetsToMainView(); - store.showNotification('Offset permanently applied to sprite position'); - }; + // applyOffsetsToMainView function removed const handleKeyDown = (e: KeyboardEvent) => { if (!isModalOpen.value) return; @@ -245,33 +228,17 @@ resetSpritePosition(); } else { // R: Reset both sprite position and viewport - // Reset the sprite offset for the current frame - const frameOffset = store.getSpriteOffset(currentFrame.value); - frameOffset.x = 0; - frameOffset.y = 0; - store.currentSpriteOffset.x = 0; - store.currentSpriteOffset.y = 0; - viewportOffset.value = { x: 0, y: 0 }; + resetSpritePosition(); updateFrame(); store.showNotification('View and position reset'); } e.preventDefault(); - } else if (previewZoom.value > 1) { - // Arrow key navigation for panning when zoomed in - const panAmount = 10; - if (e.key === 'ArrowUp') { - viewportOffset.value.y += panAmount / previewZoom.value; - e.preventDefault(); - } else if (e.key === 'ArrowDown') { - viewportOffset.value.y -= panAmount / previewZoom.value; - e.preventDefault(); - } else if (e.key === 'ArrowLeft' && animation.value.isPlaying) { - viewportOffset.value.x += panAmount / previewZoom.value; - e.preventDefault(); - } else if (e.key === 'ArrowRight' && animation.value.isPlaying) { - viewportOffset.value.x -= panAmount / previewZoom.value; - e.preventDefault(); - } + } else if (e.key === 'ArrowLeft' && animation.value.isPlaying) { + viewportOffset.value.x += panAmount / previewZoom.value; + e.preventDefault(); + } else if (e.key === 'ArrowRight' && animation.value.isPlaying) { + viewportOffset.value.x -= panAmount / previewZoom.value; + e.preventDefault(); } }; @@ -315,9 +282,22 @@ // Force render the first frame if (sprites.value.length > 0) { + // Get the current sprite + const currentSprite = sprites.value[0]; + + // Calculate center position + const centerX = Math.max(0, Math.floor((store.cellSize.width - currentSprite.width) / 2)); + const centerY = Math.max(0, Math.floor((store.cellSize.height - currentSprite.height) / 2)); + // Get the frame-specific offset for the first frame const frameOffset = store.getSpriteOffset(0); + // If the offset is (0,0), center the sprite + if (frameOffset.x === 0 && frameOffset.y === 0) { + frameOffset.x = centerX; + frameOffset.y = centerY; + } + // Update the current offset for UI display store.currentSpriteOffset.x = frameOffset.x; store.currentSpriteOffset.y = frameOffset.y; @@ -448,22 +428,30 @@ }); }; - // Reset sprite position to original + // Reset sprite position to center const resetSpritePosition = () => { - // Reset the sprite offset for the current frame to zero + // Get current sprite + const currentSprite = sprites.value[currentFrame.value]; + if (!currentSprite) return; + + // Calculate center position + const centerX = Math.max(0, Math.floor((store.cellSize.width - currentSprite.width) / 2)); + const centerY = Math.max(0, Math.floor((store.cellSize.height - currentSprite.height) / 2)); + + // Reset the sprite offset for the current frame to the center position const frameOffset = store.getSpriteOffset(currentFrame.value); - frameOffset.x = 0; - frameOffset.y = 0; + frameOffset.x = centerX; + frameOffset.y = centerY; // Also update the current offset - store.currentSpriteOffset.x = 0; - store.currentSpriteOffset.y = 0; + store.currentSpriteOffset.x = centerX; + store.currentSpriteOffset.y = centerY; // Update the frame to reflect the change updateFrame(); // Show a notification - store.showNotification('Sprite position reset to original'); + store.showNotification('Sprite position reset to center'); }; // Update canvas container size based on zoom level @@ -477,74 +465,85 @@ // Canvas drag functions for moving the sprite within its cell const startCanvasDrag = (e: MouseEvent) => { if (sprites.value.length === 0) return; - - // Don't start sprite dragging if we're already dragging the viewport if (isViewportDragging.value) return; isCanvasDragging.value = true; + + // Store initial position canvasDragStart.value = { x: e.clientX, y: e.clientY, }; - // Add temporary event listeners - window.addEventListener('mousemove', handleCanvasDrag); - window.addEventListener('mouseup', stopCanvasDrag); + window.addEventListener('mousemove', handleCanvasDrag, { capture: true }); + window.addEventListener('mouseup', stopCanvasDrag, { capture: true }); - // Prevent default to avoid text selection e.preventDefault(); + e.stopPropagation(); }; const handleCanvasDrag = (e: MouseEvent) => { if (!isCanvasDragging.value) return; - const deltaX = e.clientX - canvasDragStart.value.x; - const deltaY = e.clientY - canvasDragStart.value.y; - requestAnimationFrame(() => { - // Get the frame-specific offset - const frameOffset = store.getSpriteOffset(currentFrame.value); - - // Calculate new position and round to nearest pixel - const newX = Math.round(frameOffset.x + deltaX / previewZoom.value); - const newY = Math.round(frameOffset.y + deltaY / previewZoom.value); - // Get current sprite const currentSprite = sprites.value[currentFrame.value]; if (!currentSprite) return; - // Calculate maximum allowed offset based on sprite and cell size - const maxOffsetX = Math.floor((store.cellSize.width - currentSprite.width) / 2); - const maxOffsetY = Math.floor((store.cellSize.height - currentSprite.height) / 2); + // Calculate delta from last position + const deltaX = e.clientX - canvasDragStart.value.x; + const deltaY = e.clientY - canvasDragStart.value.y; - // Constrain movement to stay within cell boundaries, preventing negative offsets - const constrainedX = Math.max(0, Math.min(maxOffsetX, newX)); - const constrainedY = Math.max(0, Math.min(maxOffsetY, newY)); + // Only move when delta exceeds the threshold for one pixel movement at current zoom + const pixelThreshold = previewZoom.value; // One pixel at current zoom level - // Update both the current offset and the frame-specific offset - frameOffset.x = constrainedX; - frameOffset.y = constrainedY; - store.currentSpriteOffset.x = constrainedX; - store.currentSpriteOffset.y = constrainedY; + // Get the frame-specific offset + const frameOffset = store.getSpriteOffset(currentFrame.value); - // Reset drag start position - canvasDragStart.value = { - x: e.clientX, - y: e.clientY, - }; + // Calculate the maximum allowed offset + const maxOffsetX = Math.max(0, store.cellSize.width - currentSprite.width); + const maxOffsetY = Math.max(0, store.cellSize.height - currentSprite.height); - // Update the frame with the new offset + // Move one pixel at a time when threshold is reached + if (Math.abs(deltaX) >= pixelThreshold) { + const pixelsToMove = Math.sign(deltaX); + const newX = frameOffset.x + pixelsToMove; + frameOffset.x = Math.max(0, Math.min(maxOffsetX, newX)); + + // Reset the start X position for next pixel move + canvasDragStart.value.x = e.clientX; + } + + if (Math.abs(deltaY) >= pixelThreshold) { + const pixelsToMove = Math.sign(deltaY); + const newY = frameOffset.y + pixelsToMove; + frameOffset.y = Math.max(0, Math.min(maxOffsetY, newY)); + + // Reset the start Y position for next pixel move + canvasDragStart.value.y = e.clientY; + } + + // Update the current offset to match + store.currentSpriteOffset.x = frameOffset.x; + store.currentSpriteOffset.y = frameOffset.y; + + // Update the frame updateFrame(); - // Re-render the main view to reflect the changes + // Re-render the main view store.renderSpritesheetPreview(); }); }; - const stopCanvasDrag = () => { + const stopCanvasDrag = (e: MouseEvent) => { + if (!isCanvasDragging.value) return; + isCanvasDragging.value = false; - window.removeEventListener('mousemove', handleCanvasDrag); - window.removeEventListener('mouseup', stopCanvasDrag); + window.removeEventListener('mousemove', handleCanvasDrag, { capture: true }); + window.removeEventListener('mouseup', stopCanvasDrag, { capture: true }); + + e.preventDefault(); + e.stopPropagation(); }; // Canvas viewport navigation functions diff --git a/src/composables/useSpritesheetStore.ts b/src/composables/useSpritesheetStore.ts index e3295b1..b8f944a 100644 --- a/src/composables/useSpritesheetStore.ts +++ b/src/composables/useSpritesheetStore.ts @@ -527,8 +527,9 @@ export function useSpritesheetStore() { const originalOffsetY = Math.round(currentSprite.y - cellY * cellSize.height); // Calculate precise offset for pixel-perfect rendering, including the user's drag offset - const offsetX = originalOffsetX + spriteOffset.x; - const offsetY = originalOffsetY + spriteOffset.y; + // Use the spriteOffset directly as the position within the cell + const offsetX = spriteOffset.x; + const offsetY = spriteOffset.y; // Draw the current sprite at full opacity at the new position animation.ctx.drawImage(currentSprite.img, offsetX, offsetY);