diff --git a/src/components/MainContent.vue b/src/components/MainContent.vue
index 5578e1e..c33a481 100644
--- a/src/components/MainContent.vue
+++ b/src/components/MainContent.vue
@@ -18,6 +18,7 @@
:style="{
transform: `scale(${store.zoomLevel.value})`,
transformOrigin: 'top left',
+ imageRendering: 'pixelated', // Keep pixel art sharp when zooming
}"
>
diff --git a/src/components/PreviewModal.vue b/src/components/PreviewModal.vue
index 92a5dda..e07ea00 100644
--- a/src/components/PreviewModal.vue
+++ b/src/components/PreviewModal.vue
@@ -56,7 +56,7 @@
-
+
diff --git a/src/components/SettingsModal.vue b/src/components/SettingsModal.vue
index 4327c0b..665c2c3 100644
--- a/src/components/SettingsModal.vue
+++ b/src/components/SettingsModal.vue
@@ -83,9 +83,7 @@
-
- Border will only be visible in the preview and won't be included in the downloaded spritesheet.
-
+ Border will only be visible in the preview and won't be included in the downloaded spritesheet.
diff --git a/src/composables/useSpritesheetStore.ts b/src/composables/useSpritesheetStore.ts
index 1810f7b..40e870b 100644
--- a/src/composables/useSpritesheetStore.ts
+++ b/src/composables/useSpritesheetStore.ts
@@ -45,7 +45,7 @@ const zoomLevel = ref(1); // Default zoom level (1 = 100%)
const previewBorder = reactive({
enabled: false,
color: '#ff0000', // Default red color
- width: 2 // Default width in pixels
+ width: 2, // Default width in pixels
});
export function useSpritesheetStore() {
@@ -144,8 +144,6 @@ export function useSpritesheetStore() {
const cols = columns.value;
const rows = Math.ceil(totalSprites / cols);
- console.log(`Store: Updating canvas size for ${totalSprites} sprites, ${cols} columns, ${rows} rows`);
-
if (cellSize.width <= 0 || cellSize.height <= 0) {
console.error('Store: Invalid cell size for canvas update', cellSize);
return;
@@ -156,7 +154,6 @@ export function useSpritesheetStore() {
// Ensure the canvas is large enough to display all sprites
if (canvas.value.width !== newWidth || canvas.value.height !== newHeight) {
- console.log(`Store: Resizing canvas from ${canvas.value.width}x${canvas.value.height} to ${newWidth}x${newHeight}`);
canvas.value.width = newWidth;
canvas.value.height = newHeight;
@@ -183,8 +180,6 @@ export function useSpritesheetStore() {
return;
}
- console.log(`Store: Auto-arranging ${sprites.value.length} sprites with ${columns.value} columns`);
-
// First update the canvas size to ensure it's large enough
updateCanvasSize();
@@ -195,9 +190,6 @@ export function useSpritesheetStore() {
sprite.x = column * cellSize.width;
sprite.y = row * cellSize.height;
-
- // Log the position of each sprite for debugging
- console.log(`Store: Sprite ${index} (${sprite.name}) positioned at (${sprite.x}, ${sprite.y})`);
});
// Check if the canvas is ready before attempting to render
@@ -233,8 +225,6 @@ export function useSpritesheetStore() {
// Make sure the canvas size is correct before rendering
updateCanvasSize();
- console.log(`Store: Rendering ${sprites.value.length} sprites on canvas ${canvas.value.width}x${canvas.value.height}`);
-
// Clear the canvas
ctx.value.clearRect(0, 0, canvas.value.width, canvas.value.height);
@@ -242,6 +232,14 @@ export function useSpritesheetStore() {
drawGrid();
}
+ // First, collect all occupied cells
+ const occupiedCells = new Set();
+ sprites.value.forEach(sprite => {
+ const cellX = Math.floor(sprite.x / cellSize.width);
+ const cellY = Math.floor(sprite.y / cellSize.height);
+ occupiedCells.add(`${cellX},${cellY}`);
+ });
+
// Draw each sprite - remove the zoom scaling from context
sprites.value.forEach((sprite, index) => {
try {
@@ -253,33 +251,23 @@ export function useSpritesheetStore() {
// Check if sprite is within canvas bounds
if (sprite.x >= 0 && sprite.y >= 0 && sprite.x + sprite.width <= canvas.value!.width && sprite.y + sprite.height <= canvas.value!.height) {
if (sprite.img.complete && sprite.img.naturalWidth !== 0) {
- // Draw the image at its original size
- ctx.value!.drawImage(sprite.img, sprite.x, sprite.y, sprite.width, sprite.height);
+ // For pixel art, ensure we're drawing at exact pixel boundaries
+ const x = Math.round(sprite.x);
+ const y = Math.round(sprite.y);
- // Draw border around the cell if enabled (preview only)
- if (previewBorder.enabled) {
- const cellX = Math.floor(sprite.x / cellSize.width) * cellSize.width;
- const cellY = Math.floor(sprite.y / cellSize.height) * cellSize.height;
-
- ctx.value!.strokeStyle = previewBorder.color;
- ctx.value!.lineWidth = previewBorder.width / zoomLevel.value; // Adjust for zoom
- ctx.value!.strokeRect(cellX, cellY, cellSize.width, cellSize.height);
- }
+ // Draw the image at its original size with pixel-perfect rendering
+ ctx.value!.imageSmoothingEnabled = false; // Keep pixel art sharp
+ ctx.value!.drawImage(sprite.img, x, y, sprite.width, sprite.height);
} else {
console.warn(`Store: Sprite image ${index} not fully loaded, setting onload handler`);
sprite.img.onload = () => {
if (ctx.value && canvas.value) {
- ctx.value.drawImage(sprite.img, sprite.x, sprite.y, sprite.width, sprite.height);
+ // For pixel art, ensure we're drawing at exact pixel boundaries
+ const x = Math.round(sprite.x);
+ const y = Math.round(sprite.y);
- // Draw border around the cell if enabled (preview only)
- if (previewBorder.enabled) {
- const cellX = Math.floor(sprite.x / cellSize.width) * cellSize.width;
- const cellY = Math.floor(sprite.y / cellSize.height) * cellSize.height;
-
- ctx.value.strokeStyle = previewBorder.color;
- ctx.value.lineWidth = previewBorder.width / zoomLevel.value; // Adjust for zoom
- ctx.value.strokeRect(cellX, cellY, cellSize.width, cellSize.height);
- }
+ ctx.value.imageSmoothingEnabled = false; // Keep pixel art sharp
+ ctx.value.drawImage(sprite.img, x, y, sprite.width, sprite.height);
}
};
}
@@ -290,6 +278,28 @@ export function useSpritesheetStore() {
console.error(`Store: Error rendering sprite at index ${index}:`, spriteError);
}
});
+
+ // Draw borders around occupied cells if enabled (preview only)
+ if (previewBorder.enabled && occupiedCells.size > 0) {
+ ctx.value!.strokeStyle = previewBorder.color;
+ ctx.value!.lineWidth = previewBorder.width / zoomLevel.value; // Adjust for zoom
+
+ // Draw borders around each occupied cell
+ occupiedCells.forEach(cellKey => {
+ const [cellX, cellY] = cellKey.split(',').map(Number);
+
+ // Calculate pixel-perfect coordinates for the cell
+ // Add 0.5 to align with pixel boundaries for crisp lines
+ const x = Math.floor(cellX * cellSize.width) + 0.5;
+ const y = Math.floor(cellY * cellSize.height) + 0.5;
+
+ // Adjust width and height to ensure the border is inside the cell
+ const width = cellSize.width - 1;
+ const height = cellSize.height - 1;
+
+ ctx.value!.strokeRect(x, y, width, height);
+ });
+ }
} catch (error) {
console.error('Store: Error in renderSpritesheetPreview:', error);
}
@@ -305,19 +315,21 @@ export function useSpritesheetStore() {
const visibleWidth = canvas.value.width / zoomLevel.value;
const visibleHeight = canvas.value.height / zoomLevel.value;
- // Draw vertical lines
+ // Draw vertical lines - ensure pixel-perfect grid lines
for (let x = 0; x <= visibleWidth; x += cellSize.width) {
+ const pixelX = Math.floor(x) + 0.5; // Align to pixel boundary for crisp lines
ctx.value.beginPath();
- ctx.value.moveTo(x, 0);
- ctx.value.lineTo(x, visibleHeight);
+ ctx.value.moveTo(pixelX, 0);
+ ctx.value.lineTo(pixelX, visibleHeight);
ctx.value.stroke();
}
- // Draw horizontal lines
+ // Draw horizontal lines - ensure pixel-perfect grid lines
for (let y = 0; y <= visibleHeight; y += cellSize.height) {
+ const pixelY = Math.floor(y) + 0.5; // Align to pixel boundary for crisp lines
ctx.value.beginPath();
- ctx.value.moveTo(0, y);
- ctx.value.lineTo(visibleWidth, y);
+ ctx.value.moveTo(0, pixelY);
+ ctx.value.lineTo(visibleWidth, pixelY);
ctx.value.stroke();
}
}
@@ -380,8 +392,14 @@ export function useSpritesheetStore() {
tempCtx.clearRect(0, 0, tempCanvas.width, tempCanvas.height);
+ // Ensure pixel art remains sharp in the downloaded file
+ tempCtx.imageSmoothingEnabled = false;
+
sprites.value.forEach(sprite => {
- tempCtx.drawImage(sprite.img, sprite.x, sprite.y);
+ // Use rounded coordinates for pixel-perfect rendering
+ const x = Math.round(sprite.x);
+ const y = Math.round(sprite.y);
+ tempCtx.drawImage(sprite.img, x, y);
});
const link = document.createElement('a');
@@ -435,16 +453,26 @@ export function useSpritesheetStore() {
const cellX = Math.floor(currentSprite.x / cellSize.width);
const cellY = Math.floor(currentSprite.y / cellSize.height);
- const offsetX = currentSprite.x - cellX * cellSize.width;
- const offsetY = currentSprite.y - cellY * cellSize.height;
+ // Calculate precise offset for pixel-perfect rendering
+ const offsetX = Math.round(currentSprite.x - cellX * cellSize.width);
+ const offsetY = Math.round(currentSprite.y - cellY * cellSize.height);
+ // Keep pixel art sharp
+ animation.ctx.imageSmoothingEnabled = false;
animation.ctx.drawImage(currentSprite.img, offsetX, offsetY);
- // Draw border if enabled (only for preview, not included in download)
+ // Draw border around the cell if enabled (only for preview, not included in download)
if (previewBorder.enabled) {
animation.ctx.strokeStyle = previewBorder.color;
animation.ctx.lineWidth = previewBorder.width;
- animation.ctx.strokeRect(0, 0, animation.canvas.width, animation.canvas.height);
+
+ // Use pixel-perfect coordinates for the border (0.5 offset for crisp lines)
+ const x = 0.5;
+ const y = 0.5;
+ const width = animation.canvas.width - 1;
+ const height = animation.canvas.height - 1;
+
+ animation.ctx.strokeRect(x, y, width, height);
}
}