| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| <title>Photo Theme Transformer</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> |
| .dropzone { |
| border: 2px dashed #6366f1; |
| border-radius: 0.75rem; |
| transition: all 0.3s ease; |
| } |
| .dropzone.active { |
| border-color: #4f46e5; |
| background-color: #eef2ff; |
| } |
| .theme-card { |
| transition: all 0.3s ease; |
| cursor: pointer; |
| } |
| .theme-card:hover { |
| transform: translateY(-5px); |
| } |
| .theme-card.selected { |
| border-color: #6366f1; |
| box-shadow: 0 10px 15px -3px rgba(99, 102, 241, 0.2); |
| } |
| .preview-image { |
| max-height: 400px; |
| object-fit: contain; |
| } |
| .result-image { |
| max-height: 500px; |
| object-fit: contain; |
| box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1); |
| } |
| </style> |
| </head> |
| <body class="bg-gray-50 min-h-screen"> |
| <div class="container mx-auto px-4 py-12"> |
| |
| <header class="text-center mb-12" data-aos="fade-down"> |
| <h1 class="text-4xl font-bold text-indigo-600 mb-3">Photo Theme Transformer</h1> |
| <p class="text-lg text-gray-600 max-w-2xl mx-auto">Turn your photos into beautiful themed illustrations with custom text overlays</p> |
| </header> |
|
|
| |
| <div class="max-w-4xl mx-auto bg-white rounded-xl shadow-md overflow-hidden"> |
| |
| <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-100 text-indigo-600 font-bold mr-4">1</div> |
| <h2 class="text-2xl font-semibold text-gray-800">Upload Your Photo</h2> |
| </div> |
| |
| <div class="dropzone p-12 text-center mb-6" id="dropzone"> |
| <div class="flex flex-col items-center justify-center"> |
| <i data-feather="upload" class="w-12 h-12 text-indigo-500 mb-4"></i> |
| <p class="text-lg text-gray-600 mb-2">Drag & drop your photo here</p> |
| <p class="text-sm text-gray-500 mb-4">or</p> |
| <label for="file-upload" class="cursor-pointer bg-indigo-600 text-white px-6 py-2 rounded-lg hover:bg-indigo-700 transition"> |
| Select from device |
| </label> |
| <input id="file-upload" type="file" accept="image/*" class="hidden"> |
| </div> |
| </div> |
| |
| <div id="preview-container" class="hidden"> |
| <h3 class="text-lg font-medium text-gray-700 mb-3">Your Photo Preview</h3> |
| <div class="flex justify-center"> |
| <img id="preview-image" class="preview-image rounded-lg border border-gray-200" src="" alt="Preview"> |
| </div> |
| <div class="flex justify-center mt-4"> |
| <button id="change-photo" class="text-indigo-600 hover:text-indigo-800 flex items-center"> |
| <i data-feather="refresh-cw" class="w-4 h-4 mr-1"></i> Change Photo |
| </button> |
| </div> |
| </div> |
| |
| <div class="flex justify-end mt-6"> |
| <button id="next-to-step-2" class="bg-indigo-600 text-white px-6 py-2 rounded-lg hover:bg-indigo-700 transition disabled:opacity-50" disabled> |
| Next <i data-feather="arrow-right" class="w-4 h-4 ml-1"></i> |
| </button> |
| </div> |
| </div> |
|
|
| |
| <div id="step-2" class="p-8 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-100 text-indigo-600 font-bold mr-4">2</div> |
| <h2 class="text-2xl font-semibold text-gray-800">Choose Your Theme</h2> |
| </div> |
| |
| <div class="grid grid-cols-1 md:grid-cols-2 gap-6 mb-8"> |
| <div class="theme-card p-6 border-2 border-gray-200 rounded-xl" data-theme="child"> |
| <div class="flex items-center mb-4"> |
| <div class="w-10 h-10 rounded-full bg-yellow-100 flex items-center justify-center mr-3"> |
| <i data-feather="smile" class="text-yellow-500"></i> |
| </div> |
| <h3 class="text-xl font-medium text-gray-800">Child Theme</h3> |
| </div> |
| <p class="text-gray-600 mb-4">Playful, colorful illustrations with cartoon-style elements perfect for kids.</p> |
| <div class="flex justify-center"> |
| <img src="http://static.photos/education/640x360/1" alt="Child Theme Example" class="rounded-lg h-40 object-cover"> |
| </div> |
| </div> |
| |
| <div class="theme-card p-6 border-2 border-gray-200 rounded-xl" data-theme="adult"> |
| <div class="flex items-center mb-4"> |
| <div class="w-10 h-10 rounded-full bg-blue-100 flex items-center justify-center mr-3"> |
| <i data-feather="feather" class="text-blue-500"></i> |
| </div> |
| <h3 class="text-xl font-medium text-gray-800">Adult Theme</h3> |
| </div> |
| <p class="text-gray-600 mb-4">Elegant, refined illustrations with sophisticated styling for adults.</p> |
| <div class="flex justify-center"> |
| <img src="http://static.photos/minimal/640x360/2" alt="Adult Theme Example" class="rounded-lg h-40 object-cover"> |
| </div> |
| </div> |
| </div> |
| |
| <div class="flex justify-between mt-6"> |
| <button id="back-to-step-1" class="text-gray-600 hover:text-gray-800 flex items-center"> |
| <i data-feather="arrow-left" class="w-4 h-4 mr-1"></i> Back |
| </button> |
| <button id="next-to-step-3" class="bg-indigo-600 text-white px-6 py-2 rounded-lg hover:bg-indigo-700 transition disabled:opacity-50" disabled> |
| Next <i data-feather="arrow-right" class="w-4 h-4 ml-1"></i> |
| </button> |
| </div> |
| </div> |
|
|
| |
| <div id="step-3" class="p-8 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-100 text-indigo-600 font-bold mr-4">3</div> |
| <h2 class="text-2xl font-semibold text-gray-800">Add Your Text</h2> |
| </div> |
| |
| <div class="mb-6"> |
| <label for="custom-text" class="block text-sm font-medium text-gray-700 mb-2">Enter your text (max 100 characters)</label> |
| <textarea id="custom-text" rows="3" maxlength="100" class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-indigo-500 focus:border-indigo-500" placeholder="E.g., Happy Birthday!"></textarea> |
| <div class="text-right text-sm text-gray-500 mt-1"><span id="char-count">0</span>/100</div> |
| </div> |
| |
| <div class="mb-6"> |
| <label class="block text-sm font-medium text-gray-700 mb-2">Text Style</label> |
| <div class="grid grid-cols-2 md:grid-cols-4 gap-3"> |
| <button class="text-style-btn px-4 py-2 border rounded-lg hover:bg-gray-50" data-style="playful"> |
| <span class="text-yellow-500 font-bold">Playful</span> |
| </button> |
| <button class="text-style-btn px-4 py-2 border rounded-lg hover:bg-gray-50" data-style="elegant"> |
| <span class="text-blue-500 font-serif italic">Elegant</span> |
| </button> |
| <button class="text-style-btn px-4 py-2 border rounded-lg hover:bg-gray-50" data-style="bold"> |
| <span class="text-gray-800 font-bold">Bold</span> |
| </button> |
| <button class="text-style-btn px-4 py-2 border rounded-lg hover:bg-gray-50" data-style="handwritten"> |
| <span class="text-pink-500 font-sans" style="font-family: 'Comic Sans MS', cursive">Handwritten</span> |
| </button> |
| </div> |
| </div> |
| |
| <div class="flex justify-between mt-6"> |
| <button id="back-to-step-2" class="text-gray-600 hover:text-gray-800 flex items-center"> |
| <i data-feather="arrow-left" class="w-4 h-4 mr-1"></i> Back |
| </button> |
| <button id="generate-result" class="bg-indigo-600 text-white px-6 py-2 rounded-lg hover:bg-indigo-700 transition"> |
| Generate Result <i data-feather="wand" class="w-4 h-4 ml-1"></i> |
| </button> |
| </div> |
| </div> |
|
|
| |
| <div id="step-4" class="p-8 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-100 text-indigo-600 font-bold mr-4">4</div> |
| <h2 class="text-2xl font-semibold text-gray-800">Your Transformed Photo</h2> |
| </div> |
| |
| <div class="flex flex-col items-center mb-8"> |
| <div class="relative mb-6"> |
| <img id="result-image" class="result-image rounded-lg" src="" alt="Transformed Photo"> |
| <div id="text-overlay" class="absolute inset-0 flex items-center justify-center pointer-events-none"></div> |
| </div> |
| |
| <div class="flex flex-wrap justify-center gap-4"> |
| <button id="download-btn" class="bg-indigo-600 text-white px-6 py-2 rounded-lg hover:bg-indigo-700 transition flex items-center"> |
| <i data-feather="download" class="w-4 h-4 mr-2"></i> Download |
| </button> |
| <button id="share-btn" class="bg-green-600 text-white px-6 py-2 rounded-lg hover:bg-green-700 transition flex items-center"> |
| <i data-feather="share-2" class="w-4 h-4 mr-2"></i> Share |
| </button> |
| <button id="new-transformation" class="bg-gray-200 text-gray-800 px-6 py-2 rounded-lg hover:bg-gray-300 transition flex items-center"> |
| <i data-feather="repeat" class="w-4 h-4 mr-2"></i> New Transformation |
| </button> |
| </div> |
| </div> |
| </div> |
| </div> |
| </div> |
|
|
| |
| <div id="loading-modal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center hidden z-50"> |
| <div class="bg-white p-8 rounded-xl max-w-md text-center"> |
| <div class="animate-spin rounded-full h-16 w-16 border-t-2 border-b-2 border-indigo-500 mx-auto mb-4"></div> |
| <h3 class="text-xl font-semibold text-gray-800 mb-2">Transforming Your Photo</h3> |
| <p class="text-gray-600">This may take a few moments...</p> |
| </div> |
| </div> |
|
|
| <script> |
| |
| AOS.init(); |
| feather.replace(); |
| |
| |
| const dropzone = document.getElementById('dropzone'); |
| const fileInput = document.getElementById('file-upload'); |
| const previewContainer = document.getElementById('preview-container'); |
| const previewImage = document.getElementById('preview-image'); |
| const changePhotoBtn = document.getElementById('change-photo'); |
| const nextToStep2Btn = document.getElementById('next-to-step-2'); |
| const step1 = document.getElementById('step-1'); |
| const step2 = document.getElementById('step-2'); |
| const step3 = document.getElementById('step-3'); |
| const step4 = document.getElementById('step-4'); |
| const backToStep1Btn = document.getElementById('back-to-step-1'); |
| const nextToStep3Btn = document.getElementById('next-to-step-3'); |
| const backToStep2Btn = document.getElementById('back-to-step-2'); |
| const generateResultBtn = document.getElementById('generate-result'); |
| const themeCards = document.querySelectorAll('.theme-card'); |
| const customText = document.getElementById('custom-text'); |
| const charCount = document.getElementById('char-count'); |
| const textStyleBtns = document.querySelectorAll('.text-style-btn'); |
| const resultImage = document.getElementById('result-image'); |
| const textOverlay = document.getElementById('text-overlay'); |
| const downloadBtn = document.getElementById('download-btn'); |
| const shareBtn = document.getElementById('share-btn'); |
| const newTransformationBtn = document.getElementById('new-transformation'); |
| const loadingModal = document.getElementById('loading-modal'); |
| |
| |
| let selectedFile = null; |
| let selectedTheme = null; |
| let selectedTextStyle = 'playful'; |
| |
| |
| |
| dropzone.addEventListener('click', () => fileInput.click()); |
| dropzone.addEventListener('dragover', (e) => { |
| e.preventDefault(); |
| dropzone.classList.add('active'); |
| }); |
| dropzone.addEventListener('dragleave', () => { |
| dropzone.classList.remove('active'); |
| }); |
| dropzone.addEventListener('drop', (e) => { |
| e.preventDefault(); |
| dropzone.classList.remove('active'); |
| if (e.dataTransfer.files.length) { |
| handleFileUpload(e.dataTransfer.files[0]); |
| } |
| }); |
| fileInput.addEventListener('change', () => { |
| if (fileInput.files.length) { |
| handleFileUpload(fileInput.files[0]); |
| } |
| }); |
| changePhotoBtn.addEventListener('click', () => { |
| previewContainer.classList.add('hidden'); |
| nextToStep2Btn.disabled = true; |
| fileInput.value = ''; |
| selectedFile = null; |
| }); |
| |
| |
| nextToStep2Btn.addEventListener('click', () => { |
| step1.classList.add('hidden'); |
| step2.classList.remove('hidden'); |
| }); |
| backToStep1Btn.addEventListener('click', () => { |
| step2.classList.add('hidden'); |
| step1.classList.remove('hidden'); |
| }); |
| nextToStep3Btn.addEventListener('click', () => { |
| step2.classList.add('hidden'); |
| step3.classList.remove('hidden'); |
| }); |
| backToStep2Btn.addEventListener('click', () => { |
| step3.classList.add('hidden'); |
| step2.classList.remove('hidden'); |
| }); |
| |
| |
| themeCards.forEach(card => { |
| card.addEventListener('click', () => { |
| themeCards.forEach(c => c.classList.remove('selected')); |
| card.classList.add('selected'); |
| selectedTheme = card.dataset.theme; |
| nextToStep3Btn.disabled = false; |
| }); |
| }); |
| |
| |
| customText.addEventListener('input', () => { |
| charCount.textContent = customText.value.length; |
| }); |
| textStyleBtns.forEach(btn => { |
| btn.addEventListener('click', () => { |
| selectedTextStyle = btn.dataset.style; |
| textStyleBtns.forEach(b => b.classList.remove('bg-gray-100')); |
| btn.classList.add('bg-gray-100'); |
| }); |
| }); |
| |
| |
| generateResultBtn.addEventListener('click', () => { |
| loadingModal.classList.remove('hidden'); |
| |
| |
| setTimeout(() => { |
| loadingModal.classList.add('hidden'); |
| step3.classList.add('hidden'); |
| step4.classList.remove('hidden'); |
| |
| |
| |
| resultImage.src = selectedFile ? URL.createObjectURL(selectedFile) : ''; |
| |
| |
| textOverlay.innerHTML = ''; |
| if (customText.value.trim()) { |
| const textElement = document.createElement('div'); |
| textElement.textContent = customText.value; |
| textElement.className = 'text-3xl font-bold p-4 text-center'; |
| |
| |
| switch(selectedTextStyle) { |
| case 'playful': |
| textElement.className += ' text-yellow-500 font-bold'; |
| break; |
| case 'elegant': |
| textElement.className += ' text-blue-500 font-serif italic'; |
| break; |
| case 'bold': |
| textElement.className += ' text-gray-800 font-extrabold'; |
| break; |
| case 'handwritten': |
| textElement.className += ' text-pink-500'; |
| textElement.style.fontFamily = "'Comic Sans MS', cursive"; |
| break; |
| } |
| |
| textOverlay.appendChild(textElement); |
| } |
| }, 3000); |
| }); |
| |
| |
| downloadBtn.addEventListener('click', () => { |
| |
| alert('Download functionality would be implemented here'); |
| }); |
| shareBtn.addEventListener('click', () => { |
| |
| alert('Share functionality would be implemented here'); |
| }); |
| newTransformationBtn.addEventListener('click', () => { |
| step4.classList.add('hidden'); |
| step1.classList.remove('hidden'); |
| previewContainer.classList.add('hidden'); |
| fileInput.value = ''; |
| selectedFile = null; |
| selectedTheme = null; |
| selectedTextStyle = 'playful'; |
| customText.value = ''; |
| charCount.textContent = '0'; |
| nextToStep2Btn.disabled = true; |
| nextToStep3Btn.disabled = true; |
| themeCards.forEach(c => c.classList.remove('selected')); |
| textStyleBtns.forEach(b => b.classList.remove('bg-gray-100')); |
| textStyleBtns[0].classList.add('bg-gray-100'); |
| }); |
| |
| |
| function handleFileUpload(file) { |
| if (!file.type.match('image.*')) { |
| alert('Please upload an image file'); |
| return; |
| } |
| |
| selectedFile = file; |
| |
| const reader = new FileReader(); |
| reader.onload = (e) => { |
| previewImage.src = e.target.result; |
| previewContainer.classList.remove('hidden'); |
| nextToStep2Btn.disabled = false; |
| }; |
| reader.readAsDataURL(file); |
| } |
| </script> |
| </body> |
| </html> |
|
|