diff --git a/src/components/PreviewModal.vue b/src/components/PreviewModal.vue
index ab60efe..67a2c3c 100644
--- a/src/components/PreviewModal.vue
+++ b/src/components/PreviewModal.vue
@@ -80,7 +80,7 @@
- Position: {{ Math.round(spriteOffset.x) }}px, {{ Math.round(spriteOffset.y) }}px (drag to move within cell)
+ Position: {{ Math.round(spriteOffset?.x ?? 0) }}px, {{ Math.round(spriteOffset?.y ?? 0) }}px (drag to move within cell)
(null);
+ // Add this constant for pan amount
+ const panAmount = 10; // pixels to pan per keypress
+
const isModalOpen = computed({
get: () => store.isModalOpen.value,
set: value => {
@@ -150,20 +153,18 @@
get: () => {
// Get the offset for the current frame
const frameOffset = store.getSpriteOffset(currentFrame.value);
- // Update the current offset for UI display
- store.currentSpriteOffset.x = frameOffset.x;
- store.currentSpriteOffset.y = frameOffset.y;
- return store.currentSpriteOffset;
+ // Return the frame-specific offset directly
+ return frameOffset;
},
set: val => {
- // Update both the current offset and the frame-specific offset
- store.currentSpriteOffset.x = val.x;
- store.currentSpriteOffset.y = val.y;
-
- // Get the frame-specific offset and update it
+ // Update the frame-specific offset directly
const frameOffset = store.getSpriteOffset(currentFrame.value);
frameOffset.x = val.x;
frameOffset.y = val.y;
+
+ // Also update the current offset for UI consistency
+ store.currentSpriteOffset.x = val.x;
+ store.currentSpriteOffset.y = val.y;
},
});
const isCanvasDragging = ref(false);
@@ -184,7 +185,7 @@
// Computed property to check if sprite has been moved from original position
const hasSpriteOffset = computed(() => {
- return spriteOffset.x !== 0 || spriteOffset.y !== 0;
+ return spriteOffset.value.x !== 0 || spriteOffset.value.y !== 0;
});
// applyOffsetsToMainView function removed
@@ -298,10 +299,6 @@
frameOffset.y = centerY;
}
- // Update the current offset for UI display
- store.currentSpriteOffset.x = frameOffset.x;
- store.currentSpriteOffset.y = frameOffset.y;
-
// Render with the frame-specific offset
store.renderAnimationFrame(0, showAllSprites.value, frameOffset);
}
@@ -357,9 +354,8 @@
animation.value.currentFrame = currentFrame.value;
animation.value.manualUpdate = true;
- // Get the frame-specific offset
- const frameOffset = store.getSpriteOffset(currentFrame.value);
- store.renderAnimationFrame(currentFrame.value, showAllSprites.value, frameOffset);
+ // Use the computed spriteOffset directly
+ store.renderAnimationFrame(currentFrame.value, showAllSprites.value, spriteOffset.value);
};
const handleFrameRateChange = () => {
@@ -438,14 +434,8 @@
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 = centerX;
- frameOffset.y = centerY;
-
- // Also update the current offset
- store.currentSpriteOffset.x = centerX;
- store.currentSpriteOffset.y = centerY;
+ // Update the sprite offset using the computed property setter
+ spriteOffset.value = { x: centerX, y: centerY };
// Update the frame to reflect the change
updateFrame();
@@ -497,9 +487,6 @@
// Only move when delta exceeds the threshold for one pixel movement at current zoom
const pixelThreshold = previewZoom.value; // One pixel at current zoom level
- // Get the frame-specific offset
- const frameOffset = store.getSpriteOffset(currentFrame.value);
-
// 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);
@@ -507,8 +494,8 @@
// 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));
+ const newX = spriteOffset.value.x + pixelsToMove;
+ spriteOffset.value.x = Math.max(0, Math.min(maxOffsetX, newX));
// Reset the start X position for next pixel move
canvasDragStart.value.x = e.clientX;
@@ -516,17 +503,13 @@
if (Math.abs(deltaY) >= pixelThreshold) {
const pixelsToMove = Math.sign(deltaY);
- const newY = frameOffset.y + pixelsToMove;
- frameOffset.y = Math.max(0, Math.min(maxOffsetY, newY));
+ const newY = spriteOffset.value.y + pixelsToMove;
+ spriteOffset.value.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();
@@ -651,7 +634,7 @@
if (isModalOpen.value && newSprites.length > 0) {
updateCanvasSize();
updateCanvasContainerSize();
- store.renderAnimationFrame(currentFrame.value, showAllSprites.value, spriteOffset.value);
+ updateFrame();
}
},
{ deep: true }
@@ -670,7 +653,7 @@
() => previewBorder.value,
() => {
if (isModalOpen.value && sprites.value.length > 0) {
- store.renderAnimationFrame(currentFrame.value, showAllSprites.value, spriteOffset.value);
+ updateFrame();
}
},
{ deep: true }
@@ -681,7 +664,7 @@
() => showAllSprites.value,
() => {
if (isModalOpen.value && sprites.value.length > 0) {
- store.renderAnimationFrame(currentFrame.value, showAllSprites.value, spriteOffset.value);
+ updateFrame();
}
}
);
diff --git a/src/composables/useSpritesheetStore.ts b/src/composables/useSpritesheetStore.ts
index b8f944a..e8f68ac 100644
--- a/src/composables/useSpritesheetStore.ts
+++ b/src/composables/useSpritesheetStore.ts
@@ -259,9 +259,21 @@ export function useSpritesheetStore() {
// Get the frame-specific offset for this sprite
const frameOffset = getSpriteOffset(index);
- // Apply the frame-specific offset to the sprite position
- const finalX = x + frameOffset.x;
- const finalY = y + frameOffset.y;
+ // Calculate the maximum allowed offset based on sprite and cell size
+ const maxOffsetX = Math.max(0, cellSize.width - sprite.width);
+ const maxOffsetY = Math.max(0, cellSize.height - sprite.height);
+
+ // Constrain the offset to prevent out-of-bounds positioning
+ const constrainedOffsetX = Math.max(0, Math.min(maxOffsetX, frameOffset.x));
+ const constrainedOffsetY = Math.max(0, Math.min(maxOffsetY, frameOffset.y));
+
+ // Update the frame offset with the constrained values
+ frameOffset.x = constrainedOffsetX;
+ frameOffset.y = constrainedOffsetY;
+
+ // Apply the constrained offset to the sprite position
+ const finalX = x + constrainedOffsetX;
+ const finalY = y + constrainedOffsetY;
// Draw the image at its final position with pixel-perfect rendering
ctx.value!.imageSmoothingEnabled = false; // Keep pixel art sharp
@@ -277,9 +289,21 @@ export function useSpritesheetStore() {
// Get the frame-specific offset for this sprite
const frameOffset = getSpriteOffset(index);
- // Apply the frame-specific offset to the sprite position
- const finalX = x + frameOffset.x;
- const finalY = y + frameOffset.y;
+ // Calculate the maximum allowed offset based on sprite and cell size
+ const maxOffsetX = Math.max(0, cellSize.width - sprite.width);
+ const maxOffsetY = Math.max(0, cellSize.height - sprite.height);
+
+ // Constrain the offset to prevent out-of-bounds positioning
+ const constrainedOffsetX = Math.max(0, Math.min(maxOffsetX, frameOffset.x));
+ const constrainedOffsetY = Math.max(0, Math.min(maxOffsetY, frameOffset.y));
+
+ // Update the frame offset with the constrained values
+ frameOffset.x = constrainedOffsetX;
+ frameOffset.y = constrainedOffsetY;
+
+ // Apply the constrained offset to the sprite position
+ const finalX = x + constrainedOffsetX;
+ const finalY = y + constrainedOffsetY;
ctx.value.imageSmoothingEnabled = false; // Keep pixel art sharp
ctx.value.drawImage(sprite.img, finalX, finalY, sprite.width, sprite.height);
@@ -432,11 +456,33 @@ export function useSpritesheetStore() {
// Ensure pixel art remains sharp in the downloaded file
tempCtx.imageSmoothingEnabled = false;
- sprites.value.forEach(sprite => {
- // Use rounded coordinates for pixel-perfect rendering
- const x = Math.round(sprite.x);
- const y = Math.round(sprite.y);
- tempCtx.drawImage(sprite.img, x, y);
+ sprites.value.forEach((sprite, index) => {
+ // Get the frame-specific offset for this sprite
+ const frameOffset = getSpriteOffset(index);
+
+ // Calculate the cell coordinates for this sprite
+ const cellX = Math.floor(sprite.x / cellSize.width);
+ const cellY = Math.floor(sprite.y / cellSize.height);
+
+ // Calculate the base position within the cell
+ const baseX = cellX * cellSize.width;
+ const baseY = cellY * cellSize.height;
+
+ // Calculate the maximum allowed offset based on sprite and cell size
+ // This prevents sprites from going out of bounds
+ const maxOffsetX = Math.max(0, cellSize.width - sprite.width);
+ const maxOffsetY = Math.max(0, cellSize.height - sprite.height);
+
+ // Constrain the offset to prevent out-of-bounds positioning
+ const constrainedOffsetX = Math.max(0, Math.min(maxOffsetX, frameOffset.x));
+ const constrainedOffsetY = Math.max(0, Math.min(maxOffsetY, frameOffset.y));
+
+ // Apply the constrained offset to the base position
+ const finalX = baseX + constrainedOffsetX;
+ const finalY = baseY + constrainedOffsetY;
+
+ // Draw the sprite at the calculated position
+ tempCtx.drawImage(sprite.img, finalX, finalY, sprite.width, sprite.height);
});
const link = document.createElement('a');