Added help vid and help modal

This commit is contained in:
Dennis Postma 2025-04-05 23:33:19 +02:00
parent 99b0868d36
commit 5e2a231c13
5 changed files with 207 additions and 48 deletions

View File

@ -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

Binary file not shown.

View 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>

View File

@ -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) {

View File

@ -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>