Work
This commit is contained in:
parent
5a7f7032ef
commit
49b67dd10a
@ -120,11 +120,48 @@ export function renderSpritesheetPreview(showGrid = true) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if sprite is within canvas bounds
|
// Calculate the cell coordinates for this sprite
|
||||||
if (sprite.x >= 0 && sprite.y >= 0 && sprite.x + sprite.width <= canvas.value!.width && sprite.y + sprite.height <= canvas.value!.height) {
|
const cellX = Math.floor(sprite.x / cellSize.width);
|
||||||
renderSpriteOnCanvas(sprite, index);
|
const cellY = Math.floor(sprite.y / cellSize.height);
|
||||||
|
|
||||||
|
// Calculate cell boundaries
|
||||||
|
const cellLeft = cellX * cellSize.width;
|
||||||
|
const cellTop = cellY * cellSize.height;
|
||||||
|
|
||||||
|
// Get the frame-specific offset for this sprite
|
||||||
|
const frameOffset = getSpriteOffset(index);
|
||||||
|
|
||||||
|
// Calculate the maximum allowed offset based on sprite and cell size
|
||||||
|
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));
|
||||||
|
|
||||||
|
// Calculate final position ensuring sprite stays within cell bounds
|
||||||
|
const finalX = Math.max(cellLeft, Math.min(cellLeft + maxOffsetX, cellLeft + constrainedOffsetX));
|
||||||
|
const finalY = Math.max(cellTop, Math.min(cellTop + maxOffsetY, cellTop + constrainedOffsetY));
|
||||||
|
|
||||||
|
// Update sprite position to stay within bounds
|
||||||
|
sprite.x = finalX;
|
||||||
|
sprite.y = finalY;
|
||||||
|
|
||||||
|
// Update the frame offset with the constrained values
|
||||||
|
frameOffset.x = finalX - cellLeft;
|
||||||
|
frameOffset.y = finalY - cellTop;
|
||||||
|
|
||||||
|
// Draw the image at its final position with pixel-perfect rendering
|
||||||
|
if (isImageReady(sprite.img)) {
|
||||||
|
ctx.value.imageSmoothingEnabled = false; // Keep pixel art sharp
|
||||||
|
ctx.value.drawImage(sprite.img, finalX, finalY, sprite.width, sprite.height);
|
||||||
} else {
|
} else {
|
||||||
logger.warn(`Sprite at index ${index} is outside canvas bounds: sprite(${sprite.x},${sprite.y}) canvas(${canvas.value!.width},${canvas.value!.height})`);
|
logger.warn(`Sprite image ${index} not fully loaded, setting onload handler`);
|
||||||
|
sprite.img.onload = () => {
|
||||||
|
if (ctx.value && canvas.value) {
|
||||||
|
renderSpriteOnCanvas(sprite, index);
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
} catch (spriteError) {
|
} catch (spriteError) {
|
||||||
logger.error(`Error rendering sprite at index ${index}:`, spriteError);
|
logger.error(`Error rendering sprite at index ${index}:`, spriteError);
|
||||||
|
@ -30,6 +30,8 @@ export function addSprites(newSprites: Sprite[]) {
|
|||||||
|
|
||||||
sprites.value.push(...validSprites);
|
sprites.value.push(...validSprites);
|
||||||
sprites.value.sort((a, b) => a.uploadOrder - b.uploadOrder);
|
sprites.value.sort((a, b) => a.uploadOrder - b.uploadOrder);
|
||||||
|
|
||||||
|
// Update cell size before arranging sprites
|
||||||
updateCellSize();
|
updateCellSize();
|
||||||
autoArrangeSprites();
|
autoArrangeSprites();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -49,8 +51,9 @@ export function updateCellSize() {
|
|||||||
let maxWidth = 0;
|
let maxWidth = 0;
|
||||||
let maxHeight = 0;
|
let maxHeight = 0;
|
||||||
|
|
||||||
|
// Find the maximum dimensions across all sprites
|
||||||
sprites.value.forEach(sprite => {
|
sprites.value.forEach(sprite => {
|
||||||
if (sprite.width <= 0 || sprite.height <= 0) {
|
if (!sprite.img || sprite.width <= 0 || sprite.height <= 0) {
|
||||||
logger.warn('Sprite with invalid dimensions detected', sprite);
|
logger.warn('Sprite with invalid dimensions detected', sprite);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -63,10 +66,30 @@ export function updateCellSize() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
cellSize.width = maxWidth;
|
// Add a small buffer to ensure sprites fit completely (optional)
|
||||||
cellSize.height = maxHeight;
|
const buffer = 0; // Increase if you want padding between sprites
|
||||||
|
cellSize.width = maxWidth + buffer;
|
||||||
|
cellSize.height = maxHeight + buffer;
|
||||||
|
|
||||||
|
// Ensure all sprites are within their cell bounds after resize
|
||||||
|
sprites.value.forEach((sprite, index) => {
|
||||||
|
const column = index % columns.value;
|
||||||
|
const row = Math.floor(index / columns.value);
|
||||||
|
|
||||||
|
// Calculate base position for the sprite's cell
|
||||||
|
const cellX = column * cellSize.width;
|
||||||
|
const cellY = row * cellSize.height;
|
||||||
|
|
||||||
|
// Center the sprite within its cell if smaller than cell size
|
||||||
|
const offsetX = Math.floor((cellSize.width - sprite.width) / 2);
|
||||||
|
const offsetY = Math.floor((cellSize.height - sprite.height) / 2);
|
||||||
|
|
||||||
|
sprite.x = cellX + offsetX;
|
||||||
|
sprite.y = cellY + offsetY;
|
||||||
|
});
|
||||||
|
|
||||||
updateCanvasSize();
|
updateCanvasSize();
|
||||||
|
renderSpritesheetPreview();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error('Error updating cell size:', error);
|
logger.error('Error updating cell size:', error);
|
||||||
}
|
}
|
||||||
|
@ -125,6 +125,9 @@
|
|||||||
store.renderSpritesheetPreview();
|
store.renderSpritesheetPreview();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle mouse down to detect which sprite was clicked
|
||||||
|
*/
|
||||||
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;
|
||||||
|
|
||||||
@ -133,10 +136,11 @@
|
|||||||
const x = (e.clientX - rect.left) / store.zoomLevel.value;
|
const x = (e.clientX - rect.left) / store.zoomLevel.value;
|
||||||
const y = (e.clientY - rect.top) / store.zoomLevel.value;
|
const y = (e.clientY - rect.top) / store.zoomLevel.value;
|
||||||
|
|
||||||
// Find which sprite was clicked
|
// Find which sprite was clicked - start from top (last rendered)
|
||||||
for (let i = store.sprites.value.length - 1; i >= 0; i--) {
|
for (let i = store.sprites.value.length - 1; i >= 0; i--) {
|
||||||
const sprite = store.sprites.value[i];
|
const sprite = store.sprites.value[i];
|
||||||
if (x >= sprite.x && x <= sprite.x + store.cellSize.width && y >= sprite.y && y <= sprite.y + store.cellSize.height) {
|
// Check if click is within the actual sprite bounds, not just the cell
|
||||||
|
if (x >= sprite.x && x <= sprite.x + sprite.width && y >= sprite.y && y <= sprite.y + sprite.height) {
|
||||||
store.draggedSprite.value = sprite;
|
store.draggedSprite.value = sprite;
|
||||||
store.dragOffset.x = x - sprite.x;
|
store.dragOffset.x = x - sprite.x;
|
||||||
store.dragOffset.y = y - sprite.y;
|
store.dragOffset.y = y - sprite.y;
|
||||||
@ -175,14 +179,14 @@
|
|||||||
|
|
||||||
// Handle sprite dragging
|
// Handle sprite dragging
|
||||||
if (store.draggedSprite.value) {
|
if (store.draggedSprite.value) {
|
||||||
if (store.isShiftPressed.value) {
|
const sprite = store.draggedSprite.value;
|
||||||
// Free positioning within the cell bounds when shift is pressed
|
const spriteIndex = store.sprites.value.findIndex(s => s.id === sprite.id);
|
||||||
const cellX = Math.floor(store.draggedSprite.value.x / store.cellSize.width);
|
|
||||||
const cellY = Math.floor(store.draggedSprite.value.y / store.cellSize.height);
|
|
||||||
|
|
||||||
// Calculate new position
|
if (store.isShiftPressed.value) {
|
||||||
const newX = x - store.dragOffset.x;
|
// --- FREE POSITIONING WITHIN CELL ---
|
||||||
const newY = y - store.dragOffset.y;
|
// Determine the current cell the sprite is in
|
||||||
|
const cellX = Math.floor(sprite.x / store.cellSize.width);
|
||||||
|
const cellY = Math.floor(sprite.y / store.cellSize.height);
|
||||||
|
|
||||||
// Calculate cell boundaries
|
// Calculate cell boundaries
|
||||||
const cellLeft = cellX * store.cellSize.width;
|
const cellLeft = cellX * store.cellSize.width;
|
||||||
@ -190,20 +194,27 @@
|
|||||||
const cellRight = cellLeft + store.cellSize.width;
|
const cellRight = cellLeft + store.cellSize.width;
|
||||||
const cellBottom = cellTop + store.cellSize.height;
|
const cellBottom = cellTop + store.cellSize.height;
|
||||||
|
|
||||||
// Calculate maximum allowed position to keep sprite within cell
|
// Calculate new position based on mouse movement
|
||||||
const maxX = cellRight - store.draggedSprite.value.width;
|
const newX = x - store.dragOffset.x;
|
||||||
const maxY = cellBottom - store.draggedSprite.value.height;
|
const newY = y - store.dragOffset.y;
|
||||||
|
|
||||||
// Constrain position to stay within the cell
|
// Calculate maximum allowed position to keep sprite strictly within cell
|
||||||
store.draggedSprite.value.x = Math.max(cellLeft, Math.min(newX, maxX));
|
// This ensures the sprite cannot extend beyond its assigned cell
|
||||||
store.draggedSprite.value.y = Math.max(cellTop, Math.min(newY, maxY));
|
const maxX = cellLeft + (store.cellSize.width - sprite.width);
|
||||||
|
const maxY = cellTop + (store.cellSize.height - sprite.height);
|
||||||
|
|
||||||
// Calculate the offset within the cell
|
// Constrain position to keep sprite fully within the cell
|
||||||
const offsetX = store.draggedSprite.value.x - cellLeft;
|
const constrainedX = Math.max(cellLeft, Math.min(newX, maxX));
|
||||||
const offsetY = store.draggedSprite.value.y - cellTop;
|
const constrainedY = Math.max(cellTop, Math.min(newY, maxY));
|
||||||
|
|
||||||
|
// Update sprite position
|
||||||
|
sprite.x = constrainedX;
|
||||||
|
sprite.y = constrainedY;
|
||||||
|
|
||||||
|
// Calculate and update the offset within the cell
|
||||||
|
const offsetX = sprite.x - cellLeft;
|
||||||
|
const offsetY = sprite.y - cellTop;
|
||||||
|
|
||||||
// Update the sprite offset in the store for the current sprite
|
|
||||||
const spriteIndex = store.sprites.value.findIndex(s => s.id === store.draggedSprite.value.id);
|
|
||||||
if (spriteIndex !== -1) {
|
if (spriteIndex !== -1) {
|
||||||
const frameOffset = store.getSpriteOffset(spriteIndex);
|
const frameOffset = store.getSpriteOffset(spriteIndex);
|
||||||
frameOffset.x = offsetX;
|
frameOffset.x = offsetX;
|
||||||
@ -214,26 +225,30 @@
|
|||||||
store.currentSpriteOffset.y = offsetY;
|
store.currentSpriteOffset.y = offsetY;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Calculate new position based on grid cells (snap to grid)
|
// --- GRID SNAPPING MODE ---
|
||||||
const newCellX = Math.floor((x - store.dragOffset.x) / store.cellSize.width);
|
if (!canvasEl.value) return;
|
||||||
const newCellY = Math.floor((y - store.dragOffset.y) / store.cellSize.height);
|
|
||||||
|
|
||||||
// Make sure we stay within bounds
|
// Calculate target cell coordinates based on mouse position
|
||||||
if (canvasEl.value) {
|
const targetCellX = Math.floor((x - store.dragOffset.x) / store.cellSize.width);
|
||||||
const maxCellX = Math.floor(canvasEl.value.width / store.cellSize.width) - 1;
|
const targetCellY = Math.floor((y - store.dragOffset.y) / store.cellSize.height);
|
||||||
const maxCellY = Math.floor(canvasEl.value.height / store.cellSize.height) - 1;
|
|
||||||
|
|
||||||
const boundedCellX = Math.max(0, Math.min(newCellX, maxCellX));
|
// Calculate how many cells the sprite occupies
|
||||||
const boundedCellY = Math.max(0, Math.min(newCellY, maxCellY));
|
const spriteCellsWide = Math.ceil(sprite.width / store.cellSize.width);
|
||||||
|
const spriteCellsHigh = Math.ceil(sprite.height / store.cellSize.height);
|
||||||
|
|
||||||
// Update the sprite position with pixel-perfect coordinates
|
// Calculate maximum valid cell position to prevent overflow
|
||||||
const newX = boundedCellX * store.cellSize.width;
|
const maxValidCellX = Math.floor(canvasEl.value.width / store.cellSize.width) - spriteCellsWide;
|
||||||
const newY = boundedCellY * store.cellSize.height;
|
const maxValidCellY = Math.floor(canvasEl.value.height / store.cellSize.height) - spriteCellsHigh;
|
||||||
store.draggedSprite.value.x = newX;
|
|
||||||
store.draggedSprite.value.y = newY;
|
|
||||||
|
|
||||||
// When snapping to grid, reset any offsets for this sprite
|
// Ensure we don't place sprites where they would extend beyond canvas bounds
|
||||||
const spriteIndex = store.sprites.value.findIndex(s => s.id === store.draggedSprite.value.id);
|
const boundedCellX = Math.max(0, Math.min(targetCellX, maxValidCellX));
|
||||||
|
const boundedCellY = Math.max(0, Math.min(targetCellY, maxValidCellY));
|
||||||
|
|
||||||
|
// Update sprite position to align with cell grid
|
||||||
|
sprite.x = boundedCellX * store.cellSize.width;
|
||||||
|
sprite.y = boundedCellY * store.cellSize.height;
|
||||||
|
|
||||||
|
// Reset any offsets for grid-snapped sprites
|
||||||
if (spriteIndex !== -1) {
|
if (spriteIndex !== -1) {
|
||||||
const frameOffset = store.getSpriteOffset(spriteIndex);
|
const frameOffset = store.getSpriteOffset(spriteIndex);
|
||||||
frameOffset.x = 0;
|
frameOffset.x = 0;
|
||||||
@ -244,7 +259,6 @@
|
|||||||
store.currentSpriteOffset.y = 0;
|
store.currentSpriteOffset.y = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Trigger a re-render
|
// Trigger a re-render
|
||||||
store.renderSpritesheetPreview();
|
store.renderSpritesheetPreview();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user