Allow for JSON import/export
This commit is contained in:
parent
5e2a231c13
commit
f54b87140c
155
src/App.vue
155
src/App.vue
@ -9,7 +9,15 @@
|
||||
</div>
|
||||
|
||||
<div class="bg-white rounded-2xl shadow-soft p-8">
|
||||
<div class="flex justify-between items-center mb-6">
|
||||
<h2 class="text-xl font-semibold text-gray-800">Upload sprites</h2>
|
||||
<button @click="openJSONImportDialog" class="px-4 py-2 bg-indigo-500 hover:bg-indigo-600 text-white font-medium rounded-lg transition-colors flex items-center space-x-2">
|
||||
<i class="fas fa-file-import"></i>
|
||||
<span>Import JSON</span>
|
||||
</button>
|
||||
</div>
|
||||
<file-uploader @upload-sprites="handleSpritesUpload" />
|
||||
<input ref="jsonFileInput" type="file" accept=".json,application/json" class="hidden" @change="handleJSONFileChange" />
|
||||
|
||||
<div v-if="sprites.length > 0" class="mt-8">
|
||||
<div class="flex flex-wrap items-center gap-6 mb-8">
|
||||
@ -23,6 +31,11 @@
|
||||
<span>Download spritesheet</span>
|
||||
</button>
|
||||
|
||||
<button @click="exportSpritesheetJSON" class="px-6 py-2.5 bg-purple-500 hover:bg-purple-600 text-white font-medium rounded-lg transition-colors flex items-center space-x-2">
|
||||
<i class="fas fa-file-code"></i>
|
||||
<span>Export as JSON</span>
|
||||
</button>
|
||||
|
||||
<button @click="openPreviewModal" class="px-6 py-2.5 bg-green-500 hover:bg-green-600 text-white font-medium rounded-lg transition-colors flex items-center space-x-2">
|
||||
<i class="fas fa-play"></i>
|
||||
<span>Preview animation</span>
|
||||
@ -65,8 +78,19 @@
|
||||
const columns = ref(4);
|
||||
const isPreviewModalOpen = ref(false);
|
||||
const isHelpModalOpen = ref(false);
|
||||
const jsonFileInput = ref<HTMLInputElement | null>(null);
|
||||
|
||||
const handleSpritesUpload = (files: File[]) => {
|
||||
// Check if any of the files is a JSON file
|
||||
const jsonFile = files.find(file => file.type === 'application/json' || file.name.endsWith('.json'));
|
||||
|
||||
if (jsonFile) {
|
||||
// If it's a JSON file, try to import it
|
||||
importSpritesheetJSON(jsonFile);
|
||||
return;
|
||||
}
|
||||
|
||||
// Otherwise, process as normal image files
|
||||
Promise.all(
|
||||
files.map(file => {
|
||||
return new Promise<Sprite>(resolve => {
|
||||
@ -164,4 +188,135 @@
|
||||
const closeHelpModal = () => {
|
||||
isHelpModalOpen.value = false;
|
||||
};
|
||||
|
||||
// Export spritesheet as JSON with base64 images
|
||||
const exportSpritesheetJSON = async () => {
|
||||
if (sprites.value.length === 0) return;
|
||||
|
||||
// Create an array to store sprite data with base64 images
|
||||
const spritesData = await Promise.all(
|
||||
sprites.value.map(async (sprite, index) => {
|
||||
// Create a canvas for each sprite to get its base64 data
|
||||
const canvas = document.createElement('canvas');
|
||||
const ctx = canvas.getContext('2d');
|
||||
if (!ctx) return null;
|
||||
|
||||
// Set canvas size to match the sprite
|
||||
canvas.width = sprite.width;
|
||||
canvas.height = sprite.height;
|
||||
|
||||
// Draw the sprite
|
||||
ctx.drawImage(sprite.img, 0, 0);
|
||||
|
||||
// Get base64 data
|
||||
const base64Data = canvas.toDataURL('image/png');
|
||||
|
||||
return {
|
||||
id: sprite.id,
|
||||
width: sprite.width,
|
||||
height: sprite.height,
|
||||
x: sprite.x,
|
||||
y: sprite.y,
|
||||
base64: base64Data,
|
||||
name: sprite.file.name,
|
||||
};
|
||||
})
|
||||
);
|
||||
|
||||
// Create JSON object with all necessary data
|
||||
const jsonData = {
|
||||
columns: columns.value,
|
||||
sprites: spritesData.filter(Boolean), // Remove any null values
|
||||
};
|
||||
|
||||
// Convert to JSON string
|
||||
const jsonString = JSON.stringify(jsonData, null, 2);
|
||||
|
||||
// Create download link
|
||||
const blob = new Blob([jsonString], { type: 'application/json' });
|
||||
const url = URL.createObjectURL(blob);
|
||||
const link = document.createElement('a');
|
||||
link.download = 'spritesheet.json';
|
||||
link.href = url;
|
||||
link.click();
|
||||
|
||||
// Clean up
|
||||
URL.revokeObjectURL(url);
|
||||
};
|
||||
|
||||
// Open file dialog for JSON import
|
||||
const openJSONImportDialog = () => {
|
||||
jsonFileInput.value?.click();
|
||||
};
|
||||
|
||||
// Handle JSON file selection
|
||||
const handleJSONFileChange = (event: Event) => {
|
||||
const input = event.target as HTMLInputElement;
|
||||
if (input.files && input.files.length > 0) {
|
||||
const jsonFile = input.files[0];
|
||||
importSpritesheetJSON(jsonFile);
|
||||
// Reset input value so uploading the same file again will trigger the event
|
||||
if (jsonFileInput.value) jsonFileInput.value.value = '';
|
||||
}
|
||||
};
|
||||
|
||||
// Import spritesheet from JSON
|
||||
const importSpritesheetJSON = async (jsonFile: File) => {
|
||||
try {
|
||||
const jsonText = await jsonFile.text();
|
||||
const jsonData = JSON.parse(jsonText);
|
||||
|
||||
if (!jsonData.sprites || !Array.isArray(jsonData.sprites)) {
|
||||
throw new Error('Invalid JSON format: missing sprites array');
|
||||
}
|
||||
|
||||
// Set columns if available
|
||||
if (jsonData.columns && typeof jsonData.columns === 'number') {
|
||||
columns.value = jsonData.columns;
|
||||
}
|
||||
|
||||
// Process each sprite
|
||||
const newSprites = await Promise.all(
|
||||
jsonData.sprites.map(async (spriteData: any) => {
|
||||
return new Promise<Sprite>(resolve => {
|
||||
// Create image from base64
|
||||
const img = new Image();
|
||||
img.onload = () => {
|
||||
// Create a file from the base64 data
|
||||
const byteString = atob(spriteData.base64.split(',')[1]);
|
||||
const mimeType = spriteData.base64.split(',')[0].split(':')[1].split(';')[0];
|
||||
const ab = new ArrayBuffer(byteString.length);
|
||||
const ia = new Uint8Array(ab);
|
||||
|
||||
for (let i = 0; i < byteString.length; i++) {
|
||||
ia[i] = byteString.charCodeAt(i);
|
||||
}
|
||||
|
||||
const blob = new Blob([ab], { type: mimeType });
|
||||
const fileName = spriteData.name || `sprite-${spriteData.id}.png`;
|
||||
const file = new File([blob], fileName, { type: mimeType });
|
||||
|
||||
resolve({
|
||||
id: spriteData.id || crypto.randomUUID(),
|
||||
file,
|
||||
img,
|
||||
url: spriteData.base64,
|
||||
width: spriteData.width,
|
||||
height: spriteData.height,
|
||||
x: spriteData.x || 0,
|
||||
y: spriteData.y || 0,
|
||||
});
|
||||
};
|
||||
img.src = spriteData.base64;
|
||||
});
|
||||
})
|
||||
);
|
||||
|
||||
// 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.');
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
@ -9,19 +9,20 @@
|
||||
@dragleave.prevent="isDragging = false"
|
||||
@dragover.prevent
|
||||
@drop.prevent="handleDrop"
|
||||
@click="openFileDialog"
|
||||
>
|
||||
<input ref="fileInput" type="file" multiple accept="image/*" class="hidden" @change="handleFileChange" />
|
||||
<input ref="fileInput" type="file" multiple accept="image/*,.json" class="hidden" @change="handleFileChange" />
|
||||
|
||||
<div class="mb-6">
|
||||
<img src="@/assets/images/file.svg" alt="File upload" class="w-20 h-20 mx-auto mb-4 opacity-75" />
|
||||
</div>
|
||||
|
||||
<p class="text-xl font-medium text-gray-700 mb-2">Drag and drop your sprite images here</p>
|
||||
<p class="text-xl font-medium text-gray-700 mb-2">Drag and drop your sprite images or JSON file here</p>
|
||||
<p class="text-sm text-gray-500 mb-6">or</p>
|
||||
|
||||
<button @click="openFileDialog" class="px-6 py-2.5 bg-blue-500 hover:bg-blue-600 text-white font-medium rounded-lg transition-colors inline-flex items-center space-x-2">
|
||||
<button class="px-6 py-2.5 bg-blue-500 hover:bg-blue-600 text-white font-medium rounded-lg transition-colors inline-flex items-center space-x-2 cursor-pointer">
|
||||
<i class="fas fa-folder-open"></i>
|
||||
<span>Select Files</span>
|
||||
<span>Select files</span>
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
@ -55,7 +56,7 @@
|
||||
|
||||
if (event.dataTransfer?.files && event.dataTransfer.files.length > 0) {
|
||||
const files = Array.from(event.dataTransfer.files).filter(file => {
|
||||
return file.type.startsWith('image/');
|
||||
return file.type.startsWith('image/') || file.type === 'application/json' || file.name.endsWith('.json');
|
||||
});
|
||||
|
||||
if (files.length > 0) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user