faceswap-magic / script.js
yipter's picture
all texts in French
e9f7374 verified
// 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 = `
<div class="spinner"></div>
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 = `
<i data-feather="zap"></i>
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 = `
<div class="flex items-center gap-3">
<i data-feather="${icon}" class="${type === 'success' ? 'text-green-600' : type === 'error' ? 'text-red-600' : type === 'warning' ? 'text-yellow-600' : 'text-blue-600'}"></i>
<span>${message}</span>
</div>
`;
// 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 };
}