Zoom fixes

This commit is contained in:
Dennis Postma 2025-04-03 03:47:25 +02:00
parent 319a052d48
commit 33efe08207
5 changed files with 383 additions and 412 deletions

View File

@ -11,16 +11,13 @@
</div> </div>
</div> </div>
<div class="p-6"> <div class="p-6">
<div ref="containerEl" class="relative overflow-auto bg-gray-700 rounded border border-gray-600 h-96" <div ref="containerEl" class="relative overflow-auto bg-gray-700 rounded border border-gray-600 h-96" :class="{ 'cursor-grab': !isPanning, 'cursor-grabbing': isPanning }">
:class="{ 'cursor-grab': !isPanning, 'cursor-grabbing': isPanning }">
<canvas <canvas
ref="canvasEl" ref="canvasEl"
class="block" class="block"
:style="{ :style="{
transform: `scale(${store.zoomLevel.value})`, transform: `scale(${store.zoomLevel.value})`,
transformOrigin: 'top left', transformOrigin: 'top left',
width: zoomedWidth + 'px',
height: zoomedHeight + 'px'
}" }"
></canvas> ></canvas>
</div> </div>
@ -30,46 +27,48 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, computed, onMounted, onBeforeUnmount, watch, nextTick } from 'vue'; import { ref, computed, onMounted, onBeforeUnmount, watch, nextTick } from 'vue';
import { useSpritesheetStore } from '../composables/useSpritesheetStore'; import { useSpritesheetStore } from '../composables/useSpritesheetStore';
const store = useSpritesheetStore(); const store = useSpritesheetStore();
const canvasEl = ref<HTMLCanvasElement | null>(null); const canvasEl = ref<HTMLCanvasElement | null>(null);
const containerEl = ref<HTMLDivElement | null>(null); const containerEl = ref<HTMLDivElement | null>(null);
// Panning state // Panning state
const isPanning = ref(false); const isPanning = ref(false);
const isAltPressed = ref(false); const isAltPressed = ref(false);
const isMiddleMouseDown = ref(false); const isMiddleMouseDown = ref(false);
const lastPosition = ref({ x: 0, y: 0 }); const lastPosition = ref({ x: 0, y: 0 });
// Tooltip state // Tooltip state
const isTooltipVisible = ref(false); const isTooltipVisible = ref(false);
const tooltipText = ref(''); const tooltipText = ref('');
const tooltipPosition = ref({ x: 0, y: 0 }); const tooltipPosition = ref({ x: 0, y: 0 });
// Responsive canvas sizing // Responsive canvas sizing
const containerWidth = ref(0); const containerWidth = ref(0);
const containerHeight = ref(0); const containerHeight = ref(0);
const baseCanvasWidth = ref(0); const baseCanvasWidth = ref(0);
const baseCanvasHeight = ref(0); const baseCanvasHeight = ref(0);
// Computed properties for zoomed dimensions // Computed properties for zoomed dimensions
const zoomedWidth = computed(() => { const zoomedWidth = computed(() => {
return baseCanvasWidth.value * store.zoomLevel.value; return baseCanvasWidth.value * store.zoomLevel.value;
}); });
const zoomedHeight = computed(() => { const zoomedHeight = computed(() => {
return baseCanvasHeight.value * store.zoomLevel.value; return baseCanvasHeight.value * store.zoomLevel.value;
}); });
const tooltipStyle = computed(() => ({ const tooltipStyle = computed(() => ({
left: `${tooltipPosition.value.x + 15}px`, left: `${tooltipPosition.value.x + 15}px`,
top: `${tooltipPosition.value.y + 15}px`, top: `${tooltipPosition.value.y + 15}px`,
})); }));
// Watch for zoom changes to update the container scroll position // Watch for zoom changes to update the container scroll position
watch(() => store.zoomLevel.value, (newZoom, oldZoom) => { watch(
() => store.zoomLevel.value,
(newZoom, oldZoom) => {
if (!containerEl.value) return; if (!containerEl.value) return;
// Adjust scroll position to keep the center point consistent when zooming // Adjust scroll position to keep the center point consistent when zooming
@ -84,9 +83,10 @@ watch(() => store.zoomLevel.value, (newZoom, oldZoom) => {
// Re-render the canvas with the new zoom level // Re-render the canvas with the new zoom level
updateCanvasSize(); updateCanvasSize();
store.renderSpritesheetPreview(); store.renderSpritesheetPreview();
}); }
);
const setupCheckerboardPattern = () => { const setupCheckerboardPattern = () => {
if (!canvasEl.value) return; if (!canvasEl.value) return;
canvasEl.value.style.backgroundImage = ` canvasEl.value.style.backgroundImage = `
@ -97,9 +97,9 @@ const setupCheckerboardPattern = () => {
`; `;
canvasEl.value.style.backgroundSize = '20px 20px'; canvasEl.value.style.backgroundSize = '20px 20px';
canvasEl.value.style.backgroundPosition = '0 0, 0 10px, 10px -10px, -10px 0px'; canvasEl.value.style.backgroundPosition = '0 0, 0 10px, 10px -10px, -10px 0px';
}; };
const updateCanvasSize = () => { const updateCanvasSize = () => {
if (!canvasEl.value || !containerEl.value) return; if (!canvasEl.value || !containerEl.value) return;
// Get the container dimensions // Get the container dimensions
@ -108,20 +108,18 @@ const updateCanvasSize = () => {
// Set the base canvas size to fill the container // Set the base canvas size to fill the container
// These are the "unzoomed" dimensions // These are the "unzoomed" dimensions
baseCanvasWidth.value = Math.max(containerWidth.value, baseCanvasWidth.value = Math.max(containerWidth.value, store.cellSize.width * Math.ceil(containerWidth.value / store.cellSize.width));
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));
baseCanvasHeight.value = Math.max(containerHeight.value,
store.cellSize.height * Math.ceil(containerHeight.value / store.cellSize.height));
// Update the actual canvas element size // Set the actual canvas dimensions - remove any zoom scaling here
canvasEl.value.width = baseCanvasWidth.value; canvasEl.value.width = baseCanvasWidth.value;
canvasEl.value.height = baseCanvasHeight.value; canvasEl.value.height = baseCanvasHeight.value;
// Trigger a re-render // Trigger a re-render
store.renderSpritesheetPreview(); store.renderSpritesheetPreview();
}; };
const handleMouseDown = (e: MouseEvent) => { const handleMouseDown = (e: MouseEvent) => {
if (!canvasEl.value || store.sprites.value.length === 0) return; if (!canvasEl.value || store.sprites.value.length === 0) return;
const rect = canvasEl.value.getBoundingClientRect(); const rect = canvasEl.value.getBoundingClientRect();
@ -139,9 +137,9 @@ const handleMouseDown = (e: MouseEvent) => {
break; break;
} }
} }
}; };
const handleMouseMove = (e: MouseEvent) => { const handleMouseMove = (e: MouseEvent) => {
// Don't process sprite movement or tooltips while panning // Don't process sprite movement or tooltips while panning
if (isPanning.value) return; if (isPanning.value) return;
@ -156,8 +154,7 @@ const handleMouseMove = (e: MouseEvent) => {
const cellX = Math.floor(x / store.cellSize.width); const cellX = Math.floor(x / store.cellSize.width);
const cellY = Math.floor(y / store.cellSize.height); const cellY = Math.floor(y / store.cellSize.height);
if (canvasEl.value && cellX >= 0 && cellX < canvasEl.value.width / store.cellSize.width && if (canvasEl.value && cellX >= 0 && cellX < canvasEl.value.width / store.cellSize.width && cellY >= 0 && cellY < canvasEl.value.height / store.cellSize.height) {
cellY >= 0 && cellY < canvasEl.value.height / store.cellSize.height) {
isTooltipVisible.value = true; isTooltipVisible.value = true;
tooltipText.value = `Cell: (${cellX}, ${cellY})`; tooltipText.value = `Cell: (${cellX}, ${cellY})`;
tooltipPosition.value.x = e.pageX; tooltipPosition.value.x = e.pageX;
@ -207,18 +204,18 @@ const handleMouseMove = (e: MouseEvent) => {
// Trigger a re-render // Trigger a re-render
store.renderSpritesheetPreview(); store.renderSpritesheetPreview();
} }
}; };
const handleMouseUp = () => { const handleMouseUp = () => {
store.draggedSprite.value = null; store.draggedSprite.value = null;
}; };
const handleMouseOut = () => { const handleMouseOut = () => {
isTooltipVisible.value = false; isTooltipVisible.value = false;
store.draggedSprite.value = null; store.draggedSprite.value = null;
}; };
const handleKeyDown = (e: KeyboardEvent) => { const handleKeyDown = (e: KeyboardEvent) => {
if (e.key === 'Shift') { if (e.key === 'Shift') {
store.isShiftPressed.value = true; store.isShiftPressed.value = true;
} }
@ -241,9 +238,9 @@ const handleKeyDown = (e: KeyboardEvent) => {
store.resetZoom(); store.resetZoom();
} }
} }
}; };
const handleKeyUp = (e: KeyboardEvent) => { const handleKeyUp = (e: KeyboardEvent) => {
if (e.key === 'Shift') { if (e.key === 'Shift') {
store.isShiftPressed.value = false; store.isShiftPressed.value = false;
} }
@ -251,9 +248,9 @@ const handleKeyUp = (e: KeyboardEvent) => {
if (e.key === 'Alt') { if (e.key === 'Alt') {
isAltPressed.value = false; isAltPressed.value = false;
} }
}; };
const handleCanvasMouseDown = (e: MouseEvent) => { const handleCanvasMouseDown = (e: MouseEvent) => {
// Middle mouse button or Alt + left click for panning // Middle mouse button or Alt + left click for panning
if (e.button === 1 || (e.button === 0 && isAltPressed.value)) { if (e.button === 1 || (e.button === 0 && isAltPressed.value)) {
e.preventDefault(); e.preventDefault();
@ -263,9 +260,9 @@ const handleCanvasMouseDown = (e: MouseEvent) => {
// Regular sprite dragging // Regular sprite dragging
handleMouseDown(e); handleMouseDown(e);
} }
}; };
const handleCanvasMouseMove = (e: MouseEvent) => { const handleCanvasMouseMove = (e: MouseEvent) => {
if (isPanning.value && containerEl.value) { if (isPanning.value && containerEl.value) {
e.preventDefault(); e.preventDefault();
const dx = e.clientX - lastPosition.value.x; const dx = e.clientX - lastPosition.value.x;
@ -281,23 +278,23 @@ const handleCanvasMouseMove = (e: MouseEvent) => {
// Handle regular mouse move for sprites and tooltip // Handle regular mouse move for sprites and tooltip
handleMouseMove(e); handleMouseMove(e);
} }
}; };
const handleCanvasMouseUp = () => { const handleCanvasMouseUp = () => {
isPanning.value = false; isPanning.value = false;
handleMouseUp(); handleMouseUp();
}; };
const handleCanvasMouseLeave = () => { const handleCanvasMouseLeave = () => {
isPanning.value = false; isPanning.value = false;
handleMouseOut(); handleMouseOut();
}; };
const preventContextMenu = (e: MouseEvent) => { const preventContextMenu = (e: MouseEvent) => {
e.preventDefault(); e.preventDefault();
}; };
const setupCanvasEvents = () => { const setupCanvasEvents = () => {
if (!canvasEl.value) return; if (!canvasEl.value) return;
// Set up mouse events for the canvas // Set up mouse events for the canvas
@ -306,14 +303,14 @@ const setupCanvasEvents = () => {
canvasEl.value.addEventListener('mouseup', handleCanvasMouseUp); canvasEl.value.addEventListener('mouseup', handleCanvasMouseUp);
canvasEl.value.addEventListener('mouseleave', handleCanvasMouseLeave); canvasEl.value.addEventListener('mouseleave', handleCanvasMouseLeave);
canvasEl.value.addEventListener('contextmenu', preventContextMenu); canvasEl.value.addEventListener('contextmenu', preventContextMenu);
}; };
// Handle window resize to update canvas dimensions // Handle window resize to update canvas dimensions
const handleResize = () => { const handleResize = () => {
updateCanvasSize(); updateCanvasSize();
}; };
onMounted(async () => { onMounted(async () => {
// Set up global event listeners // Set up global event listeners
window.addEventListener('keydown', handleKeyDown); window.addEventListener('keydown', handleKeyDown);
window.addEventListener('keyup', handleKeyUp); window.addEventListener('keyup', handleKeyUp);
@ -333,9 +330,9 @@ onMounted(async () => {
resizeObserver.observe(containerEl.value); resizeObserver.observe(containerEl.value);
} }
} }
}); });
const initializeCanvas = () => { const initializeCanvas = () => {
if (!canvasEl.value || !containerEl.value) return; if (!canvasEl.value || !containerEl.value) return;
try { try {
@ -367,9 +364,9 @@ const initializeCanvas = () => {
} catch (error) { } catch (error) {
console.error('Error initializing canvas:', error); console.error('Error initializing canvas:', error);
} }
}; };
onBeforeUnmount(() => { onBeforeUnmount(() => {
// Remove global event listeners // Remove global event listeners
window.removeEventListener('keydown', handleKeyDown); window.removeEventListener('keydown', handleKeyDown);
window.removeEventListener('keyup', handleKeyUp); window.removeEventListener('keyup', handleKeyUp);
@ -383,15 +380,15 @@ onBeforeUnmount(() => {
canvasEl.value.removeEventListener('mouseleave', handleCanvasMouseLeave); canvasEl.value.removeEventListener('mouseleave', handleCanvasMouseLeave);
canvasEl.value.removeEventListener('contextmenu', preventContextMenu); canvasEl.value.removeEventListener('contextmenu', preventContextMenu);
} }
}); });
</script> </script>
<style scoped> <style scoped>
.cursor-grab { .cursor-grab {
cursor: grab; cursor: grab;
} }
.cursor-grabbing { .cursor-grabbing {
cursor: grabbing; cursor: grabbing;
} }
</style> </style>

View File

@ -1,5 +1,7 @@
<template> <template>
<div class="fixed inset-0 flex items-center justify-center z-50" :class="{ 'pointer-events-none': !isModalOpen }"> <!-- Make the outer container always pointer-events-none so clicks pass through -->
<div class="fixed inset-0 flex items-center justify-center z-50 pointer-events-none">
<!-- Apply pointer-events-auto ONLY to the modal itself so it can be interacted with -->
<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="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 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"> <div class="flex items-center gap-2 text-lg font-semibold select-none">
@ -73,6 +75,9 @@
const animation = computed(() => store.animation); const animation = computed(() => store.animation);
const currentFrame = ref(0); const currentFrame = ref(0);
const position = ref({ x: 0, y: 0 });
const isDragging = ref(false);
const dragOffset = ref({ x: 0, y: 0 });
const currentFrameDisplay = computed(() => { const currentFrameDisplay = computed(() => {
const totalFrames = Math.max(1, sprites.value.length); const totalFrames = Math.max(1, sprites.value.length);
@ -117,7 +122,6 @@
currentFrame.value = 0; currentFrame.value = 0;
animation.value.currentFrame = 0; animation.value.currentFrame = 0;
// Make sure the canvas is initialized
await initializeCanvas(); await initializeCanvas();
// Set modal open state if not already open // Set modal open state if not already open
@ -129,18 +133,21 @@
await new Promise(resolve => requestAnimationFrame(resolve)); await new Promise(resolve => requestAnimationFrame(resolve));
// Set proper canvas size before rendering // Set proper canvas size before rendering
if (animCanvas.value && store.cellSize.width && store.cellSize.height) { updateCanvasSize();
animCanvas.value.width = store.cellSize.width;
animCanvas.value.height = store.cellSize.height;
}
// Force render the first frame // Force render the first frame
if (sprites.value.length > 0) { if (sprites.value.length > 0) {
// Ensure we have the latest sprites
store.renderAnimationFrame(0); store.renderAnimationFrame(0);
} }
}; };
const updateCanvasSize = () => {
if (animCanvas.value && store.cellSize.width && store.cellSize.height) {
animCanvas.value.width = store.cellSize.width;
animCanvas.value.height = store.cellSize.height;
}
};
const closeModal = () => { const closeModal = () => {
store.isModalOpen.value = false; store.isModalOpen.value = false;
@ -181,44 +188,6 @@
} }
}; };
onMounted(() => {
initializeCanvas();
// 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) => { const startDrag = (e: MouseEvent) => {
isDragging.value = true; isDragging.value = true;
dragOffset.value = { dragOffset.value = {
@ -248,6 +217,37 @@
window.removeEventListener('mouseup', stopDrag); window.removeEventListener('mouseup', stopDrag);
}; };
const initializeCanvas = async () => {
if (!animCanvas.value) {
console.error('PreviewModal: Animation canvas not found');
return;
}
try {
const context = animCanvas.value.getContext('2d');
if (!context) {
console.error('PreviewModal: Failed to get 2D context from animation canvas');
return;
}
store.animation.canvas = animCanvas.value;
store.animation.ctx = context;
} catch (error) {
console.error('PreviewModal: Error initializing animation canvas:', error);
}
};
onMounted(() => {
initializeCanvas();
window.addEventListener('keydown', handleKeyDown);
});
onBeforeUnmount(() => {
window.removeEventListener('keydown', handleKeyDown);
window.removeEventListener('mousemove', handleDrag);
window.removeEventListener('mouseup', stopDrag);
});
// Keep currentFrame in sync with animation.currentFrame // Keep currentFrame in sync with animation.currentFrame
watch( watch(
() => animation.value.currentFrame, () => animation.value.currentFrame,
@ -261,13 +261,9 @@
() => sprites.value, () => sprites.value,
newSprites => { newSprites => {
if (isModalOpen.value && newSprites.length > 0) { if (isModalOpen.value && newSprites.length > 0) {
// Update the canvas with the new sprites updateCanvasSize();
if (animCanvas.value && store.cellSize.width && store.cellSize.height) {
animCanvas.value.width = store.cellSize.width;
animCanvas.value.height = store.cellSize.height;
store.renderAnimationFrame(currentFrame.value); store.renderAnimationFrame(currentFrame.value);
} }
}
}, },
{ deep: true } { deep: true }
); );
@ -294,12 +290,12 @@
cursor: pointer; cursor: pointer;
} }
/* Optional: Prevent text selection while dragging */ /* Prevent text selection while dragging */
.cursor-move { .cursor-move {
user-select: none; user-select: none;
} }
/* Add these new styles */ /* Hide scrollbar styles */
.scrollbar-hide { .scrollbar-hide {
-ms-overflow-style: none; /* IE and Edge */ -ms-overflow-style: none; /* IE and Edge */
scrollbar-width: none; /* Firefox */ scrollbar-width: none; /* Firefox */

View File

@ -53,15 +53,9 @@
<!-- Zoom Controls --> <!-- Zoom Controls -->
<div class="flex flex-wrap gap-3 mb-6"> <div class="flex flex-wrap gap-3 mb-6">
<div class="text-sm font-medium text-gray-300 mb-2 w-full">Zoom Controls</div> <div class="text-sm font-medium text-gray-300 mb-2 w-full">Zoom Controls</div>
<button @click="zoomIn" class="flex items-center gap-2 bg-gray-700 text-gray-200 border border-gray-600 rounded px-4 py-2 text-sm transition-colors hover:border-blue-500"> <button @click="zoomIn" class="flex items-center gap-2 bg-gray-700 text-gray-200 border border-gray-600 rounded px-4 py-2 text-sm transition-colors hover:border-blue-500"><i class="fas fa-search-plus"></i> Zoom In</button>
<i class="fas fa-search-plus"></i> Zoom In <button @click="zoomOut" class="flex items-center gap-2 bg-gray-700 text-gray-200 border border-gray-600 rounded px-4 py-2 text-sm transition-colors hover:border-blue-500"><i class="fas fa-search-minus"></i> Zoom Out</button>
</button> <button @click="resetZoom" class="flex items-center gap-2 bg-gray-700 text-gray-200 border border-gray-600 rounded px-4 py-2 text-sm transition-colors hover:border-blue-500"><i class="fas fa-sync-alt"></i> Reset Zoom</button>
<button @click="zoomOut" class="flex items-center gap-2 bg-gray-700 text-gray-200 border border-gray-600 rounded px-4 py-2 text-sm transition-colors hover:border-blue-500">
<i class="fas fa-search-minus"></i> Zoom Out
</button>
<button @click="resetZoom" class="flex items-center gap-2 bg-gray-700 text-gray-200 border border-gray-600 rounded px-4 py-2 text-sm transition-colors hover:border-blue-500">
<i class="fas fa-sync-alt"></i> Reset Zoom
</button>
</div> </div>
<div class="flex flex-wrap gap-3 mt-4"> <div class="flex flex-wrap gap-3 mt-4">

View File

@ -1,7 +1,7 @@
<template> <template>
<div v-if="sprites.length === 0" class="text-center text-gray-400 py-8">No sprites uploaded yet</div> <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 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 p-4">
<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"> <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" /> <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> <div class="text-xs text-gray-400 truncate">{{ index + 1 }}. {{ truncateName(sprite.name) }}</div>

View File

@ -146,9 +146,11 @@ export function useSpritesheetStore() {
canvas.value.height = newHeight; canvas.value.height = newHeight;
// Emit an event to update the wrapper dimensions // Emit an event to update the wrapper dimensions
window.dispatchEvent(new CustomEvent('canvas-size-updated', { window.dispatchEvent(
detail: { width: newWidth, height: newHeight } new CustomEvent('canvas-size-updated', {
})); detail: { width: newWidth, height: newHeight },
})
);
} catch (error) { } catch (error) {
console.error('Store: Error updating canvas size:', error); console.error('Store: Error updating canvas size:', error);
} }
@ -191,8 +193,6 @@ export function useSpritesheetStore() {
function renderSpritesheetPreview(showGrid = true) { function renderSpritesheetPreview(showGrid = true) {
if (!ctx.value || !canvas.value) { if (!ctx.value || !canvas.value) {
console.error('Store: Canvas or context not available for rendering, will retry when ready'); 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(() => { setTimeout(() => {
if (ctx.value && canvas.value) { if (ctx.value && canvas.value) {
renderSpritesheetPreview(showGrid); renderSpritesheetPreview(showGrid);
@ -201,26 +201,17 @@ export function useSpritesheetStore() {
return; return;
} }
if (sprites.value.length === 0) { if (sprites.value.length === 0) return;
return;
}
try { try {
// Make sure canvas dimensions are set correctly
updateCanvasSize();
// Clear the canvas // Clear the canvas
ctx.value.clearRect(0, 0, canvas.value.width, canvas.value.height); ctx.value.clearRect(0, 0, canvas.value.width, canvas.value.height);
// Apply zoom transformation
ctx.value.save();
ctx.value.scale(zoomLevel.value, zoomLevel.value);
if (showGrid) { if (showGrid) {
drawGrid(); drawGrid();
} }
// Draw each sprite // Draw each sprite - remove the zoom scaling from context
sprites.value.forEach((sprite, index) => { sprites.value.forEach((sprite, index) => {
try { try {
if (!sprite.img) { if (!sprite.img) {
@ -229,17 +220,13 @@ export function useSpritesheetStore() {
} }
if (sprite.img.complete && sprite.img.naturalWidth !== 0) { if (sprite.img.complete && sprite.img.naturalWidth !== 0) {
ctx.value!.drawImage(sprite.img, sprite.x, sprite.y); // Draw the image at its original size
ctx.value!.drawImage(sprite.img, sprite.x, sprite.y, sprite.width, sprite.height);
} else { } else {
console.warn(`Store: Sprite image ${index} not fully loaded, setting onload handler`); 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 = () => { sprite.img.onload = () => {
if (ctx.value && canvas.value) { if (ctx.value && canvas.value) {
ctx.value.save(); ctx.value.drawImage(sprite.img, sprite.x, sprite.y, sprite.width, sprite.height);
ctx.value.scale(zoomLevel.value, zoomLevel.value);
ctx.value.drawImage(sprite.img, sprite.x, sprite.y);
ctx.value.restore();
} }
}; };
} }
@ -247,11 +234,8 @@ export function useSpritesheetStore() {
console.error(`Store: Error rendering sprite at index ${index}:`, spriteError); console.error(`Store: Error rendering sprite at index ${index}:`, spriteError);
} }
}); });
// Restore the canvas state
ctx.value.restore();
} catch (error) { } catch (error) {
console.error('Store: Error rendering spritesheet preview:', error); console.error('Store: Error in renderSpritesheetPreview:', error);
} }
} }