This commit is contained in:
Dennis Postma 2025-04-05 13:30:37 +02:00
parent 3e849c0286
commit 5a7f7032ef
2 changed files with 82 additions and 49 deletions

View File

@ -113,7 +113,7 @@ export function createSpriteFromFile(file: File, index: number): Promise<Sprite>
/**
* Process multiple files and create sprites
*/
export async function processImageFiles(files: FileList): Promise<{ newSprites: Sprite[], errorCount: number }> {
export async function processImageFiles(files: FileList): Promise<{ newSprites: Sprite[]; errorCount: number }> {
const imageFiles = Array.from(files).filter(file => file.type.startsWith('image/'));
if (imageFiles.length === 0) {

View File

@ -46,6 +46,7 @@
<script setup lang="ts">
import { ref, computed, onMounted, onBeforeUnmount, watch, nextTick } from 'vue';
import { useSpritesheetStore } from '../composables/useSpritesheetStore';
import { logger, getPixelPerfectCoordinate } from '../application/utilities';
const store = useSpritesheetStore();
const canvasEl = ref<HTMLCanvasElement | null>(null);
@ -102,10 +103,9 @@
{ deep: true }
);
const setupCheckerboardPattern = () => {
// Remove this function or leave it empty since we don't need it anymore
};
/**
* Update canvas size based on container dimensions
*/
const updateCanvasSize = () => {
if (!canvasEl.value || !containerEl.value) return;
@ -114,11 +114,10 @@
containerHeight.value = containerEl.value.clientHeight;
// Set the base canvas size to fill the container
// These are the "unzoomed" dimensions
baseCanvasWidth.value = Math.max(containerWidth.value, store.cellSize.width * Math.ceil(containerWidth.value / store.cellSize.width));
baseCanvasHeight.value = Math.max(containerHeight.value, store.cellSize.height * Math.ceil(containerHeight.value / store.cellSize.height));
// Set the actual canvas dimensions - remove any zoom scaling here
// Set the actual canvas dimensions
canvasEl.value.width = baseCanvasWidth.value;
canvasEl.value.height = baseCanvasHeight.value;
@ -146,6 +145,9 @@
}
};
/**
* Handle mouse movement for tooltips and sprite dragging
*/
const handleMouseMove = (e: MouseEvent) => {
// Don't process sprite movement or tooltips while panning
if (isPanning.value) return;
@ -153,14 +155,15 @@
if (!canvasEl.value) return;
const rect = canvasEl.value.getBoundingClientRect();
// Adjust coordinates for zoom
// Adjust coordinates for zoom level
const x = (e.clientX - rect.left) / store.zoomLevel.value;
const y = (e.clientY - rect.top) / store.zoomLevel.value;
// Update tooltip
// Update tooltip with cell coordinates
const cellX = Math.floor(x / store.cellSize.width);
const cellY = Math.floor(y / store.cellSize.height);
// Show tooltip if mouse is within canvas bounds
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})`;
@ -170,7 +173,7 @@
isTooltipVisible.value = false;
}
// Move the sprite if we're dragging one
// Handle sprite dragging
if (store.draggedSprite.value) {
if (store.isShiftPressed.value) {
// Free positioning within the cell bounds when shift is pressed
@ -223,12 +226,11 @@
const boundedCellX = Math.max(0, Math.min(newCellX, maxCellX));
const boundedCellY = Math.max(0, Math.min(newCellY, maxCellY));
const oldX = store.draggedSprite.value.x;
const oldY = store.draggedSprite.value.y;
// Update the sprite position
store.draggedSprite.value.x = boundedCellX * store.cellSize.width;
store.draggedSprite.value.y = boundedCellY * store.cellSize.height;
// Update the sprite position with pixel-perfect coordinates
const newX = boundedCellX * store.cellSize.width;
const newY = boundedCellY * store.cellSize.height;
store.draggedSprite.value.x = newX;
store.draggedSprite.value.y = newY;
// When snapping to grid, reset any offsets for this sprite
const spriteIndex = store.sprites.value.findIndex(s => s.id === store.draggedSprite.value.id);
@ -258,6 +260,9 @@
store.draggedSprite.value = null;
};
/**
* Handle keyboard down events
*/
const handleKeyDown = (e: KeyboardEvent) => {
if (e.key === 'Shift') {
store.isShiftPressed.value = true;
@ -268,7 +273,7 @@
isAltPressed.value = true;
}
// Add keyboard shortcuts for zooming
// Handle keyboard shortcuts for zooming
if (e.ctrlKey || e.metaKey) {
if (e.key === '=' || e.key === '+') {
e.preventDefault();
@ -305,13 +310,18 @@
}
};
/**
* Handle mouse movement on the canvas for panning and sprite interactions
*/
const handleCanvasMouseMove = (e: MouseEvent) => {
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;
// Scroll the container in the opposite direction of the mouse movement
// for natural panning behavior
containerEl.value.scrollLeft -= dx;
containerEl.value.scrollTop -= dy;
@ -337,8 +347,14 @@
e.preventDefault();
};
/**
* Set up all canvas event listeners
*/
const setupCanvasEvents = () => {
if (!canvasEl.value) return;
if (!canvasEl.value) {
logger.warn('Cannot set up canvas events: canvas element not available');
return;
}
// Set up mouse events for the canvas
canvasEl.value.addEventListener('mousedown', handleCanvasMouseDown);
@ -353,35 +369,48 @@
updateCanvasSize();
};
/**
* Component lifecycle hook - setup
*/
onMounted(async () => {
// Set up global event listeners
window.addEventListener('keydown', handleKeyDown);
window.addEventListener('keyup', handleKeyUp);
window.addEventListener('resize', handleResize);
try {
// Set up global event listeners
window.addEventListener('keydown', handleKeyDown);
window.addEventListener('keyup', handleKeyUp);
window.addEventListener('resize', handleResize);
// Initialize the canvas
await nextTick();
initializeCanvas();
// Initialize the canvas after DOM is updated
await nextTick();
initializeCanvas();
// Observe container size changes
if ('ResizeObserver' in window) {
const resizeObserver = new ResizeObserver(() => {
updateCanvasSize();
});
// Set up ResizeObserver to handle container size changes
if ('ResizeObserver' in window) {
const resizeObserver = new ResizeObserver(() => {
updateCanvasSize();
});
if (containerEl.value) {
resizeObserver.observe(containerEl.value);
if (containerEl.value) {
resizeObserver.observe(containerEl.value);
}
}
} catch (error) {
logger.error('Error during component mount:', error);
}
});
/**
* Initialize the canvas and set up required event handlers
*/
const initializeCanvas = () => {
if (!canvasEl.value || !containerEl.value) return;
if (!canvasEl.value || !containerEl.value) {
logger.error('Canvas or container element not available');
return;
}
try {
const context = canvasEl.value.getContext('2d');
if (!context) {
console.error('Failed to get 2D context from canvas');
logger.error('Failed to get 2D context from canvas');
return;
}
@ -389,9 +418,6 @@
store.canvas.value = canvasEl.value;
store.ctx.value = context;
// Set up the checkerboard pattern
setupCheckerboardPattern();
// Set up canvas mouse events
setupCanvasEvents();
@ -405,23 +431,30 @@
store.renderSpritesheetPreview();
}
} catch (error) {
console.error('Error initializing canvas:', error);
logger.error('Error initializing canvas:', error);
}
};
/**
* Component lifecycle hook - cleanup
*/
onBeforeUnmount(() => {
// Remove global event listeners
window.removeEventListener('keydown', handleKeyDown);
window.removeEventListener('keyup', handleKeyUp);
window.removeEventListener('resize', handleResize);
try {
// Remove global event listeners
window.removeEventListener('keydown', handleKeyDown);
window.removeEventListener('keyup', handleKeyUp);
window.removeEventListener('resize', handleResize);
// Remove canvas event listeners
if (canvasEl.value) {
canvasEl.value.removeEventListener('mousedown', handleCanvasMouseDown);
canvasEl.value.removeEventListener('mousemove', handleCanvasMouseMove);
canvasEl.value.removeEventListener('mouseup', handleCanvasMouseUp);
canvasEl.value.removeEventListener('mouseleave', handleCanvasMouseLeave);
canvasEl.value.removeEventListener('contextmenu', preventContextMenu);
// Remove canvas event listeners
if (canvasEl.value) {
canvasEl.value.removeEventListener('mousedown', handleCanvasMouseDown);
canvasEl.value.removeEventListener('mousemove', handleCanvasMouseMove);
canvasEl.value.removeEventListener('mouseup', handleCanvasMouseUp);
canvasEl.value.removeEventListener('mouseleave', handleCanvasMouseLeave);
canvasEl.value.removeEventListener('contextmenu', preventContextMenu);
}
} catch (error) {
logger.error('Error during component unmount:', error);
}
});
</script>