Added mass position buttons
This commit is contained in:
parent
0a0838832e
commit
0ddb9f4918
92
src/App.vue
92
src/App.vue
@ -21,11 +21,33 @@
|
||||
|
||||
<div v-if="sprites.length > 0" class="mt-8">
|
||||
<div class="flex flex-wrap items-center gap-6 mb-8">
|
||||
<div class="flex items-center space-x-3">
|
||||
<div class="flex items-center space-x-1">
|
||||
<label for="columns" class="text-gray-700 font-medium">Columns:</label>
|
||||
<input id="columns" type="number" v-model="columns" min="1" max="10" class="w-20 px-3 py-2 border border-gray-200 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent outline-none transition-all" />
|
||||
</div>
|
||||
|
||||
<!-- Add mass position buttons -->
|
||||
<div class="flex items-center space-x-2">
|
||||
<button @click="alignSprites('left')" class="p-2 bg-gray-100 hover:bg-gray-200 text-gray-700 rounded-lg transition-colors" title="Align Left">
|
||||
<i class="fas fa-arrow-left"></i>
|
||||
</button>
|
||||
<button @click="alignSprites('center')" class="p-2 bg-gray-100 hover:bg-gray-200 text-gray-700 rounded-lg transition-colors" title="Align Center">
|
||||
<i class="fas fa-arrows-left-right"></i>
|
||||
</button>
|
||||
<button @click="alignSprites('right')" class="p-2 bg-gray-100 hover:bg-gray-200 text-gray-700 rounded-lg transition-colors" title="Align Right">
|
||||
<i class="fas fa-arrow-right"></i>
|
||||
</button>
|
||||
<button @click="alignSprites('top')" class="p-2 bg-gray-100 hover:bg-gray-200 text-gray-700 rounded-lg transition-colors" title="Align Top">
|
||||
<i class="fas fa-arrow-up"></i>
|
||||
</button>
|
||||
<button @click="alignSprites('middle')" class="p-2 bg-gray-100 hover:bg-gray-200 text-gray-700 rounded-lg transition-colors" title="Align Middle">
|
||||
<i class="fas fa-arrows-up-down"></i>
|
||||
</button>
|
||||
<button @click="alignSprites('bottom')" class="p-2 bg-gray-100 hover:bg-gray-200 text-gray-700 rounded-lg transition-colors" title="Align Bottom">
|
||||
<i class="fas fa-arrow-down"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<button @click="downloadSpritesheet" class="px-6 py-2.5 bg-blue-500 hover:bg-blue-600 text-white font-medium rounded-lg transition-colors flex items-center space-x-2">
|
||||
<i class="fas fa-download"></i>
|
||||
<span>Download spritesheet</span>
|
||||
@ -145,24 +167,24 @@
|
||||
canvas.width = maxWidth * columns.value;
|
||||
canvas.height = maxHeight * rows;
|
||||
|
||||
// Apply pixel art optimization for the export canvas
|
||||
// Disable image smoothing for pixel-perfect rendering
|
||||
ctx.imageSmoothingEnabled = false;
|
||||
|
||||
// Draw sprites
|
||||
// Draw sprites with integer positions
|
||||
sprites.value.forEach((sprite, index) => {
|
||||
const col = index % columns.value;
|
||||
const row = Math.floor(index / columns.value);
|
||||
|
||||
const cellX = col * maxWidth;
|
||||
const cellY = row * maxHeight;
|
||||
const cellX = Math.floor(col * maxWidth);
|
||||
const cellY = Math.floor(row * maxHeight);
|
||||
|
||||
ctx.drawImage(sprite.img, cellX + sprite.x, cellY + sprite.y);
|
||||
ctx.drawImage(sprite.img, Math.floor(cellX + sprite.x), Math.floor(cellY + sprite.y));
|
||||
});
|
||||
|
||||
// Create download link
|
||||
// Create download link with PNG format
|
||||
const link = document.createElement('a');
|
||||
link.download = 'spritesheet.png';
|
||||
link.href = canvas.toDataURL('image/png');
|
||||
link.href = canvas.toDataURL('image/png', 1.0); // Use maximum quality
|
||||
link.click();
|
||||
};
|
||||
|
||||
@ -276,7 +298,8 @@
|
||||
}
|
||||
|
||||
// Process each sprite
|
||||
const newSprites = await Promise.all(
|
||||
// Replace current sprites with imported ones
|
||||
sprites.value = await Promise.all(
|
||||
jsonData.sprites.map(async (spriteData: any) => {
|
||||
return new Promise<Sprite>(resolve => {
|
||||
// Create image from base64
|
||||
@ -311,12 +334,57 @@
|
||||
});
|
||||
})
|
||||
);
|
||||
|
||||
// Replace current sprites with imported ones
|
||||
sprites.value = newSprites;
|
||||
} catch (error) {
|
||||
console.error('Error importing JSON:', error);
|
||||
alert('Failed to import JSON file. Please check the file format.');
|
||||
}
|
||||
};
|
||||
|
||||
// Add new alignment function
|
||||
const alignSprites = (position: 'left' | 'center' | 'right' | 'top' | 'middle' | 'bottom') => {
|
||||
if (sprites.value.length === 0) return;
|
||||
|
||||
// Find max dimensions for the current column layout
|
||||
let maxWidth = 0;
|
||||
let maxHeight = 0;
|
||||
sprites.value.forEach(sprite => {
|
||||
maxWidth = Math.max(maxWidth, sprite.width);
|
||||
maxHeight = Math.max(maxHeight, sprite.height);
|
||||
});
|
||||
|
||||
sprites.value = sprites.value.map((sprite, index) => {
|
||||
let x = sprite.x;
|
||||
let y = sprite.y;
|
||||
|
||||
switch (position) {
|
||||
case 'left':
|
||||
x = 0;
|
||||
break;
|
||||
case 'center':
|
||||
x = (maxWidth - sprite.width) / 2;
|
||||
break;
|
||||
case 'right':
|
||||
x = maxWidth - sprite.width;
|
||||
break;
|
||||
case 'top':
|
||||
y = 0;
|
||||
break;
|
||||
case 'middle':
|
||||
y = (maxHeight - sprite.height) / 2;
|
||||
break;
|
||||
case 'bottom':
|
||||
y = maxHeight - sprite.height;
|
||||
break;
|
||||
}
|
||||
|
||||
return { ...sprite, x, y };
|
||||
});
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
canvas {
|
||||
image-rendering: pixelated;
|
||||
image-rendering: crisp-edges;
|
||||
}
|
||||
</style>
|
||||
|
@ -194,18 +194,14 @@
|
||||
// Clear canvas
|
||||
ctx.value.clearRect(0, 0, canvasRef.value.width, canvasRef.value.height);
|
||||
|
||||
// Set pixel art optimization
|
||||
if (pixelPerfect.value) {
|
||||
ctx.value.imageSmoothingEnabled = false;
|
||||
} else {
|
||||
ctx.value.imageSmoothingEnabled = true;
|
||||
}
|
||||
// Disable image smoothing
|
||||
ctx.value.imageSmoothingEnabled = false;
|
||||
|
||||
// Draw grid
|
||||
ctx.value.strokeStyle = '#e5e7eb';
|
||||
for (let col = 0; col < props.columns; col++) {
|
||||
for (let row = 0; row < rows; row++) {
|
||||
ctx.value.strokeRect(col * maxWidth, row * maxHeight, maxWidth, maxHeight);
|
||||
ctx.value.strokeRect(Math.floor(col * maxWidth), Math.floor(row * maxHeight), maxWidth, maxHeight);
|
||||
}
|
||||
}
|
||||
|
||||
@ -214,11 +210,11 @@
|
||||
const col = index % props.columns;
|
||||
const row = Math.floor(index / props.columns);
|
||||
|
||||
const cellX = col * maxWidth;
|
||||
const cellY = row * maxHeight;
|
||||
const cellX = Math.floor(col * maxWidth);
|
||||
const cellY = Math.floor(row * maxHeight);
|
||||
|
||||
// Draw sprite
|
||||
ctx.value?.drawImage(sprite.img, cellX + sprite.x, cellY + sprite.y);
|
||||
// Draw sprite using integer positions
|
||||
ctx.value?.drawImage(sprite.img, Math.floor(cellX + sprite.x), Math.floor(cellY + sprite.y));
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -159,6 +159,7 @@
|
||||
|
||||
// Add this after other refs
|
||||
const hiddenFrames = ref<number[]>([]);
|
||||
const pixelPerfect = ref(true);
|
||||
|
||||
// Add these computed properties
|
||||
const visibleFrames = computed(() => props.sprites.filter((_, index) => !hiddenFrames.value.includes(index)));
|
||||
@ -189,6 +190,9 @@
|
||||
|
||||
const { maxWidth, maxHeight } = calculateMaxDimensions();
|
||||
|
||||
// Apply pixel art optimization consistently
|
||||
ctx.value.imageSmoothingEnabled = !pixelPerfect.value;
|
||||
|
||||
// Set canvas size to just fit one sprite cell
|
||||
previewCanvasRef.value.width = maxWidth;
|
||||
previewCanvasRef.value.height = maxHeight;
|
||||
@ -212,8 +216,8 @@
|
||||
}
|
||||
}
|
||||
|
||||
// Apply pixel art optimization
|
||||
ctx.value.imageSmoothingEnabled = false;
|
||||
// Keep pixel art optimization consistent throughout all drawing operations
|
||||
ctx.value.imageSmoothingEnabled = !pixelPerfect.value;
|
||||
|
||||
// Draw all sprites with transparency if enabled
|
||||
if (showAllSprites.value && props.sprites.length > 1) {
|
||||
@ -329,17 +333,17 @@
|
||||
const mouseX = ((event.clientX - rect.left) / zoom.value) * scaleX;
|
||||
const mouseY = ((event.clientY - rect.top) / zoom.value) * scaleY;
|
||||
|
||||
const deltaX = mouseX - dragStartX.value;
|
||||
const deltaY = mouseY - dragStartY.value;
|
||||
const deltaX = Math.round(mouseX - dragStartX.value);
|
||||
const deltaY = Math.round(mouseY - dragStartY.value);
|
||||
|
||||
const sprite = props.sprites[currentFrameIndex.value];
|
||||
if (!sprite || sprite.id !== activeSpriteId.value) return;
|
||||
|
||||
const { maxWidth, maxHeight } = calculateMaxDimensions();
|
||||
|
||||
// Calculate new position with constraints
|
||||
let newX = spritePosBeforeDrag.value.x + deltaX;
|
||||
let newY = spritePosBeforeDrag.value.y + deltaY;
|
||||
// Calculate new position with constraints and round to integers
|
||||
let newX = Math.round(spritePosBeforeDrag.value.x + deltaX);
|
||||
let newY = Math.round(spritePosBeforeDrag.value.y + deltaY);
|
||||
|
||||
// Constrain movement within cell
|
||||
newX = Math.max(0, Math.min(maxWidth - sprite.width, newX));
|
||||
|
Loading…
x
Reference in New Issue
Block a user