From 281a37fa7e7380ff809fcd7339ca1e5beded7cce Mon Sep 17 00:00:00 2001 From: root Date: Thu, 1 Jan 2026 17:04:40 +0100 Subject: [PATCH] [FEAT] UI enhancements --- src/components/SpriteCanvas.vue | 44 +++++++++++------------ src/components/SpritePreview.vue | 28 +++++++-------- src/composables/useDragSprite.ts | 30 ++++++++-------- src/views/HomeView.vue | 62 ++++++++++++++++---------------- 4 files changed, 82 insertions(+), 82 deletions(-) diff --git a/src/components/SpriteCanvas.vue b/src/components/SpriteCanvas.vue index c1efd04..6c24b80 100644 --- a/src/components/SpriteCanvas.vue +++ b/src/components/SpriteCanvas.vue @@ -94,10 +94,10 @@ :key="`cell-${cellIndex - 1}`" class="absolute" :style="{ - left: `${getCellPosition(cellIndex - 1).x}px`, - top: `${getCellPosition(cellIndex - 1).y}px`, - width: `${gridMetrics.maxWidth}px`, - height: `${gridMetrics.maxHeight}px`, + left: `${Math.round(getCellPosition(cellIndex - 1).x)}px`, + top: `${Math.round(getCellPosition(cellIndex - 1).y)}px`, + width: `${Math.round(gridMetrics.maxWidth)}px`, + height: `${Math.round(gridMetrics.maxHeight)}px`, backgroundColor: getCellBackground(), backgroundImage: getCellBackgroundImage(), backgroundSize: getCellBackgroundSize(), @@ -119,10 +119,10 @@ :src="sprite.url" class="absolute pointer-events-none" :style="{ - left: `${getCellPosition(cellIndex - 1).x + gridMetrics.negativeSpacing + sprite.x}px`, - top: `${getCellPosition(cellIndex - 1).y + gridMetrics.negativeSpacing + sprite.y}px`, - width: `${sprite.width}px`, - height: `${sprite.height}px`, + left: `${Math.round(getCellPosition(cellIndex - 1).x + gridMetrics.negativeSpacing + sprite.x)}px`, + top: `${Math.round(getCellPosition(cellIndex - 1).y + gridMetrics.negativeSpacing + sprite.y)}px`, + width: `${Math.round(sprite.width)}px`, + height: `${Math.round(sprite.height)}px`, opacity: '0.25', imageRendering: settingsStore.pixelPerfect ? 'pixelated' : 'auto', }" @@ -143,10 +143,10 @@ class="absolute cursor-move transition-transform duration-200" :class="{ 'ring-2 ring-blue-500 ring-offset-1 dark:ring-offset-gray-900': showActiveBorder && selectedSpriteIds.has(sprite.id) }" :style="{ - left: `${getCellPosition(index).x + gridMetrics.negativeSpacing + sprite.x}px`, - top: `${getCellPosition(index).y + gridMetrics.negativeSpacing + sprite.y}px`, - width: `${sprite.width}px`, - height: `${sprite.height}px`, + left: `${Math.round(getCellPosition(index).x + gridMetrics.negativeSpacing + sprite.x)}px`, + top: `${Math.round(getCellPosition(index).y + gridMetrics.negativeSpacing + sprite.y)}px`, + width: `${Math.round(sprite.width)}px`, + height: `${Math.round(sprite.height)}px`, opacity: layer.id === activeLayerId ? '1' : '0.85', imageRendering: settingsStore.pixelPerfect ? 'pixelated' : 'auto', transform: `rotate(${sprite.rotation}deg) scale(${sprite.flipX ? -1 : 1}, ${sprite.flipY ? -1 : 1})`, @@ -165,10 +165,10 @@ :src="activeSpriteSprite?.url" class="absolute pointer-events-none" :style="{ - left: `${ghostSprite.x}px`, - top: `${ghostSprite.y}px`, - width: `${activeSpriteSprite?.width}px`, - height: `${activeSpriteSprite?.height}px`, + left: `${Math.round(ghostSprite.x)}px`, + top: `${Math.round(ghostSprite.y)}px`, + width: `${Math.round(activeSpriteSprite?.width || 0)}px`, + height: `${Math.round(activeSpriteSprite?.height || 0)}px`, opacity: '0.6', imageRendering: settingsStore.pixelPerfect ? 'pixelated' : 'auto', transform: `rotate(${activeSpriteSprite?.rotation || 0}deg) scale(${activeSpriteSprite?.flipX ? -1 : 1}, ${activeSpriteSprite?.flipY ? -1 : 1})`, @@ -183,8 +183,8 @@ :key="`label-${position.id}`" class="absolute text-[23px] leading-none font-mono text-cyan-600 dark:text-cyan-400 bg-white/90 dark:bg-gray-900/90 px-1 py-0.5 rounded-sm pointer-events-none" :style="{ - left: `${position.cellX + position.maxWidth - 2}px`, - top: `${position.cellY + position.maxHeight - 2}px`, + left: `${Math.round(position.cellX + position.maxWidth - 2)}px`, + top: `${Math.round(position.cellY + position.maxHeight - 2)}px`, transform: 'translate(-100%, -100%)', }" > @@ -329,8 +329,8 @@ const maxLen = Math.max(1, ...props.layers.map(l => l.sprites.length)); const rows = Math.max(1, Math.ceil(maxLen / props.columns)); return { - width: gridMetrics.value.maxWidth * props.columns, - height: gridMetrics.value.maxHeight * rows, + width: Math.round(gridMetrics.value.maxWidth * props.columns), + height: Math.round(gridMetrics.value.maxHeight * rows), }; }); @@ -338,8 +338,8 @@ const col = index % props.columns; const row = Math.floor(index / props.columns); return { - x: col * gridMetrics.value.maxWidth, - y: row * gridMetrics.value.maxHeight, + x: Math.round(col * gridMetrics.value.maxWidth), + y: Math.round(row * gridMetrics.value.maxHeight), }; }; diff --git a/src/components/SpritePreview.vue b/src/components/SpritePreview.vue index dfab0cf..6497f24 100644 --- a/src/components/SpritePreview.vue +++ b/src/components/SpritePreview.vue @@ -72,7 +72,7 @@ transformOrigin: 'top left', width: `${cellDimensions.cellWidth}px`, height: `${cellDimensions.cellHeight}px`, - backgroundColor: '#f9fafb', + backgroundColor: settingsStore.backgroundColor === 'transparent' ? '#f9fafb' : settingsStore.backgroundColor, backgroundImage: getPreviewBackgroundImage(), backgroundSize: settingsStore.backgroundColor === 'transparent' ? '20px 20px' : 'auto', backgroundPosition: settingsStore.backgroundColor === 'transparent' ? '0 0, 0 10px, 10px -10px, -10px 0px' : '0 0', @@ -92,10 +92,10 @@ :src="layer.sprites[i - 1].url" class="absolute pointer-events-none" :style="{ - left: `${cellDimensions.negativeSpacing + layer.sprites[i - 1].x}px`, - top: `${cellDimensions.negativeSpacing + layer.sprites[i - 1].y}px`, - width: `${layer.sprites[i - 1].width}px`, - height: `${layer.sprites[i - 1].height}px`, + left: `${Math.round(cellDimensions.negativeSpacing + layer.sprites[i - 1].x)}px`, + top: `${Math.round(cellDimensions.negativeSpacing + layer.sprites[i - 1].y)}px`, + width: `${Math.round(layer.sprites[i - 1].width)}px`, + height: `${Math.round(layer.sprites[i - 1].height)}px`, opacity: '0.3', imageRendering: settingsStore.pixelPerfect ? 'pixelated' : 'auto', transform: `rotate(${layer.sprites[i - 1].rotation || 0}deg) scale(${layer.sprites[i - 1].flipX ? -1 : 1}, ${layer.sprites[i - 1].flipY ? -1 : 1})`, @@ -115,10 +115,10 @@ class="absolute" :class="{ 'cursor-move': isDraggable }" :style="{ - left: `${cellDimensions.negativeSpacing + layer.sprites[currentFrameIndex].x}px`, - top: `${cellDimensions.negativeSpacing + layer.sprites[currentFrameIndex].y}px`, - width: `${layer.sprites[currentFrameIndex].width}px`, - height: `${layer.sprites[currentFrameIndex].height}px`, + left: `${Math.round(cellDimensions.negativeSpacing + layer.sprites[currentFrameIndex].x)}px`, + top: `${Math.round(cellDimensions.negativeSpacing + layer.sprites[currentFrameIndex].y)}px`, + width: `${Math.round(layer.sprites[currentFrameIndex].width)}px`, + height: `${Math.round(layer.sprites[currentFrameIndex].height)}px`, imageRendering: settingsStore.pixelPerfect ? 'pixelated' : 'auto', transform: `rotate(${layer.sprites[currentFrameIndex].rotation || 0}deg) scale(${layer.sprites[currentFrameIndex].flipX ? -1 : 1}, ${layer.sprites[currentFrameIndex].flipY ? -1 : 1})`, }" @@ -433,8 +433,8 @@ // If manual cell size is enabled, use manual values if (settingsStore.manualCellSizeEnabled) { return { - cellWidth: settingsStore.manualCellWidth, - cellHeight: settingsStore.manualCellHeight, + cellWidth: Math.round(settingsStore.manualCellWidth), + cellHeight: Math.round(settingsStore.manualCellHeight), negativeSpacing: 0, }; } @@ -442,10 +442,10 @@ // Otherwise, calculate from sprite dimensions across ALL layers const { maxWidth, maxHeight } = getMaxDimensionsAcrossLayers(allLayers); const allSprites = allLayers.flatMap(l => l.sprites); - const negativeSpacing = calculateNegativeSpacing(allSprites, settingsStore.negativeSpacingEnabled); + const negativeSpacing = Math.round(calculateNegativeSpacing(allSprites, settingsStore.negativeSpacingEnabled)); return { - cellWidth: maxWidth + negativeSpacing, - cellHeight: maxHeight + negativeSpacing, + cellWidth: Math.round(maxWidth + negativeSpacing), + cellHeight: Math.round(maxHeight + negativeSpacing), negativeSpacing, }; }); diff --git a/src/composables/useDragSprite.ts b/src/composables/useDragSprite.ts index 0006fae..bed17f0 100644 --- a/src/composables/useDragSprite.ts +++ b/src/composables/useDragSprite.ts @@ -102,11 +102,11 @@ export function useDragSprite(options: DragSpriteOptions) { lastMaxHeight.value = baseMaxHeight; // Calculate negative spacing using shared composable - const negativeSpacing = calculateNegativeSpacing(spritesToMeasure, negativeSpacingEnabled); + const negativeSpacing = Math.round(calculateNegativeSpacing(spritesToMeasure, negativeSpacingEnabled)); // Add negative spacing to expand each cell - const maxWidth = baseMaxWidth + negativeSpacing; - const maxHeight = baseMaxHeight + negativeSpacing; + const maxWidth = Math.round(baseMaxWidth + negativeSpacing); + const maxHeight = Math.round(baseMaxHeight + negativeSpacing); return { maxWidth, maxHeight, negativeSpacing }; }; @@ -124,14 +124,14 @@ export function useDragSprite(options: DragSpriteOptions) { // (spacing added to top and left) return { id: sprite.id, - canvasX: col * maxWidth + negativeSpacing + sprite.x, - canvasY: row * maxHeight + negativeSpacing + sprite.y, - cellX: col * maxWidth, - cellY: row * maxHeight, - width: sprite.width, - height: sprite.height, - maxWidth, - maxHeight, + canvasX: Math.round(col * maxWidth + negativeSpacing + sprite.x), + canvasY: Math.round(row * maxHeight + negativeSpacing + sprite.y), + cellX: Math.round(col * maxWidth), + cellY: Math.round(row * maxHeight), + width: Math.round(sprite.width), + height: Math.round(sprite.height), + maxWidth: Math.round(maxWidth), + maxHeight: Math.round(maxHeight), col, row, index, @@ -206,8 +206,8 @@ export function useDragSprite(options: DragSpriteOptions) { // Use the sprite's current index in the array to calculate cell position const cellCol = spriteIndex % columns; const cellRow = Math.floor(spriteIndex / columns); - const cellX = cellCol * maxWidth; - const cellY = cellRow * maxHeight; + const cellX = Math.round(cellCol * maxWidth); + const cellY = Math.round(cellRow * maxHeight); // Calculate new position relative to cell origin (without the negative spacing offset) // The sprite's x,y is stored relative to where it would be drawn after the negativeSpacing offset @@ -241,8 +241,8 @@ export function useDragSprite(options: DragSpriteOptions) { highlightCell.value = hoverCell; ghostSprite.value = { id: activeSpriteId.value, - x: pos.x - dragOffsetX.value, - y: pos.y - dragOffsetY.value, + x: Math.round(pos.x - dragOffsetX.value), + y: Math.round(pos.y - dragOffsetY.value), }; onDraw(); } else { diff --git a/src/views/HomeView.vue b/src/views/HomeView.vue index f3ade18..d8c2bd9 100644 --- a/src/views/HomeView.vue +++ b/src/views/HomeView.vue @@ -178,8 +178,33 @@ - +
+

Alignment

+
+ + + + + + + + + + + + + + + + + + +
+
+ + +

Grid Layout

@@ -201,8 +226,8 @@
- -
+ +

View Options

@@ -253,8 +278,8 @@
- -
+ +

Tools

@@ -289,31 +314,6 @@
- - -
-

Alignment

-
- - - - - - - - - - - - - - - - - - -
-
@@ -370,7 +370,7 @@ -
+
{{ Math.round(zoom * 100) }}%