Export negative offset bug fixes

This commit is contained in:
2025-11-18 20:30:59 +01:00
parent 590d76205f
commit f7a01e6c92
2 changed files with 39 additions and 17 deletions

View File

@@ -135,7 +135,7 @@
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue';
import { ref, onMounted, toRef } from 'vue';
import FileUploader from './components/FileUploader.vue';
import SpriteCanvas from './components/SpriteCanvas.vue';
import Modal from './components/utilities/Modal.vue';
@@ -147,11 +147,13 @@
import DarkModeToggle from './components/utilities/DarkModeToggle.vue';
import { useSprites } from './composables/useSprites';
import { useExport } from './composables/useExport';
import { useSettingsStore } from './stores/useSettingsStore';
import type { SpriteFile } from './types/sprites';
const settingsStore = useSettingsStore();
const { sprites, columns, updateSpritePosition, updateSpriteCell, removeSprite, replaceSprite, addSprite, addSpriteWithResize, processImageFiles, alignSprites } = useSprites();
const { downloadSpritesheet, exportSpritesheetJSON, importSpritesheetJSON, downloadAsGif, downloadAsZip } = useExport(sprites, columns);
const { downloadSpritesheet, exportSpritesheetJSON, importSpritesheetJSON, downloadAsGif, downloadAsZip } = useExport(sprites, columns, toRef(settingsStore, 'negativeSpacingEnabled'));
const isPreviewModalOpen = ref(false);
const isHelpModalOpen = ref(false);
const isFeedbackModalOpen = ref(false);

View File

@@ -5,7 +5,18 @@ import JSZip from 'jszip';
import type { Sprite } from '../types/sprites';
import { getMaxDimensions } from './useSprites';
export const useExport = (sprites: Ref<Sprite[]>, columns: Ref<number>) => {
export const useExport = (sprites: Ref<Sprite[]>, columns: Ref<number>, negativeSpacingEnabled: Ref<boolean>) => {
// Calculate negative spacing based on sprite dimensions
const calculateNegativeSpacing = (): number => {
if (!negativeSpacingEnabled.value || sprites.value.length === 0) return 0;
const { maxWidth, maxHeight } = getMaxDimensions(sprites.value);
const minWidth = Math.min(...sprites.value.map(s => s.width));
const minHeight = Math.min(...sprites.value.map(s => s.height));
const widthDiff = maxWidth - minWidth;
const heightDiff = maxHeight - minHeight;
return Math.max(widthDiff, heightDiff);
};
const downloadSpritesheet = () => {
if (!sprites.value.length) {
alert('Please upload or import sprites before downloading the spritesheet.');
@@ -13,22 +24,25 @@ export const useExport = (sprites: Ref<Sprite[]>, columns: Ref<number>) => {
}
const { maxWidth, maxHeight } = getMaxDimensions(sprites.value);
const negativeSpacing = calculateNegativeSpacing();
const cellWidth = maxWidth + negativeSpacing;
const cellHeight = maxHeight + negativeSpacing;
const rows = Math.ceil(sprites.value.length / columns.value);
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
if (!ctx) return;
canvas.width = maxWidth * columns.value;
canvas.height = maxHeight * rows;
canvas.width = cellWidth * columns.value;
canvas.height = cellHeight * rows;
ctx.imageSmoothingEnabled = false;
sprites.value.forEach((sprite, index) => {
const col = index % columns.value;
const row = Math.floor(index / columns.value);
const cellX = Math.floor(col * maxWidth);
const cellY = Math.floor(row * maxHeight);
ctx.drawImage(sprite.img, Math.floor(cellX + sprite.x), Math.floor(cellY + sprite.y));
const cellX = Math.floor(col * cellWidth);
const cellY = Math.floor(row * cellHeight);
ctx.drawImage(sprite.img, Math.floor(cellX + negativeSpacing + sprite.x), Math.floor(cellY + negativeSpacing + sprite.y));
});
const link = document.createElement('a');
@@ -133,20 +147,23 @@ export const useExport = (sprites: Ref<Sprite[]>, columns: Ref<number>) => {
}
const { maxWidth, maxHeight } = getMaxDimensions(sprites.value);
const negativeSpacing = calculateNegativeSpacing();
const cellWidth = maxWidth + negativeSpacing;
const cellHeight = maxHeight + negativeSpacing;
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
if (!ctx) return;
canvas.width = maxWidth;
canvas.height = maxHeight;
canvas.width = cellWidth;
canvas.height = cellHeight;
ctx.imageSmoothingEnabled = false;
const gif = new GIF({ workers: 2, quality: 10, width: maxWidth, height: maxHeight, workerScript: gifWorkerUrl });
const gif = new GIF({ workers: 2, quality: 10, width: cellWidth, height: cellHeight, workerScript: gifWorkerUrl });
sprites.value.forEach(sprite => {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = '#f9fafb';
ctx.fillRect(0, 0, maxWidth, maxHeight);
ctx.drawImage(sprite.img, Math.floor(sprite.x), Math.floor(sprite.y));
ctx.fillRect(0, 0, cellWidth, cellHeight);
ctx.drawImage(sprite.img, Math.floor(negativeSpacing + sprite.x), Math.floor(negativeSpacing + sprite.y));
gif.addFrame(ctx, { copy: true, delay: 1000 / fps });
});
@@ -170,18 +187,21 @@ export const useExport = (sprites: Ref<Sprite[]>, columns: Ref<number>) => {
const zip = new JSZip();
const { maxWidth, maxHeight } = getMaxDimensions(sprites.value);
const negativeSpacing = calculateNegativeSpacing();
const cellWidth = maxWidth + negativeSpacing;
const cellHeight = maxHeight + negativeSpacing;
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
if (!ctx) return;
canvas.width = maxWidth;
canvas.height = maxHeight;
canvas.width = cellWidth;
canvas.height = cellHeight;
ctx.imageSmoothingEnabled = false;
sprites.value.forEach((sprite, index) => {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = '#f9fafb';
ctx.fillRect(0, 0, maxWidth, maxHeight);
ctx.drawImage(sprite.img, Math.floor(sprite.x), Math.floor(sprite.y));
ctx.fillRect(0, 0, cellWidth, cellHeight);
ctx.drawImage(sprite.img, Math.floor(negativeSpacing + sprite.x), Math.floor(negativeSpacing + sprite.y));
const dataURL = canvas.toDataURL('image/png');
const binary = atob(dataURL.split(',')[1]);
const buf = new ArrayBuffer(binary.length);