Spaces:
Running
Running
| <html lang="pt-BR"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Magic Scene Creator</title> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <link href="https://unpkg.com/aos@2.3.1/dist/aos.css" rel="stylesheet"> | |
| <script src="https://unpkg.com/aos@2.3.1/dist/aos.js"></script> | |
| <script src="https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.js"></script> | |
| <script src="https://unpkg.com/feather-icons"></script> | |
| <style> | |
| .file-input-label:hover .file-input-icon { | |
| transform: translateY(-5px); | |
| transition: all 0.3s ease; | |
| } | |
| .result-card { | |
| box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04); | |
| } | |
| .animate-pulse-slow { | |
| animation: pulse 3s cubic-bezier(0.4, 0, 0.6, 1) infinite; | |
| } | |
| @keyframes pulse { | |
| 0%, 100% { opacity: 1; } | |
| 50% { opacity: 0.5; } | |
| } | |
| </style> | |
| </head> | |
| <body class="bg-gradient-to-br from-indigo-50 to-purple-50 min-h-screen"> | |
| <div class="container mx-auto px-4 py-12"> | |
| <!-- Header --> | |
| <header class="text-center mb-16" data-aos="fade-down"> | |
| <h1 class="text-4xl md:text-5xl font-bold text-indigo-900 mb-4">Magic Scene Creator</h1> | |
| <p class="text-lg text-indigo-700 max-w-2xl mx-auto">Transforme suas fotos em ilustrações mágicas com um toque pessoal</p> | |
| </header> | |
| <!-- Main Flow --> | |
| <div class="max-w-3xl mx-auto bg-white rounded-2xl shadow-xl overflow-hidden"> | |
| <!-- Step 1: Upload Photo --> | |
| <div id="step-1" class="p-8" data-aos="fade-up"> | |
| <div class="flex items-center mb-6"> | |
| <div class="flex items-center justify-center w-10 h-10 rounded-full bg-indigo-600 text-white font-bold mr-4">1</div> | |
| <h2 class="text-2xl font-semibold text-gray-800">Envie sua foto</h2> | |
| </div> | |
| <div class="mt-6"> | |
| <label for="photo-upload" class="file-input-label cursor-pointer flex flex-col items-center justify-center border-2 border-dashed border-indigo-300 rounded-xl p-12 text-center hover:border-indigo-400 transition-colors duration-300"> | |
| <div class="file-input-icon mb-4"> | |
| <i data-feather="upload" class="w-12 h-12 text-indigo-500"></i> | |
| </div> | |
| <span class="text-lg font-medium text-indigo-700 mb-2">Clique para selecionar uma foto</span> | |
| <span class="text-sm text-gray-500">Formatos suportados: JPG, PNG (até 5MB)</span> | |
| <input id="photo-upload" type="file" accept="image/*" class="hidden"> | |
| </label> | |
| </div> | |
| <div id="preview-container" class="mt-6 hidden"> | |
| <h3 class="text-lg font-medium text-gray-700 mb-3">Pré-visualização:</h3> | |
| <div class="relative"> | |
| <img id="image-preview" src="" alt="Preview" class="rounded-lg w-full max-h-80 object-contain border border-gray-200"> | |
| <button id="change-photo" class="absolute top-2 right-2 bg-white rounded-full p-2 shadow-md hover:bg-gray-100"> | |
| <i data-feather="x" class="w-4 h-4 text-gray-600"></i> | |
| </button> | |
| </div> | |
| </div> | |
| <div class="mt-8 flex justify-end"> | |
| <button id="next-step-1" class="px-6 py-3 bg-indigo-600 text-white rounded-lg font-medium hover:bg-indigo-700 transition-colors duration-300 disabled:opacity-50 disabled:cursor-not-allowed" disabled> | |
| Próximo <i data-feather="arrow-right" class="w-4 h-4 ml-2 inline"></i> | |
| </button> | |
| </div> | |
| </div> | |
| <!-- Step 2: Choose Style --> | |
| <div id="step-2" class="p-8 border-t border-gray-200 hidden" data-aos="fade-up"> | |
| <div class="flex items-center mb-6"> | |
| <div class="flex items-center justify-center w-10 h-10 rounded-full bg-indigo-600 text-white font-bold mr-4">2</div> | |
| <h2 class="text-2xl font-semibold text-gray-800">Escolha o estilo</h2> | |
| </div> | |
| <p class="text-gray-600 mb-8">Como você gostaria que sua foto fosse transformada?</p> | |
| <div class="grid grid-cols-1 md:grid-cols-2 gap-6"> | |
| <div class="style-option cursor-pointer group relative overflow-hidden rounded-xl border-2 border-transparent hover:border-indigo-400 transition-all duration-300"> | |
| <input type="radio" name="style" id="childish" value="childish" class="hidden peer"> | |
| <label for="childish" class="block"> | |
| <div class="relative"> | |
| <img src="http://static.photos/education/640x360/1" alt="Estilo Infantil" class="w-full h-48 object-cover"> | |
| <div class="absolute inset-0 bg-gradient-to-t from-black/60 to-transparent"></div> | |
| <div class="absolute bottom-0 left-0 p-4"> | |
| <h3 class="text-xl font-bold text-white">Infantil Lúdico</h3> | |
| <p class="text-indigo-100">Cores vibrantes, elementos mágicos</p> | |
| </div> | |
| </div> | |
| </label> | |
| <div class="absolute top-4 right-4 w-6 h-6 rounded-full border-2 border-white bg-white peer-checked:bg-indigo-600 peer-checked:border-indigo-600 transition-colors duration-300"></div> | |
| </div> | |
| <div class="style-option cursor-pointer group relative overflow-hidden rounded-xl border-2 border-transparent hover:border-indigo-400 transition-all duration-300"> | |
| <input type="radio" name="style" id="elegant" value="elegant" class="hidden peer"> | |
| <label for="elegant" class="block"> | |
| <div class="relative"> | |
| <img src="http://static.photos/minimal/640x360/2" alt="Estilo Adulto" class="w-full h-48 object-cover"> | |
| <div class="absolute inset-0 bg-gradient-to-t from-black/60 to-transparent"></div> | |
| <div class="absolute bottom-0 left-0 p-4"> | |
| <h3 class="text-xl font-bold text-white">Adulto Elegante</h3> | |
| <p class="text-indigo-100">Linhas limpas, tons sofisticados</p> | |
| </div> | |
| </div> | |
| </label> | |
| <div class="absolute top-4 right-4 w-6 h-6 rounded-full border-2 border-white bg-white peer-checked:bg-indigo-600 peer-checked:border-indigo-600 transition-colors duration-300"></div> | |
| </div> | |
| </div> | |
| <div class="mt-8 flex justify-between"> | |
| <button id="prev-step-2" class="px-6 py-3 bg-gray-200 text-gray-700 rounded-lg font-medium hover:bg-gray-300 transition-colors duration-300"> | |
| <i data-feather="arrow-left" class="w-4 h-4 mr-2 inline"></i> Voltar | |
| </button> | |
| <button id="next-step-2" class="px-6 py-3 bg-indigo-600 text-white rounded-lg font-medium hover:bg-indigo-700 transition-colors duration-300 disabled:opacity-50 disabled:cursor-not-allowed" disabled> | |
| Próximo <i data-feather="arrow-right" class="w-4 h-4 ml-2 inline"></i> | |
| </button> | |
| </div> | |
| </div> | |
| <!-- Step 3: Add Text --> | |
| <div id="step-3" class="p-8 border-t border-gray-200 hidden" data-aos="fade-up"> | |
| <div class="flex items-center mb-6"> | |
| <div class="flex items-center justify-center w-10 h-10 rounded-full bg-indigo-600 text-white font-bold mr-4">3</div> | |
| <h2 class="text-2xl font-semibold text-gray-800">Adicione um texto</h2> | |
| </div> | |
| <p class="text-gray-600 mb-6">Insira uma frase ou dedicatória para aparecer na imagem final:</p> | |
| <div class="mb-6"> | |
| <textarea id="custom-text" rows="3" class="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 transition-all duration-300" placeholder="Ex: Feliz aniversário! Que seu dia seja mágico como essa ilustração"></textarea> | |
| <p class="text-sm text-gray-500 mt-2">Máximo de 120 caracteres</p> | |
| </div> | |
| <div class="flex items-center mb-6"> | |
| <input type="checkbox" id="add-watermark" class="w-4 h-4 text-indigo-600 rounded focus:ring-indigo-500"> | |
| <label for="add-watermark" class="ml-2 text-gray-700">Adicionar marca d'água "Criado com Magic Scene"</label> | |
| </div> | |
| <div class="mt-8 flex justify-between"> | |
| <button id="prev-step-3" class="px-6 py-3 bg-gray-200 text-gray-700 rounded-lg font-medium hover:bg-gray-300 transition-colors duration-300"> | |
| <i data-feather="arrow-left" class="w-4 h-4 mr-2 inline"></i> Voltar | |
| </button> | |
| <button id="generate-btn" class="px-6 py-3 bg-gradient-to-r from-indigo-600 to-purple-600 text-white rounded-lg font-medium hover:from-indigo-700 hover:to-purple-700 transition-colors duration-300 flex items-center"> | |
| <span id="generate-text">Gerar Imagem</span> | |
| <span id="generating-spinner" class="hidden ml-2"> | |
| <svg class="animate-spin h-5 w-5 text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"> | |
| <circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle> | |
| <path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path> | |
| </svg> | |
| </span> | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Result Section (hidden initially) --> | |
| <div id="result-section" class="max-w-3xl mx-auto mt-12 hidden" data-aos="fade-up"> | |
| <div class="bg-white rounded-2xl shadow-xl overflow-hidden"> | |
| <div class="p-8"> | |
| <div class="flex items-center mb-6"> | |
| <div class="flex items-center justify-center w-10 h-10 rounded-full bg-green-600 text-white font-bold mr-4"> | |
| <i data-feather="check" class="w-5 h-5"></i> | |
| </div> | |
| <h2 class="text-2xl font-semibold text-gray-800">Sua imagem está pronta!</h2> | |
| </div> | |
| <div class="result-card bg-gray-50 rounded-xl p-6 mb-8"> | |
| <div class="relative"> | |
| <img id="final-image" src="" alt="Imagem Final" class="w-full rounded-lg border border-gray-200"> | |
| <div id="final-text-overlay" class="absolute bottom-0 left-0 right-0 p-4 text-center text-white font-medium text-shadow" style="text-shadow: 1px 1px 3px rgba(0,0,0,0.8);"></div> | |
| </div> | |
| </div> | |
| <div class="flex flex-col sm:flex-row gap-4 justify-center"> | |
| <button id="download-btn" class="px-6 py-3 bg-green-600 text-white rounded-lg font-medium hover:bg-green-700 transition-colors duration-300 flex items-center justify-center"> | |
| <i data-feather="download" class="w-4 h-4 mr-2"></i> Baixar Imagem | |
| </button> | |
| <button id="share-btn" class="px-6 py-3 bg-blue-600 text-white rounded-lg font-medium hover:bg-blue-700 transition-colors duration-300 flex items-center justify-center"> | |
| <i data-feather="share-2" class="w-4 h-4 mr-2"></i> Compartilhar | |
| </button> | |
| <button id="new-creation-btn" class="px-6 py-3 bg-indigo-600 text-white rounded-lg font-medium hover:bg-indigo-700 transition-colors duration-300 flex items-center justify-center"> | |
| <i data-feather="plus" class="w-4 h-4 mr-2"></i> Nova Criação | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <script> | |
| document.addEventListener('DOMContentLoaded', function() { | |
| // Initialize AOS and Feather Icons | |
| AOS.init({ | |
| duration: 800, | |
| once: true | |
| }); | |
| feather.replace(); | |
| // DOM Elements | |
| const photoUpload = document.getElementById('photo-upload'); | |
| const previewContainer = document.getElementById('preview-container'); | |
| const imagePreview = document.getElementById('image-preview'); | |
| const changePhotoBtn = document.getElementById('change-photo'); | |
| const nextStep1Btn = document.getElementById('next-step-1'); | |
| const step1 = document.getElementById('step-1'); | |
| const step2 = document.getElementById('step-2'); | |
| const step3 = document.getElementById('step-3'); | |
| const prevStep2Btn = document.getElementById('prev-step-2'); | |
| const nextStep2Btn = document.getElementById('next-step-2'); | |
| const prevStep3Btn = document.getElementById('prev-step-3'); | |
| const generateBtn = document.getElementById('generate-btn'); | |
| const generateText = document.getElementById('generate-text'); | |
| const generatingSpinner = document.getElementById('generating-spinner'); | |
| const resultSection = document.getElementById('result-section'); | |
| const finalImage = document.getElementById('final-image'); | |
| const finalTextOverlay = document.getElementById('final-text-overlay'); | |
| const downloadBtn = document.getElementById('download-btn'); | |
| const shareBtn = document.getElementById('share-btn'); | |
| const newCreationBtn = document.getElementById('new-creation-btn'); | |
| const customText = document.getElementById('custom-text'); | |
| // Variables to store user selections | |
| let uploadedImage = null; | |
| let selectedStyle = null; | |
| // Event Listeners | |
| photoUpload.addEventListener('change', function(e) { | |
| if (e.target.files && e.target.files[0]) { | |
| const reader = new FileReader(); | |
| reader.onload = function(event) { | |
| imagePreview.src = event.target.result; | |
| previewContainer.classList.remove('hidden'); | |
| uploadedImage = event.target.result; | |
| nextStep1Btn.disabled = false; | |
| }; | |
| reader.readAsDataURL(e.target.files[0]); | |
| } | |
| }); | |
| changePhotoBtn.addEventListener('click', function() { | |
| photoUpload.value = ''; | |
| previewContainer.classList.add('hidden'); | |
| nextStep1Btn.disabled = true; | |
| uploadedImage = null; | |
| }); | |
| nextStep1Btn.addEventListener('click', function() { | |
| step1.classList.add('hidden'); | |
| step2.classList.remove('hidden'); | |
| window.scrollTo({ top: 0, behavior: 'smooth' }); | |
| }); | |
| document.querySelectorAll('input[name="style"]').forEach(radio => { | |
| radio.addEventListener('change', function() { | |
| selectedStyle = this.value; | |
| nextStep2Btn.disabled = false; | |
| }); | |
| }); | |
| prevStep2Btn.addEventListener('click', function() { | |
| step2.classList.add('hidden'); | |
| step1.classList.remove('hidden'); | |
| window.scrollTo({ top: 0, behavior: 'smooth' }); | |
| }); | |
| nextStep2Btn.addEventListener('click', function() { | |
| step2.classList.add('hidden'); | |
| step3.classList.remove('hidden'); | |
| window.scrollTo({ top: 0, behavior: 'smooth' }); | |
| }); | |
| prevStep3Btn.addEventListener('click', function() { | |
| step3.classList.add('hidden'); | |
| step2.classList.remove('hidden'); | |
| window.scrollTo({ top: 0, behavior: 'smooth' }); | |
| }); | |
| generateBtn.addEventListener('click', function() { | |
| // Simulate processing | |
| generateText.classList.add('hidden'); | |
| generatingSpinner.classList.remove('hidden'); | |
| generateBtn.disabled = true; | |
| // In a real app, this would call an API to process the image | |
| setTimeout(() => { | |
| // For demo purposes, we'll just use the uploaded image with some text | |
| finalImage.src = uploadedImage; | |
| finalTextOverlay.textContent = customText.value || ''; | |
| // Show result | |
| step3.classList.add('hidden'); | |
| resultSection.classList.remove('hidden'); | |
| generateText.classList.remove('hidden'); | |
| generatingSpinner.classList.add('hidden'); | |
| generateBtn.disabled = false; | |
| window.scrollTo({ top: 0, behavior: 'smooth' }); | |
| }, 2000); | |
| }); | |
| downloadBtn.addEventListener('click', function() { | |
| // In a real app, this would download the processed image | |
| alert('Download iniciado! (simulação)'); | |
| }); | |
| shareBtn.addEventListener('click', function() { | |
| // In a real app, this would implement sharing functionality | |
| alert('Compartilhando... (simulação)'); | |
| }); | |
| newCreationBtn.addEventListener('click', function() { | |
| // Reset the flow | |
| photoUpload.value = ''; | |
| previewContainer.classList.add('hidden'); | |
| nextStep1Btn.disabled = true; | |
| uploadedImage = null; | |
| document.querySelector('input[name="style"]:checked')?.checked = false; | |
| selectedStyle = null; | |
| nextStep2Btn.disabled = true; | |
| customText.value = ''; | |
| document.getElementById('add-watermark').checked = false; | |
| resultSection.classList.add('hidden'); | |
| step1.classList.remove('hidden'); | |
| window.scrollTo({ top: 0, behavior: 'smooth' }); | |
| }); | |
| // Character counter for text input | |
| customText.addEventListener('input', function() { | |
| const maxLength = 120; | |
| const currentLength = this.value.length; | |
| if (currentLength > maxLength) { | |
| this.value = this.value.substring(0, maxLength); | |
| } | |
| }); | |
| }); | |
| </script> | |
| </body> | |
| </html> | |