This commit is contained in:
Dennis Postma 2025-04-04 13:35:09 +02:00
parent 293bed9135
commit 8eec236105
2 changed files with 80 additions and 51 deletions

View File

@ -80,7 +80,7 @@
<div class="flex flex-col justify-center items-center bg-gray-700 p-6 rounded mb-6 relative overflow-auto flex-grow"> <div class="flex flex-col justify-center items-center bg-gray-700 p-6 rounded mb-6 relative overflow-auto flex-grow">
<!-- Tooltip for dragging instructions --> <!-- Tooltip for dragging instructions -->
<div class="text-xs text-gray-400 mb-2" v-if="sprites.length > 0"> <div class="text-xs text-gray-400 mb-2" v-if="sprites.length > 0">
<span>Position: {{ Math.round(spriteOffset.x) }}px, {{ Math.round(spriteOffset.y) }}px (drag to move within cell)</span> <span>Position: {{ Math.round(spriteOffset?.x ?? 0) }}px, {{ Math.round(spriteOffset?.y ?? 0) }}px (drag to move within cell)</span>
</div> </div>
<div <div
@ -129,6 +129,9 @@
const store = useSpritesheetStore(); const store = useSpritesheetStore();
const animCanvas = ref<HTMLCanvasElement | null>(null); const animCanvas = ref<HTMLCanvasElement | null>(null);
// Add this constant for pan amount
const panAmount = 10; // pixels to pan per keypress
const isModalOpen = computed({ const isModalOpen = computed({
get: () => store.isModalOpen.value, get: () => store.isModalOpen.value,
set: value => { set: value => {
@ -150,20 +153,18 @@
get: () => { get: () => {
// Get the offset for the current frame // Get the offset for the current frame
const frameOffset = store.getSpriteOffset(currentFrame.value); const frameOffset = store.getSpriteOffset(currentFrame.value);
// Update the current offset for UI display // Return the frame-specific offset directly
store.currentSpriteOffset.x = frameOffset.x; return frameOffset;
store.currentSpriteOffset.y = frameOffset.y;
return store.currentSpriteOffset;
}, },
set: val => { set: val => {
// Update both the current offset and the frame-specific offset // Update the frame-specific offset directly
store.currentSpriteOffset.x = val.x;
store.currentSpriteOffset.y = val.y;
// Get the frame-specific offset and update it
const frameOffset = store.getSpriteOffset(currentFrame.value); const frameOffset = store.getSpriteOffset(currentFrame.value);
frameOffset.x = val.x; frameOffset.x = val.x;
frameOffset.y = val.y; frameOffset.y = val.y;
// Also update the current offset for UI consistency
store.currentSpriteOffset.x = val.x;
store.currentSpriteOffset.y = val.y;
}, },
}); });
const isCanvasDragging = ref(false); const isCanvasDragging = ref(false);
@ -184,7 +185,7 @@
// Computed property to check if sprite has been moved from original position // Computed property to check if sprite has been moved from original position
const hasSpriteOffset = computed(() => { const hasSpriteOffset = computed(() => {
return spriteOffset.x !== 0 || spriteOffset.y !== 0; return spriteOffset.value.x !== 0 || spriteOffset.value.y !== 0;
}); });
// applyOffsetsToMainView function removed // applyOffsetsToMainView function removed
@ -298,10 +299,6 @@
frameOffset.y = centerY; frameOffset.y = centerY;
} }
// Update the current offset for UI display
store.currentSpriteOffset.x = frameOffset.x;
store.currentSpriteOffset.y = frameOffset.y;
// Render with the frame-specific offset // Render with the frame-specific offset
store.renderAnimationFrame(0, showAllSprites.value, frameOffset); store.renderAnimationFrame(0, showAllSprites.value, frameOffset);
} }
@ -357,9 +354,8 @@
animation.value.currentFrame = currentFrame.value; animation.value.currentFrame = currentFrame.value;
animation.value.manualUpdate = true; animation.value.manualUpdate = true;
// Get the frame-specific offset // Use the computed spriteOffset directly
const frameOffset = store.getSpriteOffset(currentFrame.value); store.renderAnimationFrame(currentFrame.value, showAllSprites.value, spriteOffset.value);
store.renderAnimationFrame(currentFrame.value, showAllSprites.value, frameOffset);
}; };
const handleFrameRateChange = () => { const handleFrameRateChange = () => {
@ -438,14 +434,8 @@
const centerX = Math.max(0, Math.floor((store.cellSize.width - currentSprite.width) / 2)); const centerX = Math.max(0, Math.floor((store.cellSize.width - currentSprite.width) / 2));
const centerY = Math.max(0, Math.floor((store.cellSize.height - currentSprite.height) / 2)); const centerY = Math.max(0, Math.floor((store.cellSize.height - currentSprite.height) / 2));
// Reset the sprite offset for the current frame to the center position // Update the sprite offset using the computed property setter
const frameOffset = store.getSpriteOffset(currentFrame.value); spriteOffset.value = { x: centerX, y: centerY };
frameOffset.x = centerX;
frameOffset.y = centerY;
// Also update the current offset
store.currentSpriteOffset.x = centerX;
store.currentSpriteOffset.y = centerY;
// Update the frame to reflect the change // Update the frame to reflect the change
updateFrame(); updateFrame();
@ -497,9 +487,6 @@
// Only move when delta exceeds the threshold for one pixel movement at current zoom // Only move when delta exceeds the threshold for one pixel movement at current zoom
const pixelThreshold = previewZoom.value; // One pixel at current zoom level const pixelThreshold = previewZoom.value; // One pixel at current zoom level
// Get the frame-specific offset
const frameOffset = store.getSpriteOffset(currentFrame.value);
// Calculate the maximum allowed offset // Calculate the maximum allowed offset
const maxOffsetX = Math.max(0, store.cellSize.width - currentSprite.width); const maxOffsetX = Math.max(0, store.cellSize.width - currentSprite.width);
const maxOffsetY = Math.max(0, store.cellSize.height - currentSprite.height); const maxOffsetY = Math.max(0, store.cellSize.height - currentSprite.height);
@ -507,8 +494,8 @@
// Move one pixel at a time when threshold is reached // Move one pixel at a time when threshold is reached
if (Math.abs(deltaX) >= pixelThreshold) { if (Math.abs(deltaX) >= pixelThreshold) {
const pixelsToMove = Math.sign(deltaX); const pixelsToMove = Math.sign(deltaX);
const newX = frameOffset.x + pixelsToMove; const newX = spriteOffset.value.x + pixelsToMove;
frameOffset.x = Math.max(0, Math.min(maxOffsetX, newX)); spriteOffset.value.x = Math.max(0, Math.min(maxOffsetX, newX));
// Reset the start X position for next pixel move // Reset the start X position for next pixel move
canvasDragStart.value.x = e.clientX; canvasDragStart.value.x = e.clientX;
@ -516,17 +503,13 @@
if (Math.abs(deltaY) >= pixelThreshold) { if (Math.abs(deltaY) >= pixelThreshold) {
const pixelsToMove = Math.sign(deltaY); const pixelsToMove = Math.sign(deltaY);
const newY = frameOffset.y + pixelsToMove; const newY = spriteOffset.value.y + pixelsToMove;
frameOffset.y = Math.max(0, Math.min(maxOffsetY, newY)); spriteOffset.value.y = Math.max(0, Math.min(maxOffsetY, newY));
// Reset the start Y position for next pixel move // Reset the start Y position for next pixel move
canvasDragStart.value.y = e.clientY; canvasDragStart.value.y = e.clientY;
} }
// Update the current offset to match
store.currentSpriteOffset.x = frameOffset.x;
store.currentSpriteOffset.y = frameOffset.y;
// Update the frame // Update the frame
updateFrame(); updateFrame();
@ -651,7 +634,7 @@
if (isModalOpen.value && newSprites.length > 0) { if (isModalOpen.value && newSprites.length > 0) {
updateCanvasSize(); updateCanvasSize();
updateCanvasContainerSize(); updateCanvasContainerSize();
store.renderAnimationFrame(currentFrame.value, showAllSprites.value, spriteOffset.value); updateFrame();
} }
}, },
{ deep: true } { deep: true }
@ -670,7 +653,7 @@
() => previewBorder.value, () => previewBorder.value,
() => { () => {
if (isModalOpen.value && sprites.value.length > 0) { if (isModalOpen.value && sprites.value.length > 0) {
store.renderAnimationFrame(currentFrame.value, showAllSprites.value, spriteOffset.value); updateFrame();
} }
}, },
{ deep: true } { deep: true }
@ -681,7 +664,7 @@
() => showAllSprites.value, () => showAllSprites.value,
() => { () => {
if (isModalOpen.value && sprites.value.length > 0) { if (isModalOpen.value && sprites.value.length > 0) {
store.renderAnimationFrame(currentFrame.value, showAllSprites.value, spriteOffset.value); updateFrame();
} }
} }
); );

View File

@ -259,9 +259,21 @@ export function useSpritesheetStore() {
// Get the frame-specific offset for this sprite // Get the frame-specific offset for this sprite
const frameOffset = getSpriteOffset(index); const frameOffset = getSpriteOffset(index);
// Apply the frame-specific offset to the sprite position // Calculate the maximum allowed offset based on sprite and cell size
const finalX = x + frameOffset.x; const maxOffsetX = Math.max(0, cellSize.width - sprite.width);
const finalY = y + frameOffset.y; const maxOffsetY = Math.max(0, cellSize.height - sprite.height);
// Constrain the offset to prevent out-of-bounds positioning
const constrainedOffsetX = Math.max(0, Math.min(maxOffsetX, frameOffset.x));
const constrainedOffsetY = Math.max(0, Math.min(maxOffsetY, frameOffset.y));
// Update the frame offset with the constrained values
frameOffset.x = constrainedOffsetX;
frameOffset.y = constrainedOffsetY;
// Apply the constrained offset to the sprite position
const finalX = x + constrainedOffsetX;
const finalY = y + constrainedOffsetY;
// Draw the image at its final position with pixel-perfect rendering // Draw the image at its final position with pixel-perfect rendering
ctx.value!.imageSmoothingEnabled = false; // Keep pixel art sharp ctx.value!.imageSmoothingEnabled = false; // Keep pixel art sharp
@ -277,9 +289,21 @@ export function useSpritesheetStore() {
// Get the frame-specific offset for this sprite // Get the frame-specific offset for this sprite
const frameOffset = getSpriteOffset(index); const frameOffset = getSpriteOffset(index);
// Apply the frame-specific offset to the sprite position // Calculate the maximum allowed offset based on sprite and cell size
const finalX = x + frameOffset.x; const maxOffsetX = Math.max(0, cellSize.width - sprite.width);
const finalY = y + frameOffset.y; const maxOffsetY = Math.max(0, cellSize.height - sprite.height);
// Constrain the offset to prevent out-of-bounds positioning
const constrainedOffsetX = Math.max(0, Math.min(maxOffsetX, frameOffset.x));
const constrainedOffsetY = Math.max(0, Math.min(maxOffsetY, frameOffset.y));
// Update the frame offset with the constrained values
frameOffset.x = constrainedOffsetX;
frameOffset.y = constrainedOffsetY;
// Apply the constrained offset to the sprite position
const finalX = x + constrainedOffsetX;
const finalY = y + constrainedOffsetY;
ctx.value.imageSmoothingEnabled = false; // Keep pixel art sharp ctx.value.imageSmoothingEnabled = false; // Keep pixel art sharp
ctx.value.drawImage(sprite.img, finalX, finalY, sprite.width, sprite.height); ctx.value.drawImage(sprite.img, finalX, finalY, sprite.width, sprite.height);
@ -432,11 +456,33 @@ export function useSpritesheetStore() {
// Ensure pixel art remains sharp in the downloaded file // Ensure pixel art remains sharp in the downloaded file
tempCtx.imageSmoothingEnabled = false; tempCtx.imageSmoothingEnabled = false;
sprites.value.forEach(sprite => { sprites.value.forEach((sprite, index) => {
// Use rounded coordinates for pixel-perfect rendering // Get the frame-specific offset for this sprite
const x = Math.round(sprite.x); const frameOffset = getSpriteOffset(index);
const y = Math.round(sprite.y);
tempCtx.drawImage(sprite.img, x, y); // Calculate the cell coordinates for this sprite
const cellX = Math.floor(sprite.x / cellSize.width);
const cellY = Math.floor(sprite.y / cellSize.height);
// Calculate the base position within the cell
const baseX = cellX * cellSize.width;
const baseY = cellY * cellSize.height;
// Calculate the maximum allowed offset based on sprite and cell size
// This prevents sprites from going out of bounds
const maxOffsetX = Math.max(0, cellSize.width - sprite.width);
const maxOffsetY = Math.max(0, cellSize.height - sprite.height);
// Constrain the offset to prevent out-of-bounds positioning
const constrainedOffsetX = Math.max(0, Math.min(maxOffsetX, frameOffset.x));
const constrainedOffsetY = Math.max(0, Math.min(maxOffsetY, frameOffset.y));
// Apply the constrained offset to the base position
const finalX = baseX + constrainedOffsetX;
const finalY = baseY + constrainedOffsetY;
// Draw the sprite at the calculated position
tempCtx.drawImage(sprite.img, finalX, finalY, sprite.width, sprite.height);
}); });
const link = document.createElement('a'); const link = document.createElement('a');