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

View File

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