This commit is contained in:
Dennis Postma 2025-04-05 14:20:58 +02:00
parent a773b51ece
commit 22390acfa6
7 changed files with 164 additions and 33 deletions

View File

@ -28,12 +28,23 @@ export function addSprites(newSprites: Sprite[]) {
return;
}
// Log the number of valid sprites being added
logger.info(`Adding ${validSprites.length} valid sprites`);
sprites.value.push(...validSprites);
sprites.value.sort((a, b) => a.uploadOrder - b.uploadOrder);
// Update cell size before arranging sprites
logger.info('Updating cell size after adding sprites');
updateCellSize();
autoArrangeSprites();
// Only auto-arrange if cell size is valid
if (cellSize && typeof cellSize.width === 'number' && typeof cellSize.height === 'number' && cellSize.width > 0 && cellSize.height > 0) {
logger.info('Auto-arranging sprites');
autoArrangeSprites();
} else {
logger.warn('Skipping auto-arrange due to invalid cell size');
}
} catch (error) {
logger.error('Error adding sprites:', error);
}
@ -44,12 +55,14 @@ export function addSprites(newSprites: Sprite[]) {
*/
export function updateCellSize() {
if (sprites.value.length === 0) {
logger.warn('Cannot update cell size: no sprites available');
return;
}
try {
let maxWidth = 0;
let maxHeight = 0;
let validSpriteCount = 0;
// Find the maximum dimensions across all sprites
sprites.value.forEach(sprite => {
@ -59,17 +72,29 @@ export function updateCellSize() {
}
maxWidth = Math.max(maxWidth, sprite.width);
maxHeight = Math.max(maxHeight, sprite.height);
validSpriteCount++;
});
if (maxWidth === 0 || maxHeight === 0) {
logger.error('Failed to calculate valid cell size');
if (maxWidth === 0 || maxHeight === 0 || validSpriteCount === 0) {
logger.error('Failed to calculate valid cell size - no valid sprites found');
return;
}
// Add a small buffer to ensure sprites fit completely (optional)
const buffer = 0; // Increase if you want padding between sprites
cellSize.width = maxWidth + buffer;
cellSize.height = maxHeight + buffer;
// Set cell size with validation
const newWidth = maxWidth + buffer;
const newHeight = maxHeight + buffer;
if (newWidth <= 0 || newHeight <= 0) {
logger.error(`Invalid calculated cell dimensions: ${newWidth}x${newHeight}`);
return;
}
logger.info(`Updating cell size to ${newWidth}x${newHeight}`);
cellSize.width = newWidth;
cellSize.height = newHeight;
// Ensure all sprites are within their cell bounds after resize
sprites.value.forEach((sprite, index) => {
@ -100,13 +125,25 @@ export function updateCellSize() {
*/
export function autoArrangeSprites() {
if (sprites.value.length === 0) {
logger.warn('No sprites to arrange');
return;
}
try {
if (cellSize.width <= 0 || cellSize.height <= 0) {
// Ensure cell size is valid before proceeding
if (!cellSize || typeof cellSize.width !== 'number' || typeof cellSize.height !== 'number' || cellSize.width <= 0 || cellSize.height <= 0) {
logger.error('Invalid cell size for auto-arranging', cellSize);
return;
// Try to update cell size first
updateCellSize();
// Check again after update attempt
if (!cellSize || typeof cellSize.width !== 'number' || typeof cellSize.height !== 'number' || cellSize.width <= 0 || cellSize.height <= 0) {
logger.error('Still invalid cell size after update attempt', cellSize);
return;
}
logger.warn('Cell size was updated and is now valid');
}
// First update the canvas size to ensure it's large enough

View File

@ -5,6 +5,9 @@ import { type Sprite } from '@/application/types';
* Logger utility with consistent error format
*/
export const logger = {
info: (message: string, details?: any) => {
console.log(`Spritesheet: ${message}`, details || '');
},
warn: (message: string, details?: any) => {
console.warn(`Spritesheet: ${message}`, details || '');
},

View File

@ -1,5 +1,12 @@
<template>
<div @click="openFileDialog" @dragover.prevent="onDragOver" @dragleave="onDragLeave" @drop.prevent="onDrop" class="border-2 border-dashed border-gray-600 rounded-lg p-8 text-center cursor-pointer transition-all" :class="{ 'border-blue-500 bg-blue-500 bg-opacity-5': isDragOver }">
<div
@click="openFileDialog"
@dragover.prevent.stop="onDragOver"
@dragleave.prevent.stop="onDragLeave"
@drop.prevent.stop="onDrop"
class="border-2 border-dashed border-gray-600 rounded-lg p-8 text-center cursor-pointer transition-all"
:class="{ 'border-blue-500 bg-blue-500 bg-opacity-5': isDragOver }"
>
<i class="fas fa-cloud-upload-alt text-blue-500 text-3xl mb-4"></i>
<p class="text-gray-400">Drag & drop sprite images here<br />or click to select files</p>
<input type="file" ref="fileInput" multiple accept="image/*" class="hidden" @change="onFileChange" />
@ -37,6 +44,8 @@
if (e.dataTransfer?.files.length) {
handleFiles(e.dataTransfer.files);
}
e.preventDefault();
e.stopPropagation();
};
const onFileChange = (e: Event) => {

View File

@ -158,6 +158,9 @@
if (!canvasEl.value) return;
// Prevent default to avoid any browser handling that might interfere
e.preventDefault();
const rect = canvasEl.value.getBoundingClientRect();
// Adjust coordinates for zoom level
const x = (e.clientX - rect.left) / store.zoomLevel.value;
@ -265,8 +268,14 @@
}
};
const handleMouseUp = () => {
const handleMouseUp = (e: MouseEvent) => {
store.draggedSprite.value = null;
// Ensure the event is captured
if (e) {
e.preventDefault();
e.stopPropagation();
}
};
const handleMouseOut = () => {
@ -322,14 +331,19 @@
// Regular sprite dragging
handleMouseDown(e);
}
// Ensure the event is captured
e.stopPropagation();
};
/**
* Handle mouse movement on the canvas for panning and sprite interactions
*/
const handleCanvasMouseMove = (e: MouseEvent) => {
// Always prevent default to ensure consistent behavior
e.preventDefault();
if (isPanning.value && containerEl.value) {
e.preventDefault();
// Calculate the distance moved since last position
const dx = e.clientX - lastPosition.value.x;
const dy = e.clientY - lastPosition.value.y;
@ -345,11 +359,18 @@
// Handle regular mouse move for sprites and tooltip
handleMouseMove(e);
}
// Ensure the event is captured
e.stopPropagation();
};
const handleCanvasMouseUp = () => {
const handleCanvasMouseUp = (e: MouseEvent) => {
isPanning.value = false;
handleMouseUp();
handleMouseUp(e);
// Ensure the event is captured
e.preventDefault();
e.stopPropagation();
};
const handleCanvasMouseLeave = () => {
@ -371,11 +392,15 @@
}
// Set up mouse events for the canvas
canvasEl.value.addEventListener('mousedown', handleCanvasMouseDown);
canvasEl.value.addEventListener('mousemove', handleCanvasMouseMove);
canvasEl.value.addEventListener('mouseup', handleCanvasMouseUp);
canvasEl.value.addEventListener('mouseleave', handleCanvasMouseLeave);
canvasEl.value.addEventListener('contextmenu', preventContextMenu);
canvasEl.value.addEventListener('mousedown', handleCanvasMouseDown, { passive: false });
canvasEl.value.addEventListener('mousemove', handleCanvasMouseMove, { passive: false });
canvasEl.value.addEventListener('mouseup', handleCanvasMouseUp, { passive: false });
canvasEl.value.addEventListener('mouseleave', handleCanvasMouseLeave, { passive: false });
canvasEl.value.addEventListener('contextmenu', preventContextMenu, { passive: false });
// Add global event listeners to ensure drag operations complete even if cursor leaves canvas
window.addEventListener('mousemove', handleCanvasMouseMove, { passive: false });
window.addEventListener('mouseup', handleCanvasMouseUp, { passive: false });
};
// Handle window resize to update canvas dimensions
@ -467,6 +492,10 @@
canvasEl.value.removeEventListener('mouseleave', handleCanvasMouseLeave);
canvasEl.value.removeEventListener('contextmenu', preventContextMenu);
}
// Remove global event listeners
window.removeEventListener('mousemove', handleCanvasMouseMove);
window.removeEventListener('mouseup', handleCanvasMouseUp);
} catch (error) {
logger.error('Error during component unmount:', error);
}

View File

@ -27,8 +27,8 @@
minHeight: `${store.cellSize.height * previewZoom}px`,
cursor: previewZoom > 1 ? (isViewportDragging ? 'grabbing' : 'grab') : 'default',
}"
@mousedown="e => startViewportDrag(e, isCanvasDragging)"
@wheel="handleCanvasWheel"
@mousedown.prevent="e => startViewportDrag(e, isCanvasDragging)"
@wheel.prevent="handleCanvasWheel"
>
<div
class="sprite-wrapper"
@ -47,7 +47,7 @@
backgroundPosition: '0 0, 0 5px, 5px -5px, -5px 0px',
backgroundColor: '#2d3748',
}"
@mousedown.stop="e => startCanvasDrag(e, isViewportDragging, previewZoom)"
@mousedown.prevent.stop="e => startCanvasDrag(e, isViewportDragging, previewZoom)"
title="Drag to move sprite within cell"
>
<canvas ref="animCanvas" class="block pixel-art absolute top-0 left-0"></canvas>
@ -143,7 +143,9 @@
await nextTick();
// Ensure cell size is valid before proceeding
if (!store.cellSize || typeof store.cellSize.width !== 'number' || typeof store.cellSize.height !== 'number' || store.cellSize.width <= 0 || store.cellSize.height <= 0) {
if (!store.cellSize.value || typeof store.cellSize.value.width !== 'number' || typeof store.cellSize.value.height !== 'number' || store.cellSize.value.width <= 0 || store.cellSize.value.height <= 0) {
console.log('Attempting to update cell size...', store.cellSize.value);
// Try to update cell size if there are sprites
if (sprites.value.length > 0) {
store.updateCellSize();
@ -151,8 +153,12 @@
}
// Check again after update attempt
if (!store.cellSize || typeof store.cellSize.width !== 'number' || typeof store.cellSize.height !== 'number' || store.cellSize.width <= 0 || store.cellSize.height <= 0) {
if (!store.cellSize.value || typeof store.cellSize.value.width !== 'number' || typeof store.cellSize.value.height !== 'number' || store.cellSize.value.width <= 0 || store.cellSize.value.height <= 0) {
console.warn('Failed to set valid cell dimensions', store.cellSize.value);
store.showNotification('Invalid cell dimensions. Please check sprite sizes.', 'error');
return; // Don't proceed if we can't set valid cell dimensions
} else {
console.log('Successfully updated cell size to', store.cellSize.value);
}
}
@ -242,6 +248,24 @@
}
);
// Watch for changes in cellSize to update the canvas
watch(
() => store.cellSize.value,
newCellSize => {
if (isModalOpen.value && sprites.value.length > 0) {
if (newCellSize && typeof newCellSize.width === 'number' && typeof newCellSize.height === 'number' && newCellSize.width > 0 && newCellSize.height > 0) {
console.log('Cell size changed, updating canvas...', newCellSize);
updateCanvasSize();
updateCanvasContainerSize();
updateFrame();
} else {
console.warn('Invalid cell size detected in watcher', newCellSize);
}
}
},
{ deep: true }
);
// Expose openModal for external use
defineExpose({ openModal });
</script>

View File

@ -58,6 +58,13 @@ export function useSpritePosition(sprites: Ref<Sprite[]>, currentFrame: Ref<numb
if (sprites.value.length === 0) return;
if (isViewportDragging.value) return;
// Validate cell size before starting drag
if (!cellSize.value || typeof cellSize.value.width !== 'number' || typeof cellSize.value.height !== 'number' || cellSize.value.width <= 0 || cellSize.value.height <= 0) {
console.warn('Cannot start drag - invalid cell dimensions', cellSize.value ? `width: ${cellSize.value.width}, height: ${cellSize.value.height}` : 'cellSize is undefined');
showNotification('Cannot drag sprite - invalid cell dimensions', 'error');
return;
}
isCanvasDragging.value = true;
// Store initial position
@ -66,9 +73,13 @@ export function useSpritePosition(sprites: Ref<Sprite[]>, currentFrame: Ref<numb
y: e.clientY,
};
// Add event listeners
window.addEventListener('mousemove', e => handleCanvasDrag(e, previewZoom), { capture: true });
window.addEventListener('mouseup', stopCanvasDrag, { capture: true });
// Add event listeners with passive: false to ensure preventDefault works
const boundHandleCanvasDrag = (e: MouseEvent) => handleCanvasDrag(e, previewZoom);
window.addEventListener('mousemove', boundHandleCanvasDrag, { capture: true, passive: false });
window.addEventListener('mouseup', stopCanvasDrag, { capture: true, passive: false });
// Store the bound function for later removal
(window as any).__boundHandleCanvasDrag = boundHandleCanvasDrag;
e.preventDefault();
e.stopPropagation();
@ -82,9 +93,11 @@ export function useSpritePosition(sprites: Ref<Sprite[]>, currentFrame: Ref<numb
const sprite = sprites.value[currentFrame.value];
if (!sprite) return;
// Check if cellSize is properly defined
if (!cellSize.value || typeof cellSize.value.width !== 'number' || typeof cellSize.value.height !== 'number') {
console.warn('Invalid cell dimensions during drag operation');
// More robust check for cellSize validity
if (!cellSize.value || typeof cellSize.value.width !== 'number' || typeof cellSize.value.height !== 'number' || cellSize.value.width <= 0 || cellSize.value.height <= 0) {
console.warn('Invalid cell dimensions during drag operation', cellSize.value ? `width: ${cellSize.value.width}, height: ${cellSize.value.height}` : 'cellSize is undefined');
showNotification('Cannot drag sprite - invalid cell dimensions', 'error');
isCanvasDragging.value = false;
return;
}
@ -127,9 +140,13 @@ export function useSpritePosition(sprites: Ref<Sprite[]>, currentFrame: Ref<numb
if (!isCanvasDragging.value) return;
isCanvasDragging.value = false;
window.removeEventListener('mousemove', handleCanvasDrag as any, { capture: true });
// Use the stored bound function for removal
window.removeEventListener('mousemove', (window as any).__boundHandleCanvasDrag, { capture: true });
window.removeEventListener('mouseup', stopCanvasDrag, { capture: true });
// Clean up the reference
delete (window as any).__boundHandleCanvasDrag;
e.preventDefault();
e.stopPropagation();
};

View File

@ -56,9 +56,9 @@ export function useViewport(animation: Ref<AnimationState>, onUpdateFrame: () =>
y: e.clientY,
};
// Add temporary event listeners
window.addEventListener('mousemove', handleViewportDrag);
window.addEventListener('mouseup', stopViewportDrag);
// Add temporary event listeners with passive: false to ensure preventDefault works
window.addEventListener('mousemove', handleViewportDrag, { passive: false });
window.addEventListener('mouseup', stopViewportDrag, { passive: false });
// Prevent default to avoid text selection
e.preventDefault();
@ -67,6 +67,10 @@ export function useViewport(animation: Ref<AnimationState>, onUpdateFrame: () =>
const handleViewportDrag = (e: MouseEvent) => {
if (!isViewportDragging.value) return;
// Prevent default browser behavior
e.preventDefault();
e.stopPropagation();
const deltaX = e.clientX - viewportDragStart.value.x;
const deltaY = e.clientY - viewportDragStart.value.y;
@ -83,10 +87,18 @@ export function useViewport(animation: Ref<AnimationState>, onUpdateFrame: () =>
};
};
const stopViewportDrag = () => {
const stopViewportDrag = (e: MouseEvent) => {
if (!isViewportDragging.value) return;
isViewportDragging.value = false;
window.removeEventListener('mousemove', handleViewportDrag);
window.removeEventListener('mouseup', stopViewportDrag);
// Prevent default browser behavior
if (e) {
e.preventDefault();
e.stopPropagation();
}
};
const handleCanvasWheel = (e: WheelEvent) => {