+
Add Sprite
@@ -310,8 +310,17 @@
const clickedSprite = findSpriteAtPosition(pos.x, pos.y);
contextMenuSpriteId.value = clickedSprite?.id || null;
- contextMenuX.value = event.clientX;
- contextMenuY.value = event.clientY;
+
+ // Get the root component element to calculate offset
+ const rootElement = canvasRef.value.closest('.space-y-6') as HTMLElement;
+ if (!rootElement) return;
+
+ const rootRect = rootElement.getBoundingClientRect();
+
+ // Position relative to the component root
+ contextMenuX.value = event.clientX - rootRect.left;
+ contextMenuY.value = event.clientY - rootRect.top;
+
showContextMenu.value = true;
return;
}
diff --git a/src/components/SpritePreview.vue b/src/components/SpritePreview.vue
index 3159b69..c61915f 100644
--- a/src/components/SpritePreview.vue
+++ b/src/components/SpritePreview.vue
@@ -103,6 +103,11 @@
Reposition
+
+
+ All layers
+
+
Compare sprites
@@ -170,6 +175,7 @@
const emit = defineEmits<{
(e: 'updateSprite', id: string, x: number, y: number): void;
+ (e: 'updateSpriteInLayer', layerId: string, spriteId: string, x: number, y: number): void;
}>();
const previewCanvasRef = ref(null);
@@ -207,6 +213,7 @@
// Preview state
const isDraggable = ref(false);
+ const repositionAllLayers = ref(false);
const showAllSprites = ref(false);
const compositeFrames = computed(() => {
@@ -238,6 +245,7 @@
const dragStartX = ref(0);
const dragStartY = ref(0);
const spritePosBeforeDrag = ref({ x: 0, y: 0 });
+ const allSpritesPosBeforeDrag = ref>(new Map());
// Canvas drawing
@@ -319,19 +327,47 @@
const mouseX = ((event.clientX - rect.left) / zoom.value) * scaleX;
const mouseY = ((event.clientY - rect.top) / zoom.value) * scaleY;
- const activeSprite = props.layers.find(l => l.id === props.activeLayerId)?.sprites[currentFrameIndex.value];
const { negativeSpacing } = getCellDimensions();
- // Check if click is on sprite (accounting for negative spacing offset)
- if (activeSprite) {
- const spriteCanvasX = negativeSpacing + activeSprite.x;
- const spriteCanvasY = negativeSpacing + activeSprite.y;
- if (mouseX >= spriteCanvasX && mouseX <= spriteCanvasX + activeSprite.width && mouseY >= spriteCanvasY && mouseY <= spriteCanvasY + activeSprite.height) {
- isDragging.value = true;
- activeSpriteId.value = activeSprite.id;
- dragStartX.value = mouseX;
- dragStartY.value = mouseY;
- spritePosBeforeDrag.value = { x: activeSprite.x, y: activeSprite.y };
+ if (repositionAllLayers.value) {
+ // Check if click is on any sprite from any visible layer
+ const visibleLayers = getVisibleLayers();
+ for (const layer of visibleLayers) {
+ const sprite = layer.sprites[currentFrameIndex.value];
+ if (!sprite) continue;
+
+ const spriteCanvasX = negativeSpacing + sprite.x;
+ const spriteCanvasY = negativeSpacing + sprite.y;
+ if (mouseX >= spriteCanvasX && mouseX <= spriteCanvasX + sprite.width && mouseY >= spriteCanvasY && mouseY <= spriteCanvasY + sprite.height) {
+ isDragging.value = true;
+ activeSpriteId.value = 'ALL_LAYERS'; // Special marker for all layers
+ dragStartX.value = mouseX;
+ dragStartY.value = mouseY;
+
+ // Store initial positions for all sprites in this frame from all visible layers
+ allSpritesPosBeforeDrag.value.clear();
+ visibleLayers.forEach(layer => {
+ const s = layer.sprites[currentFrameIndex.value];
+ if (s) {
+ allSpritesPosBeforeDrag.value.set(s.id, { x: s.x, y: s.y });
+ }
+ });
+ return;
+ }
+ }
+ } else {
+ // Only check active layer sprite
+ const activeSprite = props.layers.find(l => l.id === props.activeLayerId)?.sprites[currentFrameIndex.value];
+ if (activeSprite) {
+ const spriteCanvasX = negativeSpacing + activeSprite.x;
+ const spriteCanvasY = negativeSpacing + activeSprite.y;
+ if (mouseX >= spriteCanvasX && mouseX <= spriteCanvasX + activeSprite.width && mouseY >= spriteCanvasY && mouseY <= spriteCanvasY + activeSprite.height) {
+ isDragging.value = true;
+ activeSpriteId.value = activeSprite.id;
+ dragStartX.value = mouseX;
+ dragStartY.value = mouseY;
+ spritePosBeforeDrag.value = { x: activeSprite.x, y: activeSprite.y };
+ }
}
}
};
@@ -349,21 +385,45 @@
const deltaX = Math.round(mouseX - dragStartX.value);
const deltaY = Math.round(mouseY - dragStartY.value);
- const activeSprite = props.layers.find(l => l.id === props.activeLayerId)?.sprites[currentFrameIndex.value];
- if (!activeSprite || activeSprite.id !== activeSpriteId.value) return;
-
const { cellWidth, cellHeight, negativeSpacing } = getCellDimensions();
- // Calculate new position with constraints and round to integers
- let newX = Math.round(spritePosBeforeDrag.value.x + deltaX);
- let newY = Math.round(spritePosBeforeDrag.value.y + deltaY);
+ if (activeSpriteId.value === 'ALL_LAYERS') {
+ // Move all sprites in current frame from all visible layers
+ const visibleLayers = getVisibleLayers();
+ visibleLayers.forEach(layer => {
+ const sprite = layer.sprites[currentFrameIndex.value];
+ if (!sprite) return;
- // Constrain movement within expanded cell (allow negative values up to -negativeSpacing)
- newX = Math.max(-negativeSpacing, Math.min(cellWidth - negativeSpacing - activeSprite.width, newX));
- newY = Math.max(-negativeSpacing, Math.min(cellHeight - negativeSpacing - activeSprite.height, newY));
+ const originalPos = allSpritesPosBeforeDrag.value.get(sprite.id);
+ if (!originalPos) return;
- emit('updateSprite', activeSpriteId.value, newX, newY);
- drawPreviewCanvas();
+ // Calculate new position with constraints
+ let newX = Math.round(originalPos.x + deltaX);
+ let newY = Math.round(originalPos.y + deltaY);
+
+ // Constrain movement within expanded cell
+ newX = Math.max(-negativeSpacing, Math.min(cellWidth - negativeSpacing - sprite.width, newX));
+ newY = Math.max(-negativeSpacing, Math.min(cellHeight - negativeSpacing - sprite.height, newY));
+
+ emit('updateSpriteInLayer', layer.id, sprite.id, newX, newY);
+ });
+ drawPreviewCanvas();
+ } else {
+ // Move only the active layer sprite
+ const activeSprite = props.layers.find(l => l.id === props.activeLayerId)?.sprites[currentFrameIndex.value];
+ if (!activeSprite || activeSprite.id !== activeSpriteId.value) return;
+
+ // Calculate new position with constraints and round to integers
+ let newX = Math.round(spritePosBeforeDrag.value.x + deltaX);
+ let newY = Math.round(spritePosBeforeDrag.value.y + deltaY);
+
+ // Constrain movement within expanded cell (allow negative values up to -negativeSpacing)
+ newX = Math.max(-negativeSpacing, Math.min(cellWidth - negativeSpacing - activeSprite.width, newX));
+ newY = Math.max(-negativeSpacing, Math.min(cellHeight - negativeSpacing - activeSprite.height, newY));
+
+ emit('updateSprite', activeSpriteId.value, newX, newY);
+ drawPreviewCanvas();
+ }
};
const stopDrag = () => {
diff --git a/src/composables/useLayers.ts b/src/composables/useLayers.ts
index 7cb2900..c9bc14d 100644
--- a/src/composables/useLayers.ts
+++ b/src/composables/useLayers.ts
@@ -37,6 +37,16 @@ export const useLayers = () => {
}
};
+ const updateSpriteInLayer = (layerId: string, spriteId: string, x: number, y: number) => {
+ const l = layers.value.find(layer => layer.id === layerId);
+ if (!l) return;
+ const i = l.sprites.findIndex(s => s.id === spriteId);
+ if (i !== -1) {
+ l.sprites[i].x = Math.floor(x);
+ l.sprites[i].y = Math.floor(y);
+ }
+ };
+
const alignSprites = (position: 'left' | 'center' | 'right' | 'top' | 'middle' | 'bottom') => {
const l = activeLayer.value;
if (!l || !l.sprites.length) return;
@@ -138,9 +148,7 @@ export const useLayers = () => {
img.src = url;
};
- const addSprite = (file: File) => addSpriteWithResize(file);
-
- const addSpriteWithResize = (file: File) => {
+ const addSprite = (file: File) => {
const l = activeLayer.value;
if (!l) return;
const url = URL.createObjectURL(file);
@@ -163,7 +171,7 @@ export const useLayers = () => {
};
const processImageFiles = async (files: File[]) => {
- for (const f of files) addSpriteWithResize(f);
+ for (const f of files) addSprite(f);
};
const addLayer = (name?: string) => {
@@ -203,11 +211,11 @@ export const useLayers = () => {
columns,
getMaxDimensions,
updateSpritePosition,
+ updateSpriteInLayer,
updateSpriteCell,
removeSprite,
replaceSprite,
addSprite,
- addSpriteWithResize,
processImageFiles,
alignSprites,
addLayer,