This commit is contained in:
Dennis Postma 2025-04-04 03:55:20 +02:00
parent 1a16457efc
commit 293bed9135
2 changed files with 92 additions and 92 deletions

View File

@ -57,15 +57,7 @@
<button @click="zoomIn" :disabled="previewZoom >= 5" class="flex items-center justify-center w-8 h-8 bg-gray-700 text-gray-200 border border-gray-600 rounded transition-colors disabled:opacity-60 disabled:cursor-not-allowed hover:border-blue-500">
<i class="fas fa-search-plus"></i>
</button>
<button
@click="applyOffsetsToMainView"
:disabled="!hasSpriteOffset"
class="flex items-center gap-1 px-2 h-8 bg-gray-700 text-gray-200 border border-gray-600 rounded text-xs transition-colors disabled:opacity-60 disabled:cursor-not-allowed hover:border-blue-500"
title="Permanently apply offset to sprite position"
>
<i class="fas fa-save"></i>
Apply Offset
</button>
<!-- Apply Offset button removed -->
<button @click="resetZoom" :disabled="previewZoom === 1" class="flex items-center justify-center px-2 h-8 bg-gray-700 text-gray-200 border border-gray-600 rounded text-xs transition-colors disabled:opacity-60 disabled:cursor-not-allowed hover:border-blue-500">Reset Zoom</button>
</div>
@ -78,23 +70,17 @@
</label>
<!-- Reset sprite position button -->
<button
@click="resetSpritePosition"
:disabled="!hasSpriteOffset"
class="flex items-center gap-1 px-2 h-8 bg-gray-700 text-gray-200 border border-gray-600 rounded text-xs transition-colors disabled:opacity-60 disabled:cursor-not-allowed hover:border-blue-500"
title="Reset sprite to original position"
>
<button @click="resetSpritePosition" :disabled="!hasSpriteOffset" class="flex items-center gap-1 px-2 h-8 bg-gray-700 text-gray-200 border border-gray-600 rounded text-xs transition-colors disabled:opacity-60 disabled:cursor-not-allowed hover:border-blue-500" title="Center sprite in cell">
<i class="fas fa-crosshairs"></i>
Reset Position
Center Sprite
</button>
</div>
</div>
<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 -->
<div class="text-xs text-gray-400 mb-2" v-if="hasSpriteOffset || sprites.length > 0">
<span v-if="hasSpriteOffset">Sprite offset: {{ Math.round(spriteOffset.x) }}px, {{ Math.round(spriteOffset.y) }}px</span>
<span v-else>Click and drag the sprite to move it within the cell</span>
<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>
</div>
<div
@ -201,10 +187,7 @@
return spriteOffset.x !== 0 || spriteOffset.y !== 0;
});
const applyOffsetsToMainView = () => {
store.applyOffsetsToMainView();
store.showNotification('Offset permanently applied to sprite position');
};
// applyOffsetsToMainView function removed
const handleKeyDown = (e: KeyboardEvent) => {
if (!isModalOpen.value) return;
@ -245,33 +228,17 @@
resetSpritePosition();
} else {
// R: Reset both sprite position and viewport
// Reset the sprite offset for the current frame
const frameOffset = store.getSpriteOffset(currentFrame.value);
frameOffset.x = 0;
frameOffset.y = 0;
store.currentSpriteOffset.x = 0;
store.currentSpriteOffset.y = 0;
viewportOffset.value = { x: 0, y: 0 };
resetSpritePosition();
updateFrame();
store.showNotification('View and position reset');
}
e.preventDefault();
} else if (previewZoom.value > 1) {
// Arrow key navigation for panning when zoomed in
const panAmount = 10;
if (e.key === 'ArrowUp') {
viewportOffset.value.y += panAmount / previewZoom.value;
e.preventDefault();
} else if (e.key === 'ArrowDown') {
viewportOffset.value.y -= panAmount / previewZoom.value;
e.preventDefault();
} else if (e.key === 'ArrowLeft' && animation.value.isPlaying) {
viewportOffset.value.x += panAmount / previewZoom.value;
e.preventDefault();
} else if (e.key === 'ArrowRight' && animation.value.isPlaying) {
viewportOffset.value.x -= panAmount / previewZoom.value;
e.preventDefault();
}
} else if (e.key === 'ArrowLeft' && animation.value.isPlaying) {
viewportOffset.value.x += panAmount / previewZoom.value;
e.preventDefault();
} else if (e.key === 'ArrowRight' && animation.value.isPlaying) {
viewportOffset.value.x -= panAmount / previewZoom.value;
e.preventDefault();
}
};
@ -315,9 +282,22 @@
// Force render the first frame
if (sprites.value.length > 0) {
// Get the current sprite
const currentSprite = sprites.value[0];
// Calculate center position
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));
// Get the frame-specific offset for the first frame
const frameOffset = store.getSpriteOffset(0);
// If the offset is (0,0), center the sprite
if (frameOffset.x === 0 && frameOffset.y === 0) {
frameOffset.x = centerX;
frameOffset.y = centerY;
}
// Update the current offset for UI display
store.currentSpriteOffset.x = frameOffset.x;
store.currentSpriteOffset.y = frameOffset.y;
@ -448,22 +428,30 @@
});
};
// Reset sprite position to original
// Reset sprite position to center
const resetSpritePosition = () => {
// Reset the sprite offset for the current frame to zero
// Get current sprite
const currentSprite = sprites.value[currentFrame.value];
if (!currentSprite) return;
// Calculate center position
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));
// Reset the sprite offset for the current frame to the center position
const frameOffset = store.getSpriteOffset(currentFrame.value);
frameOffset.x = 0;
frameOffset.y = 0;
frameOffset.x = centerX;
frameOffset.y = centerY;
// Also update the current offset
store.currentSpriteOffset.x = 0;
store.currentSpriteOffset.y = 0;
store.currentSpriteOffset.x = centerX;
store.currentSpriteOffset.y = centerY;
// Update the frame to reflect the change
updateFrame();
// Show a notification
store.showNotification('Sprite position reset to original');
store.showNotification('Sprite position reset to center');
};
// Update canvas container size based on zoom level
@ -477,74 +465,85 @@
// Canvas drag functions for moving the sprite within its cell
const startCanvasDrag = (e: MouseEvent) => {
if (sprites.value.length === 0) return;
// Don't start sprite dragging if we're already dragging the viewport
if (isViewportDragging.value) return;
isCanvasDragging.value = true;
// Store initial position
canvasDragStart.value = {
x: e.clientX,
y: e.clientY,
};
// Add temporary event listeners
window.addEventListener('mousemove', handleCanvasDrag);
window.addEventListener('mouseup', stopCanvasDrag);
window.addEventListener('mousemove', handleCanvasDrag, { capture: true });
window.addEventListener('mouseup', stopCanvasDrag, { capture: true });
// Prevent default to avoid text selection
e.preventDefault();
e.stopPropagation();
};
const handleCanvasDrag = (e: MouseEvent) => {
if (!isCanvasDragging.value) return;
const deltaX = e.clientX - canvasDragStart.value.x;
const deltaY = e.clientY - canvasDragStart.value.y;
requestAnimationFrame(() => {
// Get the frame-specific offset
const frameOffset = store.getSpriteOffset(currentFrame.value);
// Calculate new position and round to nearest pixel
const newX = Math.round(frameOffset.x + deltaX / previewZoom.value);
const newY = Math.round(frameOffset.y + deltaY / previewZoom.value);
// Get current sprite
const currentSprite = sprites.value[currentFrame.value];
if (!currentSprite) return;
// Calculate maximum allowed offset based on sprite and cell size
const maxOffsetX = Math.floor((store.cellSize.width - currentSprite.width) / 2);
const maxOffsetY = Math.floor((store.cellSize.height - currentSprite.height) / 2);
// Calculate delta from last position
const deltaX = e.clientX - canvasDragStart.value.x;
const deltaY = e.clientY - canvasDragStart.value.y;
// Constrain movement to stay within cell boundaries, preventing negative offsets
const constrainedX = Math.max(0, Math.min(maxOffsetX, newX));
const constrainedY = Math.max(0, Math.min(maxOffsetY, newY));
// Only move when delta exceeds the threshold for one pixel movement at current zoom
const pixelThreshold = previewZoom.value; // One pixel at current zoom level
// Update both the current offset and the frame-specific offset
frameOffset.x = constrainedX;
frameOffset.y = constrainedY;
store.currentSpriteOffset.x = constrainedX;
store.currentSpriteOffset.y = constrainedY;
// Get the frame-specific offset
const frameOffset = store.getSpriteOffset(currentFrame.value);
// Reset drag start position
canvasDragStart.value = {
x: e.clientX,
y: e.clientY,
};
// Calculate the maximum allowed offset
const maxOffsetX = Math.max(0, store.cellSize.width - currentSprite.width);
const maxOffsetY = Math.max(0, store.cellSize.height - currentSprite.height);
// Update the frame with the new offset
// Move one pixel at a time when threshold is reached
if (Math.abs(deltaX) >= pixelThreshold) {
const pixelsToMove = Math.sign(deltaX);
const newX = frameOffset.x + pixelsToMove;
frameOffset.x = Math.max(0, Math.min(maxOffsetX, newX));
// Reset the start X position for next pixel move
canvasDragStart.value.x = e.clientX;
}
if (Math.abs(deltaY) >= pixelThreshold) {
const pixelsToMove = Math.sign(deltaY);
const newY = frameOffset.y + pixelsToMove;
frameOffset.y = Math.max(0, Math.min(maxOffsetY, newY));
// Reset the start Y position for next pixel move
canvasDragStart.value.y = e.clientY;
}
// Update the current offset to match
store.currentSpriteOffset.x = frameOffset.x;
store.currentSpriteOffset.y = frameOffset.y;
// Update the frame
updateFrame();
// Re-render the main view to reflect the changes
// Re-render the main view
store.renderSpritesheetPreview();
});
};
const stopCanvasDrag = () => {
const stopCanvasDrag = (e: MouseEvent) => {
if (!isCanvasDragging.value) return;
isCanvasDragging.value = false;
window.removeEventListener('mousemove', handleCanvasDrag);
window.removeEventListener('mouseup', stopCanvasDrag);
window.removeEventListener('mousemove', handleCanvasDrag, { capture: true });
window.removeEventListener('mouseup', stopCanvasDrag, { capture: true });
e.preventDefault();
e.stopPropagation();
};
// Canvas viewport navigation functions

View File

@ -527,8 +527,9 @@ export function useSpritesheetStore() {
const originalOffsetY = Math.round(currentSprite.y - cellY * cellSize.height);
// Calculate precise offset for pixel-perfect rendering, including the user's drag offset
const offsetX = originalOffsetX + spriteOffset.x;
const offsetY = originalOffsetY + spriteOffset.y;
// Use the spriteOffset directly as the position within the cell
const offsetX = spriteOffset.x;
const offsetY = spriteOffset.y;
// Draw the current sprite at full opacity at the new position
animation.ctx.drawImage(currentSprite.img, offsetX, offsetY);