-
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);