// JavaScript principal pour l'application FaceSwap Magic // DOM Elements const originalUpload = document.getElementById('original-upload'); const targetUpload = document.getElementById('target-upload'); const originalPreview = document.getElementById('original-preview'); const targetPreview = document.getElementById('target-preview'); const originalImage = document.getElementById('original-image'); const targetImage = document.getElementById('target-image'); const generateBtn = document.getElementById('generate-btn'); const resultSection = document.getElementById('result-section'); const resultImage = document.getElementById('result-image'); const displayOriginal = document.getElementById('display-original'); const downloadBtn = document.getElementById('download-btn'); const shareBtn = document.getElementById('share-btn'); const tryAgainBtn = document.getElementById('try-again-btn'); // Sample results for demo purposes (using placeholder API) const sampleResults = [ 'http://static.photos/people/640x360/201', 'http://static.photos/people/640x360/202', 'http://static.photos/people/640x360/203', 'http://static.photos/people/640x360/204', 'http://static.photos/people/640x360/205' ]; // State Management let currentState = { originalImage: null, targetImage: null, generatedResult: null, isProcessing: false }; // Initialize Application document.addEventListener('DOMContentLoaded', function() { console.log('FaceSwap Magic initialisé !'); // Set up event listeners setupEventListeners(); // Check for saved state in localStorage loadSavedState(); // Initialize tooltips initializeTooltips(); // Add animation to hero section animateHeroSection(); }); // Set up all event listeners function setupEventListeners() { // File upload handlers originalUpload.addEventListener('change', (e) => handleFileUpload(e, 'original')); targetUpload.addEventListener('change', (e) => handleFileUpload(e, 'target')); // Generate button click generateBtn.addEventListener('click', generateFaceSwap); // Result buttons downloadBtn.addEventListener('click', downloadResult); shareBtn.addEventListener('click', shareResult); tryAgainBtn.addEventListener('click', resetApplication); // Drag and drop functionality setupDragAndDrop(); // Range slider updates setupRangeSliders(); } // Handle file upload function handleFileUpload(event, type) { const file = event.target.files[0]; if (!file) return; if (!file.type.match('image.*')) { showToast('Veuillez télécharger un fichier image', 'error'); return; } if (file.size > 10 * 1024 * 1024) { // 10MB limit showToast('La taille du fichier doit être inférieure à 10Mo', 'error'); return; } const reader = new FileReader(); reader.onload = function(e) { const imageUrl = e.target.result; if (type === 'original') { currentState.originalImage = imageUrl; originalImage.src = imageUrl; originalPreview.classList.remove('hidden'); displayOriginal.src = imageUrl; } else { currentState.targetImage = imageUrl; targetImage.src = imageUrl; targetPreview.classList.remove('hidden'); } // Enable generate button if both images are uploaded if (currentState.originalImage && currentState.targetImage) { generateBtn.disabled = false; generateBtn.classList.remove('opacity-50', 'cursor-not-allowed'); } showToast(`${type === 'original' ? 'Originale' : 'Cible'} image téléchargée avec succès !`, 'success'); // Save to localStorage saveState(); }; reader.readAsDataURL(file); } // Setup drag and drop functionality function setupDragAndDrop() { const uploadAreas = document.querySelectorAll('.border-dashed'); uploadAreas.forEach(area => { area.addEventListener('dragover', (e) => { e.preventDefault(); area.classList.add('border-primary', 'bg-primary/5'); }); area.addEventListener('dragleave', () => { area.classList.remove('border-primary', 'bg-primary/5'); }); area.addEventListener('drop', (e) => { e.preventDefault(); area.classList.remove('border-primary', 'bg-primary/5'); const file = e.dataTransfer.files[0]; if (file && file.type.match('image.*')) { // Simulate file input change const isOriginal = area.closest('.space-y-6').querySelector('h3').textContent.includes('Original'); const input = isOriginal ? originalUpload : targetUpload; // Create a new FileList (simulated) const dataTransfer = new DataTransfer(); dataTransfer.items.add(file); input.files = dataTransfer.files; // Trigger change event input.dispatchEvent(new Event('change')); } }); }); } // Setup range sliders function setupRangeSliders() { const sliders = document.querySelectorAll('input[type="range"]'); sliders.forEach(slider => { slider.addEventListener('input', function() { const value = this.value; const label = this.previousElementSibling; // Update label with percentage if (label && label.classList.contains('text-gray-700')) { const labelText = label.textContent.split(':')[0]; label.textContent = `${labelText}: ${value}%`; } }); }); } // Generate face swap (simulated) function generateFaceSwap() { if (!currentState.originalImage || !currentState.targetImage) { showToast('Veuillez télécharger les deux images d\'abord !', 'error'); return; } // Show loading state currentState.isProcessing = true; generateBtn.disabled = true; generateBtn.innerHTML = `
Traitement en cours... `; // Simulate API call delay setTimeout(() => { // Get random sample result for demo const randomIndex = Math.floor(Math.random() * sampleResults.length); currentState.generatedResult = sampleResults[randomIndex]; resultImage.src = currentState.generatedResult; // Show result section resultSection.classList.remove('hidden'); resultSection.scrollIntoView({ behavior: 'smooth' }); // Reset button generateBtn.disabled = false; generateBtn.innerHTML = ` Générer l'Échange `; feather.replace(); currentState.isProcessing = false; showToast('Échange de visage généré avec succès !', 'success'); // Save state saveState(); }, 3000); } // Download result function downloadResult() { if (!currentState.generatedResult) { showToast('Aucun résultat à télécharger !', 'error'); return; } // In a real app, this would download the actual generated image // For demo, we'll create a download link const link = document.createElement('a'); link.href = currentState.generatedResult; link.download = `faceswap-${Date.now()}.jpg`; document.body.appendChild(link); link.click(); document.body.removeChild(link); showToast('Téléchargement démarré !', 'success'); } // Share result function shareResult() { if (!currentState.generatedResult) { showToast('Aucun résultat à partager !', 'error'); return; } if (navigator.share) { navigator.share({ title: 'My AI FaceSwap Creation', text: 'Check out this awesome face swap I made with FaceSwap Magic!', url: window.location.href }) .then(() => showToast('Partagé avec succès !', 'success')) .catch(() => showToast('Partage annulé', 'info')); } else { // Fallback: copy to clipboard navigator.clipboard.writeText(window.location.href) .then(() => showToast('Lien copié dans le presse-papier !', 'success')) .catch(() => showToast('Échec de la copie du lien', 'error')); } } // Reset application function resetApplication() { // Reset file inputs originalUpload.value = ''; targetUpload.value = ''; // Hide previews originalPreview.classList.add('hidden'); targetPreview.classList.add('hidden'); resultSection.classList.add('hidden'); // Reset images originalImage.src = ''; targetImage.src = ''; resultImage.src = ''; // Reset state currentState = { originalImage: null, targetImage: null, generatedResult: null, isProcessing: false }; // Reset range sliders document.querySelectorAll('input[type="range"]').forEach(slider => { slider.value = slider.id.includes('alignment') ? 50 : slider.id.includes('skin') ? 75 : 60; slider.dispatchEvent(new Event('input')); }); // Scroll to upload section document.getElementById('upload-section').scrollIntoView({ behavior: 'smooth' }); showToast('Prêt pour un nouvel échange de visage !', 'info'); // Clear localStorage localStorage.removeItem('faceswapState'); } // Show toast notification function showToast(message, type = 'info') { // Remove existing toasts document.querySelectorAll('.toast').forEach(toast => toast.remove()); // Create toast element const toast = document.createElement('div'); toast.className = 'toast'; // Set icon based on type let icon = 'info'; let bgColor = 'bg-blue-100'; let borderColor = 'border-blue-500'; if (type === 'success') { icon = 'check-circle'; bgColor = 'bg-green-100'; borderColor = 'border-green-500'; } else if (type === 'error') { icon = 'alert-circle'; bgColor = 'bg-red-100'; borderColor = 'border-red-500'; } else if (type === 'warning') { icon = 'alert-triangle'; bgColor = 'bg-yellow-100'; borderColor = 'border-yellow-500'; } toast.innerHTML = `
${message}
`; // Add classes toast.classList.add(bgColor, borderColor, 'border-l-4'); // Add to DOM document.body.appendChild(toast); // Show toast setTimeout(() => toast.classList.add('show'), 10); // Update feather icons feather.replace(); // Remove toast after 5 seconds setTimeout(() => { toast.classList.remove('show'); setTimeout(() => toast.remove(), 300); }, 5000); } // Save state to localStorage function saveState() { const stateToSave = { originalImage: currentState.originalImage, targetImage: currentState.targetImage, generatedResult: currentState.generatedResult }; localStorage.setItem('faceswapState', JSON.stringify(stateToSave)); } // Load saved state from localStorage function loadSavedState() { try { const saved = localStorage.getItem('faceswapState'); if (saved) { const state = JSON.parse(saved); if (state.originalImage) { currentState.originalImage = state.originalImage; originalImage.src = state.originalImage; originalPreview.classList.remove('hidden'); displayOriginal.src = state.originalImage; } if (state.targetImage) { currentState.targetImage = state.targetImage; targetImage.src = state.targetImage; targetPreview.classList.remove('hidden'); } if (state.generatedResult) { currentState.generatedResult = state.generatedResult; resultImage.src = state.generatedResult; resultSection.classList.remove('hidden'); } if (currentState.originalImage && currentState.targetImage) { generateBtn.disabled = false; generateBtn.classList.remove('opacity-50', 'cursor-not-allowed'); } } } catch (error) { console.error('Error loading saved state:', error); localStorage.removeItem('faceswapState'); } } // Initialize tooltips function initializeTooltips() { // Add tooltips to range sliders const sliders = document.querySelectorAll('input[type="range"]'); sliders.forEach(slider => { slider.addEventListener('mouseenter', function() { const tooltip = document.createElement('div'); tooltip.className = 'absolute -top-8 bg-dark text-white px-2 py-1 rounded text-xs'; tooltip.textContent = `${this.value}%`; tooltip.style.left = `${(this.value / 100) * this.offsetWidth - 15}px`; this.parentElement.style.position = 'relative'; this.parentElement.appendChild(tooltip); this.addEventListener('mousemove', updateTooltip); this.addEventListener('mouseleave', () => tooltip.remove()); function updateTooltip() { tooltip.textContent = `${this.value}%`; tooltip.style.left = `${(this.value / 100) * this.offsetWidth - 15}px`; } }); }); } // Animate hero section elements function animateHeroSection() { const heroTitle = document.querySelector('h1'); const heroText = document.querySelector('section.mb-16 p'); const heroButtons = document.querySelector('section.mb-16 .flex'); if (heroTitle) heroTitle.classList.add('animate-fadeInUp'); if (heroText) { heroText.style.animationDelay = '0.2s'; heroText.classList.add('animate-fadeInUp'); } if (heroButtons) { heroButtons.style.animationDelay = '0.4s'; heroButtons.classList.add('animate-fadeInUp'); } } // API Integration (placeholder for real implementation) class FaceSwapAPI { static async generateFaceSwap(originalImage, targetImage, options = {}) { // In a real implementation, this would call your backend API // For demo, we return a mock response return new Promise((resolve) => { setTimeout(() => { resolve({ success: true, resultUrl: sampleResults[Math.floor(Math.random() * sampleResults.length)], processingTime: '2.8s', confidenceScore: Math.random() * 30 + 70 // 70-100% }); }, 3000); }); } static async getGalleryImages(page = 1, limit = 9) { // Mock gallery API call const images = []; for (let i = 0; i < limit; i++) { images.push({ id: i + 1, url: `http://static.photos/people/640x360/${200 + i}`, title: `FaceSwap Example ${i + 1}`, author: `User${Math.floor(Math.random() * 1000)}`, likes: Math.floor(Math.random() * 1000) }); } return new Promise((resolve) => { setTimeout(() => resolve(images), 500); }); } } // Export for potential module usage if (typeof module !== 'undefined' && module.exports) { module.exports = { FaceSwapAPI }; }