Added help vid and help modal
This commit is contained in:
parent
99b0868d36
commit
5e2a231c13
31
src/App.vue
31
src/App.vue
@ -3,15 +3,9 @@
|
||||
<div class="max-w-6xl mx-auto">
|
||||
<h1 class="text-4xl font-bold text-center mb-8 text-gray-900 tracking-tight">Spritesheet generator</h1>
|
||||
<div class="flex justify-center space-x-4 mb-8">
|
||||
<a href="https://gitea.directonline.io/noxious/spritesheet-generator" target="_blank" class="text-blue-500 hover:text-blue-600 transition-colors">
|
||||
<i class="fab fa-github"></i> Source
|
||||
</a>
|
||||
<a href="https://discord.gg/JTev3nzeDa" target="_blank" class="text-blue-500 hover:text-blue-600 transition-colors">
|
||||
<i class="fab fa-discord"></i> Discord
|
||||
</a>
|
||||
<a href="#" class="text-blue-500 hover:text-blue-600 transition-colors">
|
||||
<i class="fas fa-question-circle"></i> Help
|
||||
</a>
|
||||
<a href="https://gitea.directonline.io/noxious/spritesheet-generator" target="_blank" class="text-blue-500 hover:text-blue-600 transition-colors"> <i class="fab fa-github"></i> Source </a>
|
||||
<a href="https://discord.gg/JTev3nzeDa" target="_blank" class="text-blue-500 hover:text-blue-600 transition-colors"> <i class="fab fa-discord"></i> Discord </a>
|
||||
<a href="#" @click.prevent="openHelpModal" class="text-blue-500 hover:text-blue-600 transition-colors"> <i class="fas fa-question-circle"></i> Help </a>
|
||||
</div>
|
||||
|
||||
<div class="bg-white rounded-2xl shadow-soft p-8">
|
||||
@ -26,12 +20,12 @@
|
||||
|
||||
<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>
|
||||
<span>Download spritesheet</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>
|
||||
<span>Preview animation</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@ -40,9 +34,11 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Modal :is-open="isPreviewModalOpen" @close="closePreviewModal" title="Animation Preview">
|
||||
<Modal :is-open="isPreviewModalOpen" @close="closePreviewModal" title="Animation preview">
|
||||
<sprite-preview :sprites="sprites" :columns="columns" @update-sprite="updateSpritePosition" />
|
||||
</Modal>
|
||||
|
||||
<HelpModal :is-open="isHelpModalOpen" @close="closeHelpModal" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -52,6 +48,7 @@
|
||||
import SpriteCanvas from './components/SpriteCanvas.vue';
|
||||
import Modal from './components/utilities/Modal.vue';
|
||||
import SpritePreview from './components/SpritePreview.vue';
|
||||
import HelpModal from './components/HelpModal.vue';
|
||||
|
||||
interface Sprite {
|
||||
id: string;
|
||||
@ -67,6 +64,7 @@
|
||||
const sprites = ref<Sprite[]>([]);
|
||||
const columns = ref(4);
|
||||
const isPreviewModalOpen = ref(false);
|
||||
const isHelpModalOpen = ref(false);
|
||||
|
||||
const handleSpritesUpload = (files: File[]) => {
|
||||
Promise.all(
|
||||
@ -157,4 +155,13 @@
|
||||
const closePreviewModal = () => {
|
||||
isPreviewModalOpen.value = false;
|
||||
};
|
||||
|
||||
// Help modal control
|
||||
const openHelpModal = () => {
|
||||
isHelpModalOpen.value = true;
|
||||
};
|
||||
|
||||
const closeHelpModal = () => {
|
||||
isHelpModalOpen.value = false;
|
||||
};
|
||||
</script>
|
||||
|
BIN
src/assets/tut.mp4
Normal file
BIN
src/assets/tut.mp4
Normal file
Binary file not shown.
149
src/components/HelpModal.vue
Normal file
149
src/components/HelpModal.vue
Normal file
@ -0,0 +1,149 @@
|
||||
<template>
|
||||
<Modal :is-open="isOpen" @close="close" title="Help & information" :initialWidth="800" :initialHeight="600">
|
||||
<div class="flex flex-col h-full">
|
||||
<!-- Tabs -->
|
||||
<div class="flex border-b border-gray-200">
|
||||
<button v-for="(tab, index) in tabs" :key="index" @click="activeTab = index" class="px-4 py-2 font-medium text-sm transition-colors" :class="activeTab === index ? 'text-blue-600 border-b-2 border-blue-500' : 'text-gray-600 hover:text-blue-500'">
|
||||
{{ tab.name }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Tab Content -->
|
||||
<div class="flex-1 overflow-auto p-4">
|
||||
<!-- Video Instructions Tab -->
|
||||
<div v-if="activeTab === 0" class="h-full flex flex-col">
|
||||
<h3 class="text-lg font-semibold mb-4">Video tutorial</h3>
|
||||
<div class="flex-1 bg-gray-100 rounded-lg flex items-center justify-center">
|
||||
<div class="w-full aspect-video max-w-3xl mx-auto">
|
||||
<video controls class="w-full h-full object-contain rounded-lg shadow-md">
|
||||
<source src="@/assets/tut.mp4" type="video/mp4" />
|
||||
</video>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- About & Instructions Tab -->
|
||||
<div v-if="activeTab === 1" class="h-full overflow-y-auto">
|
||||
<h3 class="text-lg font-semibold mb-4">About Spritesheet generator</h3>
|
||||
|
||||
<div class="max-w-none text-gray-700">
|
||||
<p class="mb-4 text-base leading-relaxed">Spritesheet generator is a free, open-source tool for creating spritesheets for game development and animation projects. This tool allows you to upload individual sprite images and arrange them into a spritesheet with customizable layout.</p>
|
||||
|
||||
<h4 class="mt-6 mb-3 text-lg font-medium text-gray-900">How to use:</h4>
|
||||
<ol class="list-decimal pl-6 space-y-2 mb-4">
|
||||
<li>Upload your sprite images by dragging and dropping them or clicking the upload area</li>
|
||||
<li>Arrange your sprites by dragging them to the desired position</li>
|
||||
<li>Adjust the number of columns to change the layout</li>
|
||||
<li>Preview your animation using the "Preview Animation" button</li>
|
||||
<li>Download your spritesheet as a PNG file</li>
|
||||
</ol>
|
||||
|
||||
<h4 class="mt-6 mb-3 text-lg font-medium text-gray-900">Tips:</h4>
|
||||
<ul class="list-disc pl-6 space-y-2">
|
||||
<li>For best results, use sprites with consistent dimensions</li>
|
||||
<li>The preview animation plays frames in the order they appear in the spritesheet (left to right, top to bottom)</li>
|
||||
<li>You can adjust the animation speed in the preview window</li>
|
||||
<li>The tool works entirely in your browser - no files are uploaded to any server</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Donations Tab -->
|
||||
<div v-if="activeTab === 2" class="h-full overflow-y-auto">
|
||||
<h3 class="text-lg font-semibold mb-4">Support my <a href="https://xvx.sh" title="XVX" target="_blank" class="text-blue-500 hover:text-blue-600 transition-colors">projects</a>.</h3>
|
||||
|
||||
<p class="mb-6">
|
||||
Spritesheet generator is free and open-source software. If you find it useful, please consider supporting the project with a donation. Check out the source code on
|
||||
<a href="https://gitea.directonline.io/noxious/spritesheet-generator" title="Source code" target="_blank" class="text-blue-500 hover:text-blue-600 transition-colors">Gitea</a>.
|
||||
</p>
|
||||
|
||||
<div class="space-y-6">
|
||||
<!-- Bitcoin -->
|
||||
<div class="p-4 bg-gray-50 rounded-lg border border-gray-200">
|
||||
<div class="flex items-center mb-3">
|
||||
<div class="w-8 h-8 mr-3 flex items-center justify-center bg-orange-100 rounded-full">
|
||||
<svg class="w-5 h-5 text-orange-500" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path
|
||||
d="M23.638 14.904c-1.602 6.43-8.113 10.34-14.542 8.736C2.67 22.05-1.244 15.525.362 9.105 1.962 2.67 8.475-1.243 14.9.358c6.43 1.605 10.342 8.115 8.738 14.548v-.002zm-6.35-4.613c.24-1.59-.974-2.45-2.64-3.03l.54-2.153-1.315-.33-.525 2.107c-.345-.087-.705-.167-1.064-.25l.526-2.127-1.32-.33-.54 2.165c-.285-.067-.565-.132-.84-.2l-1.815-.45-.35 1.407s.975.225.955.236c.535.136.63.486.615.766l-1.477 5.92c-.075.166-.24.406-.614.314.015.02-.96-.24-.96-.24l-.66 1.51 1.71.426.93.242-.54 2.19 1.32.327.54-2.17c.36.1.705.19 1.05.273l-.51 2.154 1.32.33.545-2.19c2.24.427 3.93.257 4.64-1.774.57-1.637-.03-2.58-1.217-3.196.854-.193 1.5-.76 1.68-1.93h.01zm-3.01 4.22c-.404 1.64-3.157.75-4.05.53l.72-2.9c.896.23 3.757.67 3.33 2.37zm.41-4.24c-.37 1.49-2.662.735-3.405.55l.654-2.64c.744.18 3.137.524 2.75 2.084v.006z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<h4 class="text-md font-medium">Bitcoin (BTC)</h4>
|
||||
</div>
|
||||
<div class="flex">
|
||||
<input type="text" readonly value="bc1ql2a3nxnhfwft7qex0cclj5ar2lfsslvs0aygeq" class="flex-1 p-2 text-sm bg-white border border-gray-200 rounded-l-md focus:outline-none" />
|
||||
<button @click="copyToClipboard('bc1ql2a3nxnhfwft7qex0cclj5ar2lfsslvs0aygeq')" class="px-3 bg-blue-500 text-white rounded-r-md hover:bg-blue-600 transition-colors">Copy</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Litecoin -->
|
||||
<div class="p-4 bg-gray-50 rounded-lg border border-gray-200">
|
||||
<div class="flex items-center mb-3">
|
||||
<div class="w-8 h-8 mr-3 flex items-center justify-center bg-blue-100 rounded-full">
|
||||
<svg class="w-5 h-5 text-blue-500" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M12 0a12 12 0 1 0 0 24 12 12 0 0 0 0-24zm-.738 4.8h2.616l-1.654 6.187 2.566-.338-1.017 3.63-2.115.338-1.23 4.6h7.12l-.56 2.07H6.96l1.654-6.16-2.565.337.952-3.38 2.18-.338 1.23-4.662H4.8l.504-2.285h5.958z" />
|
||||
</svg>
|
||||
</div>
|
||||
<h4 class="text-md font-medium">Litecoin (LTC)</h4>
|
||||
</div>
|
||||
<div class="flex">
|
||||
<input type="text" readonly value="ltc1qdkn46hpt39ppmhk25ed2eycu7m2pj5cdzuxw84" class="flex-1 p-2 text-sm bg-white border border-gray-200 rounded-l-md focus:outline-none" />
|
||||
<button @click="copyToClipboard('ltc1qdkn46hpt39ppmhk25ed2eycu7m2pj5cdzuxw84')" class="px-3 bg-blue-500 text-white rounded-r-md hover:bg-blue-600 transition-colors">Copy</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Ethereum -->
|
||||
<div class="p-4 bg-gray-50 rounded-lg border border-gray-200">
|
||||
<div class="flex items-center mb-3">
|
||||
<div class="w-8 h-8 mr-3 flex items-center justify-center bg-purple-100 rounded-full">
|
||||
<svg class="w-5 h-5 text-purple-500" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M11.944 17.97L4.58 13.62 11.943 24l7.37-10.38-7.372 4.35h.003zM12.056 0L4.69 12.223l7.365 4.354 7.365-4.35L12.056 0z" />
|
||||
</svg>
|
||||
</div>
|
||||
<h4 class="text-md font-medium">Ethereum (ETH)</h4>
|
||||
</div>
|
||||
<div class="flex">
|
||||
<input type="text" readonly value="0x30843c72DF6E9A9226d967bf2403602f1C2aB67b" class="flex-1 p-2 text-sm bg-white border border-gray-200 rounded-l-md focus:outline-none" />
|
||||
<button @click="copyToClipboard('0x30843c72DF6E9A9226d967bf2403602f1C2aB67b')" class="px-3 bg-blue-500 text-white rounded-r-md hover:bg-blue-600 transition-colors">Copy</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p class="text-center text-gray-600 mt-6">Thank you for your support! ❤️</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import Modal from './utilities/Modal.vue';
|
||||
|
||||
const props = defineProps<{
|
||||
isOpen: boolean;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'close'): void;
|
||||
}>();
|
||||
|
||||
const activeTab = ref(0);
|
||||
|
||||
const tabs = [{ name: 'Video tutorial' }, { name: 'About & instructions' }, { name: 'Support the project' }];
|
||||
|
||||
const close = () => {
|
||||
emit('close');
|
||||
};
|
||||
|
||||
const copyToClipboard = (text: string) => {
|
||||
navigator.clipboard
|
||||
.writeText(text)
|
||||
.then(() => {
|
||||
alert('Address copied to clipboard!');
|
||||
})
|
||||
.catch(err => {
|
||||
console.error('Failed to copy: ', err);
|
||||
});
|
||||
};
|
||||
</script>
|
@ -46,6 +46,13 @@
|
||||
<span class="text-sm whitespace-nowrap">Show all sprites</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div v-if="showAllSprites" class="w-full">
|
||||
<label class="block text-sm font-medium text-gray-700 mb-2">Hide frames</label>
|
||||
<select multiple v-model="hiddenFrames" class="w-full rounded-md border border-gray-300 shadow-sm px-3 py-2 focus:ring-blue-500 focus:border-blue-500" size="4">
|
||||
<option v-for="(sprite, index) in props.sprites" :key="sprite.id" :value="index" class="py-1">Frame {{ index + 1 }}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -135,6 +142,9 @@
|
||||
// Computed properties
|
||||
const totalFrames = computed(() => props.sprites.length);
|
||||
|
||||
// Add this after other refs
|
||||
const hiddenFrames = ref<number[]>([]);
|
||||
|
||||
// Canvas drawing
|
||||
const calculateMaxDimensions = () => {
|
||||
let maxWidth = 0;
|
||||
@ -186,8 +196,8 @@
|
||||
if (showAllSprites.value && props.sprites.length > 1) {
|
||||
ctx.value.globalAlpha = 0.3;
|
||||
props.sprites.forEach((sprite, index) => {
|
||||
if (index !== currentFrameIndex.value) {
|
||||
ctx.value.drawImage(sprite.img, sprite.x, sprite.y);
|
||||
if (index !== currentFrameIndex.value && !hiddenFrames.value.includes(index)) {
|
||||
ctx.value?.drawImage(sprite.img, sprite.x, sprite.y);
|
||||
}
|
||||
});
|
||||
ctx.value.globalAlpha = 1.0;
|
||||
@ -200,13 +210,6 @@
|
||||
ctx.value.strokeStyle = '#e5e7eb';
|
||||
ctx.value.lineWidth = 1;
|
||||
ctx.value.strokeRect(0, 0, maxWidth, maxHeight);
|
||||
|
||||
// Highlight current frame if draggable
|
||||
if (isDraggable.value) {
|
||||
ctx.value.strokeStyle = '#3b82f6';
|
||||
ctx.value.lineWidth = 2;
|
||||
ctx.value.strokeRect(0, 0, maxWidth, maxHeight);
|
||||
}
|
||||
};
|
||||
|
||||
// Animation control
|
||||
@ -357,6 +360,7 @@
|
||||
watch(zoom, drawPreviewCanvas);
|
||||
watch(isDraggable, drawPreviewCanvas);
|
||||
watch(showAllSprites, drawPreviewCanvas);
|
||||
watch(hiddenFrames, drawPreviewCanvas);
|
||||
|
||||
// Initial draw
|
||||
if (props.sprites.length > 0) {
|
||||
|
@ -1,33 +1,32 @@
|
||||
<template>
|
||||
<Teleport to="body">
|
||||
<div v-if="isOpen" class="fixed inset-0 z-50 flex items-center justify-center">
|
||||
<div
|
||||
ref="modalRef"
|
||||
:style="{
|
||||
position: 'absolute',
|
||||
left: `${position.x}px`,
|
||||
top: `${position.y}px`,
|
||||
width: `${size.width}px`,
|
||||
height: `${size.height}px`,
|
||||
}"
|
||||
class="bg-white rounded-2xl border-2 border-gray-300 shadow-xl flex flex-col"
|
||||
>
|
||||
<!-- Header with drag handle -->
|
||||
<div class="flex justify-between items-center p-4 border-b border-gray-200 cursor-move" @mousedown="startDrag" @touchstart="startDrag">
|
||||
<h3 class="text-2xl font-semibold text-gray-900">{{ title }}</h3>
|
||||
<button @click="close" class="p-2 hover:bg-gray-100 rounded-lg transition-colors">
|
||||
<img src="@/assets/images/close-icon.svg" class="w-5 h-5" alt="Close" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Body -->
|
||||
<div class="p-6 flex-1 overflow-auto">
|
||||
<slot></slot>
|
||||
</div>
|
||||
|
||||
<!-- Resize handle -->
|
||||
<div class="absolute bottom-0 right-0 w-5 h-5 cursor-se-resize" @mousedown="startResize" @touchstart="startResize"></div>
|
||||
<div
|
||||
v-if="isOpen"
|
||||
ref="modalRef"
|
||||
:style="{
|
||||
position: 'absolute',
|
||||
left: `${position.x}px`,
|
||||
top: `${position.y}px`,
|
||||
width: `${size.width}px`,
|
||||
height: `${size.height}px`,
|
||||
}"
|
||||
class="bg-white rounded-2xl border-2 border-gray-300 shadow-xl flex flex-col z-50"
|
||||
>
|
||||
<!-- Header with drag handle -->
|
||||
<div class="flex justify-between items-center p-4 border-b border-gray-200 cursor-move" @mousedown="startDrag" @touchstart="startDrag">
|
||||
<h3 class="text-2xl font-semibold text-gray-900">{{ title }}</h3>
|
||||
<button @click="close" class="p-2 hover:bg-gray-100 rounded-lg transition-colors">
|
||||
<img src="@/assets/images/close-icon.svg" class="w-5 h-5" alt="Close" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Body -->
|
||||
<div class="p-6 flex-1 overflow-auto">
|
||||
<slot></slot>
|
||||
</div>
|
||||
|
||||
<!-- Resize handle -->
|
||||
<div class="absolute bottom-0 right-0 w-5 h-5 cursor-se-resize" @mousedown="startResize" @touchstart="startResize"></div>
|
||||
</div>
|
||||
</Teleport>
|
||||
</template>
|
||||
|
Loading…
x
Reference in New Issue
Block a user