[FEAT] Replace img
This commit is contained in:
@@ -40,10 +40,10 @@
|
|||||||
@touchmove="handleTouchMove"
|
@touchmove="handleTouchMove"
|
||||||
@touchend="stopDrag"
|
@touchend="stopDrag"
|
||||||
@contextmenu.prevent.stop
|
@contextmenu.prevent.stop
|
||||||
@dragover="handleDragOver"
|
@dragover="handleGridDragOver"
|
||||||
@dragenter="handleDragEnter"
|
@dragenter="handleGridDragEnter"
|
||||||
@dragleave="onDragLeave"
|
@dragleave="handleGridDragLeave"
|
||||||
@drop="handleDrop"
|
@drop="handleGridDrop"
|
||||||
:class="{ 'ring-4 ring-blue-500 ring-opacity-50': isDragOver }"
|
:class="{ 'ring-4 ring-blue-500 ring-opacity-50': isDragOver }"
|
||||||
>
|
>
|
||||||
<!-- Grid cells -->
|
<!-- Grid cells -->
|
||||||
@@ -174,7 +174,7 @@
|
|||||||
import { ref, onMounted, watch, onUnmounted, toRef, computed, nextTick } from 'vue';
|
import { ref, onMounted, watch, onUnmounted, toRef, computed, nextTick } from 'vue';
|
||||||
import { useSettingsStore } from '@/stores/useSettingsStore';
|
import { useSettingsStore } from '@/stores/useSettingsStore';
|
||||||
import type { Sprite } from '@/types/sprites';
|
import type { Sprite } from '@/types/sprites';
|
||||||
import { useDragSprite } from '@/composables/useDragSprite';
|
import { useDragSprite, type CellPosition } from '@/composables/useDragSprite';
|
||||||
import { useFileDrop } from '@/composables/useFileDrop';
|
import { useFileDrop } from '@/composables/useFileDrop';
|
||||||
import { useGridMetrics } from '@/composables/useGridMetrics';
|
import { useGridMetrics } from '@/composables/useGridMetrics';
|
||||||
import { useBackgroundStyles } from '@/composables/useBackgroundStyles';
|
import { useBackgroundStyles } from '@/composables/useBackgroundStyles';
|
||||||
@@ -204,6 +204,7 @@
|
|||||||
(e: 'removeSprites', ids: string[]): void;
|
(e: 'removeSprites', ids: string[]): void;
|
||||||
(e: 'replaceSprite', id: string, file: File): void;
|
(e: 'replaceSprite', id: string, file: File): void;
|
||||||
(e: 'addSprite', file: File, index?: number): void;
|
(e: 'addSprite', file: File, index?: number): void;
|
||||||
|
(e: 'addSprites', files: File[], index?: number): void;
|
||||||
(e: 'addSpriteWithResize', file: File): void;
|
(e: 'addSpriteWithResize', file: File): void;
|
||||||
(e: 'rotateSprite', id: string, angle: number): void;
|
(e: 'rotateSprite', id: string, angle: number): void;
|
||||||
(e: 'flipSprite', id: string, direction: 'horizontal' | 'vertical'): void;
|
(e: 'flipSprite', id: string, direction: 'horizontal' | 'vertical'): void;
|
||||||
@@ -541,10 +542,50 @@
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const onDragLeave = (event: DragEvent) => {
|
// Grid Drag & Drop
|
||||||
|
// Grid Drag & Drop
|
||||||
|
|
||||||
|
const handleGridDragEnter = (event: DragEvent) => {
|
||||||
|
handleDragEnter(event);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleGridDragOver = (event: DragEvent) => {
|
||||||
|
handleDragOver(event);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleGridDragLeave = (event: DragEvent) => {
|
||||||
handleDragLeave(event, gridContainerRef.value);
|
handleDragLeave(event, gridContainerRef.value);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleGridDrop = (event: DragEvent) => {
|
||||||
|
handleDragLeave(event, gridContainerRef.value); // Reset drag over state
|
||||||
|
isDragOver.value = false;
|
||||||
|
|
||||||
|
if (!event.dataTransfer?.files.length) return;
|
||||||
|
|
||||||
|
// Calculate target cell immediately on drop
|
||||||
|
let targetCellIndex: number | undefined;
|
||||||
|
const pos = getMousePosition(event as unknown as MouseEvent, props.zoom);
|
||||||
|
if (pos) {
|
||||||
|
const cell = findCellAtPosition(pos.x, pos.y);
|
||||||
|
if (cell) {
|
||||||
|
targetCellIndex = cell.index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const files = Array.from(event.dataTransfer.files).filter(file => file.type.startsWith('image/'));
|
||||||
|
|
||||||
|
if (files.length === 0) return;
|
||||||
|
|
||||||
|
if (targetCellIndex !== undefined) {
|
||||||
|
// Add sprites starting from the target cell index
|
||||||
|
emit('addSprites', files, targetCellIndex);
|
||||||
|
} else {
|
||||||
|
// Fallback to default behavior (append)
|
||||||
|
emit('addSprites', files);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
document.addEventListener('mouseup', stopDrag);
|
document.addEventListener('mouseup', stopDrag);
|
||||||
document.addEventListener('keydown', handleKeyDown);
|
document.addEventListener('keydown', handleKeyDown);
|
||||||
|
|||||||
@@ -286,6 +286,63 @@ export const useLayers = () => {
|
|||||||
reader.readAsDataURL(file);
|
reader.readAsDataURL(file);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const addSprites = async (files: File[], index?: number) => {
|
||||||
|
const l = activeLayer.value;
|
||||||
|
if (!l) return;
|
||||||
|
|
||||||
|
const promises = files.map(file => {
|
||||||
|
return new Promise<Sprite>((resolve, reject) => {
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.onload = e => {
|
||||||
|
const url = e.target?.result as string;
|
||||||
|
const img = new Image();
|
||||||
|
img.onload = () => {
|
||||||
|
resolve({
|
||||||
|
id: crypto.randomUUID(),
|
||||||
|
file,
|
||||||
|
img,
|
||||||
|
url,
|
||||||
|
width: img.width,
|
||||||
|
height: img.height,
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
rotation: 0,
|
||||||
|
flipX: false,
|
||||||
|
flipY: false,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
img.onerror = () => {
|
||||||
|
console.error('Failed to load sprite image:', file.name);
|
||||||
|
reject(new Error(`Failed to load sprite image: ${file.name}`));
|
||||||
|
};
|
||||||
|
img.src = url;
|
||||||
|
};
|
||||||
|
reader.onerror = () => {
|
||||||
|
console.error('Failed to read sprite image file:', file.name);
|
||||||
|
reject(new Error(`Failed to read sprite image file: ${file.name}`));
|
||||||
|
};
|
||||||
|
reader.readAsDataURL(file);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
const newSprites = await Promise.all(promises);
|
||||||
|
const currentSprites = [...l.sprites];
|
||||||
|
|
||||||
|
if (typeof index === 'number') {
|
||||||
|
while (currentSprites.length < index) {
|
||||||
|
currentSprites.push(createEmptySprite());
|
||||||
|
}
|
||||||
|
currentSprites.splice(index, 0, ...newSprites);
|
||||||
|
} else {
|
||||||
|
currentSprites.push(...newSprites);
|
||||||
|
}
|
||||||
|
l.sprites = currentSprites;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error adding sprites:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const processImageFiles = async (files: File[]) => {
|
const processImageFiles = async (files: File[]) => {
|
||||||
for (const f of files) addSprite(f);
|
for (const f of files) addSprite(f);
|
||||||
};
|
};
|
||||||
@@ -376,6 +433,7 @@ export const useLayers = () => {
|
|||||||
flipSprite,
|
flipSprite,
|
||||||
replaceSprite,
|
replaceSprite,
|
||||||
addSprite,
|
addSprite,
|
||||||
|
addSprites,
|
||||||
processImageFiles,
|
processImageFiles,
|
||||||
alignSprites,
|
alignSprites,
|
||||||
addLayer,
|
addLayer,
|
||||||
|
|||||||
@@ -309,6 +309,7 @@
|
|||||||
@flip-sprite="flipSprite"
|
@flip-sprite="flipSprite"
|
||||||
@copy-sprite-to-frame="copySpriteToFrame"
|
@copy-sprite-to-frame="copySpriteToFrame"
|
||||||
@open-pixel-editor="openPixelEditor"
|
@open-pixel-editor="openPixelEditor"
|
||||||
|
@add-sprites="addSprites"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div v-else-if="activeTab === 'preview'" class="h-full flex items-center justify-center">
|
<div v-else-if="activeTab === 'preview'" class="h-full flex items-center justify-center">
|
||||||
@@ -372,8 +373,29 @@
|
|||||||
const projectStore = useProjectStore();
|
const projectStore = useProjectStore();
|
||||||
const { closeProject, loadProjectData } = useProjectManager();
|
const { closeProject, loadProjectData } = useProjectManager();
|
||||||
const settingsStore = useSettingsStore();
|
const settingsStore = useSettingsStore();
|
||||||
const { layers, visibleLayers, activeLayer, activeLayerId, columns, updateSpritePosition, updateSpriteInLayer, updateSpriteCell, removeSprite, removeSprites, replaceSprite, addSprite, processImageFiles, alignSprites, addLayer, removeLayer, moveLayer, rotateSprite, flipSprite, copySpriteToFrame } =
|
const {
|
||||||
useLayers();
|
layers,
|
||||||
|
visibleLayers,
|
||||||
|
activeLayer,
|
||||||
|
activeLayerId,
|
||||||
|
columns,
|
||||||
|
updateSpritePosition,
|
||||||
|
updateSpriteInLayer,
|
||||||
|
updateSpriteCell,
|
||||||
|
removeSprite,
|
||||||
|
removeSprites,
|
||||||
|
replaceSprite,
|
||||||
|
addSprite,
|
||||||
|
addSprites,
|
||||||
|
processImageFiles,
|
||||||
|
alignSprites,
|
||||||
|
addLayer,
|
||||||
|
removeLayer,
|
||||||
|
moveLayer,
|
||||||
|
rotateSprite,
|
||||||
|
flipSprite,
|
||||||
|
copySpriteToFrame,
|
||||||
|
} = useLayers();
|
||||||
|
|
||||||
const { downloadSpritesheet, exportSpritesheetJSON, importSpritesheetJSON, downloadAsGif, downloadAsZip } = useExportLayers(
|
const { downloadSpritesheet, exportSpritesheetJSON, importSpritesheetJSON, downloadAsGif, downloadAsZip } = useExportLayers(
|
||||||
layers,
|
layers,
|
||||||
|
|||||||
Reference in New Issue
Block a user