Multiple fixes
This commit is contained in:
parent
ca28f66997
commit
7cb5605b54
@ -1,6 +1,11 @@
|
||||
<template>
|
||||
<div class="min-h-screen bg-gray-900 text-gray-200 font-sans">
|
||||
<app-header @toggle-help="showHelpModal" />
|
||||
<div class="max-w-7xl mx-auto px-6">
|
||||
<div class="bg-blue-500 bg-opacity-10 border-l-4 border-blue-500 p-4 mt-6 rounded-r">
|
||||
<p>Container size will adjust to fit the largest sprite. All sprites will be placed in cells of the same size.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="max-w-7xl mx-auto p-6 grid grid-cols-1 lg:grid-cols-4 gap-6">
|
||||
<sidebar class="lg:col-span-1" />
|
||||
<main-content class="lg:col-span-3" />
|
||||
|
@ -47,9 +47,7 @@
|
||||
};
|
||||
|
||||
const handleFiles = async (files: FileList) => {
|
||||
console.log('Handling files:', files.length);
|
||||
const imageFiles = Array.from(files).filter(file => file.type.startsWith('image/'));
|
||||
console.log('Image files filtered:', imageFiles.length);
|
||||
|
||||
if (imageFiles.length === 0) {
|
||||
store.showNotification('Please upload image files only', 'error');
|
||||
@ -61,12 +59,10 @@
|
||||
|
||||
for (let i = 0; i < imageFiles.length; i++) {
|
||||
const file = imageFiles[i];
|
||||
console.log(`Processing file ${i+1}/${imageFiles.length}:`, file.name, file.type, file.size);
|
||||
|
||||
try {
|
||||
const sprite = await createSpriteFromFile(file, i);
|
||||
newSprites.push(sprite);
|
||||
console.log(`Successfully processed ${file.name}`);
|
||||
} catch (error) {
|
||||
errorCount++;
|
||||
console.error('Error loading sprite:', error);
|
||||
@ -75,7 +71,6 @@
|
||||
}
|
||||
|
||||
if (newSprites.length > 0) {
|
||||
console.log('Adding sprites to store:', newSprites.length);
|
||||
store.addSprites(newSprites);
|
||||
emit('files-uploaded', newSprites);
|
||||
store.showNotification(`Added ${newSprites.length} sprites successfully`);
|
||||
@ -86,56 +81,48 @@
|
||||
|
||||
const createSpriteFromFile = (file: File, index: number): Promise<Sprite> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const reader = new FileReader();
|
||||
// Create a URL for the file
|
||||
const objectUrl = URL.createObjectURL(file);
|
||||
|
||||
reader.onload = e => {
|
||||
if (!e.target?.result) {
|
||||
console.error('Failed to read file:', file.name);
|
||||
reject(new Error(`Failed to read file: ${file.name}`));
|
||||
const img = new Image();
|
||||
|
||||
// Set up event handlers
|
||||
img.onload = () => {
|
||||
// Verify the image has loaded properly
|
||||
if (img.width === 0 || img.height === 0) {
|
||||
console.error('Image loaded with invalid dimensions:', file.name, img.width, img.height);
|
||||
URL.revokeObjectURL(objectUrl);
|
||||
reject(new Error(`Image has invalid dimensions: ${file.name}`));
|
||||
return;
|
||||
}
|
||||
|
||||
const img = new Image();
|
||||
|
||||
// Set crossOrigin to anonymous to handle CORS issues
|
||||
img.crossOrigin = 'anonymous';
|
||||
|
||||
img.onload = () => {
|
||||
// Verify the image has loaded properly
|
||||
if (img.width === 0 || img.height === 0) {
|
||||
console.error('Image loaded with invalid dimensions:', file.name, img.width, img.height);
|
||||
reject(new Error(`Image has invalid dimensions: ${file.name}`));
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('Sprite created successfully:', file.name, img.width, img.height);
|
||||
resolve({
|
||||
img,
|
||||
width: img.width,
|
||||
height: img.height,
|
||||
x: 0,
|
||||
y: 0,
|
||||
name: file.name,
|
||||
id: `sprite-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
|
||||
uploadOrder: index,
|
||||
});
|
||||
// Create the sprite object
|
||||
const sprite: Sprite = {
|
||||
img,
|
||||
width: img.width,
|
||||
height: img.height,
|
||||
x: 0,
|
||||
y: 0,
|
||||
name: file.name,
|
||||
id: `sprite-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
|
||||
uploadOrder: index,
|
||||
};
|
||||
|
||||
img.onerror = (error) => {
|
||||
console.error('Error loading image:', file.name, error);
|
||||
reject(new Error(`Failed to load image: ${file.name}`));
|
||||
};
|
||||
// Keep the objectUrl reference and don't revoke it yet
|
||||
// The image is still needed for rendering later
|
||||
// URL.revokeObjectURL(objectUrl); - Don't do this anymore
|
||||
|
||||
// Set the source after setting up event handlers
|
||||
img.src = e.target.result as string;
|
||||
resolve(sprite);
|
||||
};
|
||||
|
||||
reader.onerror = (error) => {
|
||||
console.error('Error reading file:', file.name, error);
|
||||
reject(new Error(`Error reading file: ${file.name}`));
|
||||
img.onerror = error => {
|
||||
console.error('Error loading image:', file.name, error);
|
||||
URL.revokeObjectURL(objectUrl);
|
||||
reject(new Error(`Failed to load image: ${file.name}`));
|
||||
};
|
||||
|
||||
reader.readAsDataURL(file);
|
||||
// Set the source to the object URL
|
||||
img.src = objectUrl;
|
||||
});
|
||||
};
|
||||
</script>
|
||||
|
@ -39,17 +39,24 @@
|
||||
}));
|
||||
|
||||
const setupCheckerboardPattern = () => {
|
||||
if (!canvasEl.value) return;
|
||||
if (!canvasEl.value) {
|
||||
console.error('MainContent: Canvas element not available for checkerboard pattern');
|
||||
return;
|
||||
}
|
||||
|
||||
// This will be done with CSS using Tailwind's bg utilities
|
||||
canvasEl.value.style.backgroundImage = `
|
||||
linear-gradient(45deg, #1a1a1a 25%, transparent 25%),
|
||||
linear-gradient(-45deg, #1a1a1a 25%, transparent 25%),
|
||||
linear-gradient(45deg, transparent 75%, #1a1a1a 75%),
|
||||
linear-gradient(-45deg, transparent 75%, #1a1a1a 75%)
|
||||
`;
|
||||
canvasEl.value.style.backgroundSize = '20px 20px';
|
||||
canvasEl.value.style.backgroundPosition = '0 0, 0 10px, 10px -10px, -10px 0px';
|
||||
try {
|
||||
// This will be done with CSS using Tailwind's bg utilities
|
||||
canvasEl.value.style.backgroundImage = `
|
||||
linear-gradient(45deg, #1a1a1a 25%, transparent 25%),
|
||||
linear-gradient(-45deg, #1a1a1a 25%, transparent 25%),
|
||||
linear-gradient(45deg, transparent 75%, #1a1a1a 75%),
|
||||
linear-gradient(-45deg, transparent 75%, #1a1a1a 75%)
|
||||
`;
|
||||
canvasEl.value.style.backgroundSize = '20px 20px';
|
||||
canvasEl.value.style.backgroundPosition = '0 0, 0 10px, 10px -10px, -10px 0px';
|
||||
} catch (error) {
|
||||
console.error('MainContent: Error setting up checkerboard pattern:', error);
|
||||
}
|
||||
};
|
||||
|
||||
const handleMouseDown = (e: MouseEvent) => {
|
||||
@ -85,8 +92,9 @@
|
||||
if (canvasEl.value && cellX >= 0 && cellX < canvasEl.value.width / store.cellSize.width && cellY >= 0 && cellY < canvasEl.value.height / store.cellSize.height) {
|
||||
isTooltipVisible.value = true;
|
||||
tooltipText.value = `Cell: (${cellX}, ${cellY})`;
|
||||
tooltipPosition.value.x = e.clientX;
|
||||
tooltipPosition.value.y = e.clientY;
|
||||
// Use pageX and pageY instead of clientX and clientY
|
||||
tooltipPosition.value.x = e.pageX;
|
||||
tooltipPosition.value.y = e.pageY;
|
||||
} else {
|
||||
isTooltipVisible.value = false;
|
||||
}
|
||||
@ -106,12 +114,15 @@
|
||||
// Calculate cell boundaries
|
||||
const cellLeft = cellX * store.cellSize.width;
|
||||
const cellTop = cellY * store.cellSize.height;
|
||||
const cellRight = cellLeft + store.cellSize.width - store.draggedSprite.value.width;
|
||||
const cellBottom = cellTop + store.cellSize.height - store.draggedSprite.value.height;
|
||||
const cellRight = cellLeft + store.cellSize.width - store.draggedSprite.value.img.width;
|
||||
const cellBottom = cellTop + store.cellSize.height - store.draggedSprite.value.img.height;
|
||||
|
||||
// Constrain position to stay within the cell
|
||||
store.draggedSprite.value.x = Math.max(cellLeft, Math.min(newX, cellRight));
|
||||
store.draggedSprite.value.y = Math.max(cellTop, Math.min(newY, cellBottom));
|
||||
|
||||
// Trigger a re-render
|
||||
store.renderSpritesheetPreview();
|
||||
} else {
|
||||
// Calculate new position based on grid cells (snap to grid)
|
||||
const newCellX = Math.floor((x - store.dragOffset.x) / store.cellSize.width);
|
||||
@ -127,6 +138,9 @@
|
||||
|
||||
store.draggedSprite.value.x = boundedCellX * store.cellSize.width;
|
||||
store.draggedSprite.value.y = boundedCellY * store.cellSize.height;
|
||||
|
||||
// Trigger a re-render
|
||||
store.renderSpritesheetPreview();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -154,31 +168,80 @@
|
||||
};
|
||||
|
||||
const setupCanvasEvents = () => {
|
||||
if (!canvasEl.value) return;
|
||||
if (!canvasEl.value) {
|
||||
console.error('MainContent: Canvas element not available for event setup');
|
||||
return;
|
||||
}
|
||||
|
||||
canvasEl.value.addEventListener('mousedown', handleMouseDown);
|
||||
canvasEl.value.addEventListener('mousemove', handleMouseMove);
|
||||
canvasEl.value.addEventListener('mouseup', handleMouseUp);
|
||||
canvasEl.value.addEventListener('mouseout', handleMouseOut);
|
||||
try {
|
||||
canvasEl.value.addEventListener('mousedown', handleMouseDown);
|
||||
canvasEl.value.addEventListener('mousemove', handleMouseMove);
|
||||
canvasEl.value.addEventListener('mouseup', handleMouseUp);
|
||||
canvasEl.value.addEventListener('mouseout', handleMouseOut);
|
||||
} catch (error) {
|
||||
console.error('MainContent: Error setting up canvas events:', error);
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
if (canvasEl.value) {
|
||||
// Initialize canvas immediately
|
||||
initializeCanvas();
|
||||
|
||||
// Also set up a MutationObserver to ensure the canvas is properly initialized
|
||||
// even if there are DOM changes
|
||||
const observer = new MutationObserver(mutations => {
|
||||
initializeCanvas();
|
||||
});
|
||||
|
||||
// Start observing the document with the configured parameters
|
||||
observer.observe(document.body, { childList: true, subtree: true });
|
||||
|
||||
// Clean up the observer after a short time
|
||||
setTimeout(() => {
|
||||
observer.disconnect();
|
||||
}, 2000);
|
||||
});
|
||||
|
||||
const initializeCanvas = () => {
|
||||
if (!canvasEl.value) {
|
||||
console.error('MainContent: Canvas element not found');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// Get the 2D context
|
||||
const context = canvasEl.value.getContext('2d');
|
||||
if (!context) {
|
||||
console.error('MainContent: Failed to get 2D context from canvas');
|
||||
return;
|
||||
}
|
||||
|
||||
// Set the store references
|
||||
store.canvas.value = canvasEl.value;
|
||||
store.ctx.value = canvasEl.value.getContext('2d');
|
||||
store.ctx.value = context;
|
||||
|
||||
// Initialize canvas size
|
||||
canvasEl.value.width = 400;
|
||||
canvasEl.value.height = 300;
|
||||
|
||||
// Setup the canvas
|
||||
setupCheckerboardPattern();
|
||||
setupCanvasEvents();
|
||||
|
||||
// Setup keyboard events for modifiers
|
||||
window.addEventListener('keydown', handleKeyDown);
|
||||
window.addEventListener('keyup', handleKeyUp);
|
||||
|
||||
// Check if we have sprites that need rendering
|
||||
if (store.sprites.value.length > 0) {
|
||||
store.updateCellSize();
|
||||
store.autoArrangeSprites();
|
||||
store.renderSpritesheetPreview();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('MainContent: Error initializing canvas:', error);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
window.removeEventListener('keydown', handleKeyDown);
|
||||
|
@ -1,8 +1,8 @@
|
||||
<template>
|
||||
<div class="fixed inset-0 bg-black bg-opacity-70 flex items-center justify-center z-50 transition-all duration-300" :class="{ 'opacity-0 invisible': !isModalOpen, 'opacity-100 visible': isModalOpen }" @click.self="closeModal">
|
||||
<div class="bg-gray-800 rounded-lg max-w-4xl max-h-[90vh] overflow-auto shadow-lg transform transition-transform duration-300" :class="{ '-translate-y-5': !isModalOpen, 'translate-y-0': isModalOpen }">
|
||||
<div class="flex items-center justify-between p-4 bg-gray-700 border-b border-gray-600">
|
||||
<div class="flex items-center gap-2 text-lg font-semibold">
|
||||
<div class="fixed inset-0 flex items-center justify-center z-50" :class="{ 'pointer-events-none': !isModalOpen }">
|
||||
<div class="bg-gray-800 rounded-lg max-w-4xl max-h-[90vh] overflow-auto scrollbar-hide shadow-lg pointer-events-auto" :class="{ invisible: !isModalOpen, visible: isModalOpen }" :style="{ transform: `translate3d(${position.x}px, ${position.y + (isModalOpen ? 0 : -20)}px, 0)` }">
|
||||
<div class="flex items-center justify-between p-4 bg-gray-700 border-b border-gray-600 cursor-move" @mousedown="startDrag">
|
||||
<div class="flex items-center gap-2 text-lg font-semibold select-none">
|
||||
<i class="fas fa-film text-blue-500"></i>
|
||||
<span>Animation Preview</span>
|
||||
</div>
|
||||
@ -104,17 +104,37 @@
|
||||
}
|
||||
};
|
||||
|
||||
const openModal = () => {
|
||||
const openModal = async () => {
|
||||
if (sprites.value.length === 0) {
|
||||
store.showNotification('Please add sprites first', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
// Reset position when opening
|
||||
position.value = { x: 0, y: 0 };
|
||||
|
||||
// Reset to first frame
|
||||
currentFrame.value = 0;
|
||||
animation.value.currentFrame = 0;
|
||||
|
||||
// Make sure the canvas is initialized
|
||||
await initializeCanvas();
|
||||
|
||||
// Set modal open state
|
||||
store.isModalOpen.value = true;
|
||||
|
||||
// Show the current frame
|
||||
if (!animation.value.isPlaying && sprites.value.length > 0) {
|
||||
store.renderAnimationFrame(currentFrame.value);
|
||||
// Wait for next frame to ensure DOM is updated
|
||||
await new Promise(resolve => requestAnimationFrame(resolve));
|
||||
|
||||
// Set proper canvas size before rendering
|
||||
if (animCanvas.value && store.cellSize.width && store.cellSize.height) {
|
||||
animCanvas.value.width = store.cellSize.width;
|
||||
animCanvas.value.height = store.cellSize.height;
|
||||
}
|
||||
|
||||
// Force render the first frame
|
||||
if (sprites.value.length > 0) {
|
||||
store.renderAnimationFrame(0);
|
||||
}
|
||||
};
|
||||
|
||||
@ -159,23 +179,72 @@
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
if (animCanvas.value) {
|
||||
store.animation.canvas = animCanvas.value;
|
||||
store.animation.ctx = animCanvas.value.getContext('2d');
|
||||
initializeCanvas();
|
||||
|
||||
// Initialize canvas size
|
||||
animCanvas.value.width = 200;
|
||||
animCanvas.value.height = 200;
|
||||
|
||||
// Setup keyboard shortcuts for the modal
|
||||
window.addEventListener('keydown', handleKeyDown);
|
||||
}
|
||||
// Setup keyboard shortcuts for the modal
|
||||
window.addEventListener('keydown', handleKeyDown);
|
||||
});
|
||||
|
||||
const initializeCanvas = async () => {
|
||||
if (!animCanvas.value) {
|
||||
console.error('PreviewModal: Animation canvas not found');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// Get the 2D context
|
||||
const context = animCanvas.value.getContext('2d');
|
||||
if (!context) {
|
||||
console.error('PreviewModal: Failed to get 2D context from animation canvas');
|
||||
return;
|
||||
}
|
||||
|
||||
// Set the store references
|
||||
store.animation.canvas = animCanvas.value;
|
||||
store.animation.ctx = context;
|
||||
} catch (error) {
|
||||
console.error('PreviewModal: Error initializing animation canvas:', error);
|
||||
}
|
||||
};
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
window.removeEventListener('keydown', handleKeyDown);
|
||||
});
|
||||
|
||||
// Add these new refs for dragging functionality
|
||||
const position = ref({ x: 0, y: 0 });
|
||||
const isDragging = ref(false);
|
||||
const dragOffset = ref({ x: 0, y: 0 });
|
||||
|
||||
const startDrag = (e: MouseEvent) => {
|
||||
isDragging.value = true;
|
||||
dragOffset.value = {
|
||||
x: e.clientX - position.value.x,
|
||||
y: e.clientY - position.value.y,
|
||||
};
|
||||
|
||||
// Add temporary event listeners
|
||||
window.addEventListener('mousemove', handleDrag);
|
||||
window.addEventListener('mouseup', stopDrag);
|
||||
};
|
||||
|
||||
const handleDrag = (e: MouseEvent) => {
|
||||
if (!isDragging.value) return;
|
||||
|
||||
requestAnimationFrame(() => {
|
||||
position.value = {
|
||||
x: e.clientX - dragOffset.value.x,
|
||||
y: e.clientY - dragOffset.value.y,
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
const stopDrag = () => {
|
||||
isDragging.value = false;
|
||||
window.removeEventListener('mousemove', handleDrag);
|
||||
window.removeEventListener('mouseup', stopDrag);
|
||||
};
|
||||
|
||||
// Keep currentFrame in sync with animation.currentFrame
|
||||
watch(
|
||||
() => animation.value.currentFrame,
|
||||
@ -205,4 +274,19 @@
|
||||
border-radius: 50%;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* Optional: Prevent text selection while dragging */
|
||||
.cursor-move {
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
/* Add these new styles */
|
||||
.scrollbar-hide {
|
||||
-ms-overflow-style: none; /* IE and Edge */
|
||||
scrollbar-width: none; /* Firefox */
|
||||
}
|
||||
|
||||
.scrollbar-hide::-webkit-scrollbar {
|
||||
display: none; /* Chrome, Safari and Opera */
|
||||
}
|
||||
</style>
|
||||
|
@ -10,10 +10,6 @@
|
||||
</div>
|
||||
<div class="p-6">
|
||||
<drop-zone @files-uploaded="handleUpload" />
|
||||
|
||||
<div class="bg-blue-500 bg-opacity-10 border-l-4 border-blue-500 p-4 mt-6 rounded-r">
|
||||
<p>Container size will adjust to fit the largest sprite. All sprites will be placed in cells of the same size.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -75,14 +71,14 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue';
|
||||
import { useSpritesheetStore } from '../composables/useSpritesheetStore';
|
||||
import { type Sprite, useSpritesheetStore } from '../composables/useSpritesheetStore';
|
||||
import DropZone from './DropZone.vue';
|
||||
import SpriteList from './SpriteList.vue';
|
||||
|
||||
const store = useSpritesheetStore();
|
||||
const sprites = computed(() => store.sprites.value);
|
||||
|
||||
const handleUpload = () => {
|
||||
const handleUpload = (sprites: Sprite[]) => {
|
||||
// The dropzone component handles adding sprites to the store
|
||||
// This is just for event handling if needed
|
||||
};
|
||||
|
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div v-if="sprites.length === 0" class="text-center text-gray-400 py-8">No sprites uploaded yet</div>
|
||||
|
||||
<div v-else class="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 gap-3 max-h-72 overflow-y-auto pr-2">
|
||||
<div v-else class="grid grid-cols-2 gap-3 max-h-72 overflow-y-auto pr-2">
|
||||
<div v-for="(sprite, index) in sprites" :key="sprite.id" @click="$emit('spriteClicked', sprite.id)" class="border border-gray-600 rounded bg-gray-700 p-2 text-center transition-all cursor-pointer hover:border-blue-500 hover:-translate-y-0.5 hover:shadow-md">
|
||||
<img :src="sprite.img.src" :alt="sprite.name" class="max-w-full max-h-16 mx-auto mb-2 bg-black bg-opacity-20 rounded" />
|
||||
<div class="text-xs text-gray-400 truncate">{{ index + 1 }}. {{ truncateName(sprite.name) }}</div>
|
||||
|
@ -28,17 +28,17 @@ export interface AnimationState {
|
||||
manualUpdate: boolean;
|
||||
}
|
||||
|
||||
export function useSpritesheetStore() {
|
||||
const sprites = ref<Sprite[]>([]);
|
||||
const canvas = ref<HTMLCanvasElement | null>(null);
|
||||
const ctx = ref<CanvasRenderingContext2D | null>(null);
|
||||
const cellSize = reactive<CellSize>({ width: 0, height: 0 });
|
||||
const columns = ref(4); // Default number of columns
|
||||
const draggedSprite = ref<Sprite | null>(null);
|
||||
const dragOffset = reactive({ x: 0, y: 0 });
|
||||
const isShiftPressed = ref(false);
|
||||
const isModalOpen = ref(false);
|
||||
const sprites = ref<Sprite[]>([]);
|
||||
const canvas = ref<HTMLCanvasElement | null>(null);
|
||||
const ctx = ref<CanvasRenderingContext2D | null>(null);
|
||||
const cellSize = reactive<CellSize>({ width: 0, height: 0 });
|
||||
const columns = ref(4); // Default number of columns
|
||||
const draggedSprite = ref<Sprite | null>(null);
|
||||
const dragOffset = reactive({ x: 0, y: 0 });
|
||||
const isShiftPressed = ref(false);
|
||||
const isModalOpen = ref(false);
|
||||
|
||||
export function useSpritesheetStore() {
|
||||
const animation = reactive<AnimationState>({
|
||||
canvas: null,
|
||||
ctx: null,
|
||||
@ -58,7 +58,6 @@ export function useSpritesheetStore() {
|
||||
});
|
||||
|
||||
function addSprites(newSprites: Sprite[]) {
|
||||
console.log('Store: Adding sprites', newSprites.length);
|
||||
if (newSprites.length === 0) {
|
||||
console.warn('Store: Attempted to add empty sprites array');
|
||||
return;
|
||||
@ -79,21 +78,17 @@ export function useSpritesheetStore() {
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('Store: Adding valid sprites', validSprites.length);
|
||||
sprites.value.push(...validSprites);
|
||||
sprites.value.sort((a, b) => a.uploadOrder - b.uploadOrder);
|
||||
updateCellSize();
|
||||
autoArrangeSprites();
|
||||
console.log('Store: Sprites added successfully, total count:', sprites.value.length);
|
||||
} catch (error) {
|
||||
console.error('Store: Error adding sprites:', error);
|
||||
}
|
||||
}
|
||||
|
||||
function updateCellSize() {
|
||||
console.log('Store: Updating cell size');
|
||||
if (sprites.value.length === 0) {
|
||||
console.log('Store: No sprites to update cell size');
|
||||
return;
|
||||
}
|
||||
|
||||
@ -115,7 +110,6 @@ export function useSpritesheetStore() {
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('Store: Cell size calculated', maxWidth, maxHeight);
|
||||
cellSize.width = maxWidth;
|
||||
cellSize.height = maxHeight;
|
||||
|
||||
@ -126,14 +120,11 @@ export function useSpritesheetStore() {
|
||||
}
|
||||
|
||||
function updateCanvasSize() {
|
||||
console.log('Store: Updating canvas size');
|
||||
if (!canvas.value) {
|
||||
console.error('Store: Canvas not available for size update');
|
||||
return;
|
||||
}
|
||||
|
||||
if (sprites.value.length === 0) {
|
||||
console.log('Store: No sprites to determine canvas size');
|
||||
return;
|
||||
}
|
||||
|
||||
@ -150,7 +141,6 @@ export function useSpritesheetStore() {
|
||||
const newWidth = cols * cellSize.width;
|
||||
const newHeight = rows * cellSize.height;
|
||||
|
||||
console.log('Store: Setting canvas size to', newWidth, newHeight);
|
||||
canvas.value.width = newWidth;
|
||||
canvas.value.height = newHeight;
|
||||
} catch (error) {
|
||||
@ -159,9 +149,7 @@ export function useSpritesheetStore() {
|
||||
}
|
||||
|
||||
function autoArrangeSprites() {
|
||||
console.log('Store: Auto-arranging sprites');
|
||||
if (sprites.value.length === 0) {
|
||||
console.log('Store: No sprites to arrange');
|
||||
return;
|
||||
}
|
||||
|
||||
@ -177,52 +165,75 @@ export function useSpritesheetStore() {
|
||||
|
||||
sprite.x = column * cellSize.width;
|
||||
sprite.y = row * cellSize.height;
|
||||
console.log(`Store: Positioned sprite ${index} at (${sprite.x}, ${sprite.y})`);
|
||||
});
|
||||
|
||||
// Check if the canvas is ready before attempting to render
|
||||
if (!ctx.value || !canvas.value) {
|
||||
return;
|
||||
}
|
||||
|
||||
renderSpritesheetPreview();
|
||||
|
||||
if (!animation.isPlaying && animation.manualUpdate && isModalOpen.value) {
|
||||
renderAnimationFrame(animation.currentFrame);
|
||||
}
|
||||
|
||||
console.log('Store: Sprites arranged successfully');
|
||||
} catch (error) {
|
||||
console.error('Store: Error auto-arranging sprites:', error);
|
||||
}
|
||||
}
|
||||
|
||||
function renderSpritesheetPreview(showGrid = true) {
|
||||
console.log('Store: Rendering spritesheet preview');
|
||||
if (!ctx.value || !canvas.value) {
|
||||
console.error('Store: Canvas or context not available for rendering');
|
||||
console.error('Store: Canvas or context not available for rendering, will retry when ready');
|
||||
|
||||
// Set up a small delay and retry when elements might be ready
|
||||
setTimeout(() => {
|
||||
if (ctx.value && canvas.value) {
|
||||
renderSpritesheetPreview(showGrid);
|
||||
}
|
||||
}, 100);
|
||||
return;
|
||||
}
|
||||
|
||||
if (sprites.value.length === 0) {
|
||||
console.log('Store: No sprites to render');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// Make sure canvas dimensions are set correctly
|
||||
updateCanvasSize();
|
||||
|
||||
// Clear the canvas
|
||||
ctx.value.clearRect(0, 0, canvas.value.width, canvas.value.height);
|
||||
|
||||
if (showGrid) {
|
||||
drawGrid();
|
||||
}
|
||||
|
||||
// Draw each sprite
|
||||
sprites.value.forEach((sprite, index) => {
|
||||
try {
|
||||
if (!sprite.img || sprite.img.width === 0 || sprite.img.height === 0) {
|
||||
console.warn(`Store: Invalid sprite at index ${index}, skipping render`);
|
||||
if (!sprite.img) {
|
||||
console.warn(`Store: Sprite at index ${index} has no image, skipping render`);
|
||||
return;
|
||||
}
|
||||
ctx.value!.drawImage(sprite.img, sprite.x, sprite.y);
|
||||
|
||||
if (sprite.img.complete && sprite.img.naturalWidth !== 0) {
|
||||
ctx.value!.drawImage(sprite.img, sprite.x, sprite.y);
|
||||
} else {
|
||||
console.warn(`Store: Sprite image ${index} not fully loaded, setting onload handler`);
|
||||
|
||||
// If image isn't loaded yet, set an onload handler
|
||||
sprite.img.onload = () => {
|
||||
if (ctx.value && canvas.value) {
|
||||
ctx.value.drawImage(sprite.img, sprite.x, sprite.y);
|
||||
}
|
||||
};
|
||||
}
|
||||
} catch (spriteError) {
|
||||
console.error(`Store: Error rendering sprite at index ${index}:`, spriteError);
|
||||
}
|
||||
});
|
||||
console.log('Store: Spritesheet preview rendered successfully');
|
||||
} catch (error) {
|
||||
console.error('Store: Error rendering spritesheet preview:', error);
|
||||
}
|
||||
@ -314,7 +325,7 @@ export function useSpritesheetStore() {
|
||||
});
|
||||
|
||||
const link = document.createElement('a');
|
||||
link.download = 'noxious-spritesheet.png';
|
||||
link.download = 'spritesheet.png';
|
||||
link.href = tempCanvas.toDataURL('image/png');
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
|
Loading…
x
Reference in New Issue
Block a user