Spaces:
Running
Running
| <html lang="fr"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Assistante Visuelle Corporelle</title> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> | |
| <style> | |
| .body-map { | |
| position: relative; | |
| background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 400"><circle cx="100" cy="80" r="40" fill="none" stroke="gray" stroke-width="1"/><path d="M100 120 L100 300" fill="none" stroke="gray" stroke-width="1"/><path d="M100 150 L60 200" fill="none" stroke="gray" stroke-width="1"/><path d="M100 150 L140 200" fill="none" stroke="gray" stroke-width="1"/><path d="M100 300 L60 380" fill="none" stroke="gray" stroke-width="1"/><path d="M100 300 L140 380" fill="none" stroke="gray" stroke-width="1"/></svg>'); | |
| background-repeat: no-repeat; | |
| background-position: center; | |
| background-size: contain; | |
| } | |
| .target-area { | |
| position: absolute; | |
| width: 40px; | |
| height: 40px; | |
| border-radius: 50%; | |
| border: 2px dashed #3B82F6; | |
| background-color: rgba(59, 130, 246, 0.2); | |
| transform: translate(-50%, -50%); | |
| pointer-events: none; | |
| } | |
| .body-zone { | |
| position: absolute; | |
| width: 30px; | |
| height: 30px; | |
| border-radius: 50%; | |
| background-color: rgba(239, 68, 68, 0.3); | |
| transform: translate(-50%, -50%); | |
| cursor: pointer; | |
| transition: all 0.2s; | |
| } | |
| .body-zone:hover { | |
| background-color: rgba(239, 68, 68, 0.6); | |
| } | |
| @media (max-width: 640px) { | |
| .body-map { | |
| height: 70vh; | |
| } | |
| } | |
| </style> | |
| </head> | |
| <body class="bg-gray-100 min-h-screen"> | |
| <div class="container mx-auto px-4 py-8"> | |
| <div class="bg-white rounded-xl shadow-lg overflow-hidden max-w-md mx-auto"> | |
| <!-- Header --> | |
| <div class="bg-blue-600 text-white p-4 flex items-center justify-between"> | |
| <div class="flex items-center space-x-3"> | |
| <i class="fas fa-crosshairs text-xl"></i> | |
| <h1 class="text-xl font-bold">Assistante Visuelle</h1> | |
| </div> | |
| <button id="help-btn" class="text-white hover:text-blue-200"> | |
| <i class="fas fa-question-circle text-xl"></i> | |
| </button> | |
| </div> | |
| <!-- Main Content --> | |
| <div class="p-4"> | |
| <div class="mb-4"> | |
| <h2 class="text-lg font-semibold text-gray-800">Zone cible sélectionnée:</h2> | |
| <div id="selected-area" class="mt-2 p-3 bg-blue-50 rounded-lg text-blue-800 font-medium"> | |
| Aucune zone sélectionnée | |
| </div> | |
| </div> | |
| <div class="mb-6"> | |
| <h2 class="text-lg font-semibold text-gray-800 mb-2">Carte du corps:</h2> | |
| <div class="body-map w-full h-96 relative" id="body-map"> | |
| <!-- Body zones will be added here --> | |
| <div class="target-area hidden" id="target-area"></div> | |
| </div> | |
| </div> | |
| <div class="grid grid-cols-2 gap-4 mb-4"> | |
| <button id="lock-btn" class="bg-blue-600 hover:bg-blue-700 text-white py-3 px-4 rounded-lg font-medium flex items-center justify-center space-x-2"> | |
| <i class="fas fa-lock"></i> | |
| <span>Verrouiller</span> | |
| </button> | |
| <button id="reset-btn" class="bg-gray-200 hover:bg-gray-300 text-gray-800 py-3 px-4 rounded-lg font-medium flex items-center justify-center space-x-2"> | |
| <i class="fas fa-redo"></i> | |
| <span>Réinitialiser</span> | |
| </button> | |
| </div> | |
| <div class="bg-yellow-50 border-l-4 border-yellow-400 p-4 mb-4"> | |
| <div class="flex"> | |
| <div class="flex-shrink-0"> | |
| <i class="fas fa-info-circle text-yellow-400"></i> | |
| </div> | |
| <div class="ml-3"> | |
| <p class="text-sm text-yellow-700"> | |
| L'assistante utilise les capteurs de votre téléphone pour maintenir le viseur sur la zone sélectionnée. | |
| </p> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Help Modal --> | |
| <div id="help-modal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center hidden z-50"> | |
| <div class="bg-white rounded-lg max-w-md w-full mx-4"> | |
| <div class="bg-blue-600 text-white p-4 flex items-center justify-between"> | |
| <h3 class="text-lg font-semibold">Aide</h3> | |
| <button id="close-help" class="text-white hover:text-blue-200"> | |
| <i class="fas fa-times"></i> | |
| </button> | |
| </div> | |
| <div class="p-4"> | |
| <div class="mb-4"> | |
| <h4 class="font-medium text-gray-800 mb-2">Comment utiliser:</h4> | |
| <ol class="list-decimal pl-5 space-y-2 text-gray-700"> | |
| <li>Appuyez sur une zone du corps pour la sélectionner</li> | |
| <li>Cliquez sur "Verrouiller" pour activer le suivi</li> | |
| <li>L'assistante maintiendra le viseur sur cette zone</li> | |
| <li>Utilisez "Réinitialiser" pour choisir une nouvelle zone</li> | |
| </ol> | |
| </div> | |
| <div class="bg-blue-50 p-3 rounded-lg"> | |
| <h4 class="font-medium text-blue-800 mb-1">Astuce:</h4> | |
| <p class="text-blue-700 text-sm">Maintenez votre téléphone stable pour des résultats optimaux.</p> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <script> | |
| document.addEventListener('DOMContentLoaded', function() { | |
| // Body zones positions (x%, y%) | |
| const bodyZones = [ | |
| { name: "Tête", x: 50, y: 10 }, | |
| { name: "Épaule gauche", x: 30, y: 25 }, | |
| { name: "Épaule droite", x: 70, y: 25 }, | |
| { name: "Poitrine", x: 50, y: 35 }, | |
| { name: "Ventre", x: 50, y: 45 }, | |
| { name: "Main gauche", x: 20, y: 50 }, | |
| { name: "Main droite", x: 80, y: 50 }, | |
| { name: "Hanche gauche", x: 40, y: 55 }, | |
| { name: "Hanche droite", x: 60, y: 55 }, | |
| { name: "Genou gauche", x: 45, y: 70 }, | |
| { name: "Genou droit", x: 55, y: 70 }, | |
| { name: "Pied gauche", x: 45, y: 90 }, | |
| { name: "Pied droit", x: 55, y: 90 } | |
| ]; | |
| const bodyMap = document.getElementById('body-map'); | |
| const targetArea = document.getElementById('target-area'); | |
| const selectedAreaDisplay = document.getElementById('selected-area'); | |
| const lockBtn = document.getElementById('lock-btn'); | |
| const resetBtn = document.getElementById('reset-btn'); | |
| const helpBtn = document.getElementById('help-btn'); | |
| const helpModal = document.getElementById('help-modal'); | |
| const closeHelp = document.getElementById('close-help'); | |
| let selectedZone = null; | |
| let isLocked = false; | |
| let deviceOrientationHandler = null; | |
| // Create body zones | |
| bodyZones.forEach(zone => { | |
| const zoneElement = document.createElement('div'); | |
| zoneElement.className = 'body-zone'; | |
| zoneElement.style.left = `${zone.x}%`; | |
| zoneElement.style.top = `${zone.y}%`; | |
| zoneElement.dataset.name = zone.name; | |
| zoneElement.dataset.x = zone.x; | |
| zoneElement.dataset.y = zone.y; | |
| zoneElement.addEventListener('click', function() { | |
| if (isLocked) return; | |
| // Remove previous selection | |
| document.querySelectorAll('.body-zone').forEach(el => { | |
| el.style.backgroundColor = 'rgba(239, 68, 68, 0.3)'; | |
| }); | |
| // Highlight selected zone | |
| this.style.backgroundColor = 'rgba(239, 68, 68, 0.8)'; | |
| selectedZone = { | |
| name: this.dataset.name, | |
| x: parseFloat(this.dataset.x), | |
| y: parseFloat(this.dataset.y) | |
| }; | |
| // Show target area | |
| targetArea.style.left = `${selectedZone.x}%`; | |
| targetArea.style.top = `${selectedZone.y}%`; | |
| targetArea.classList.remove('hidden'); | |
| // Update display | |
| selectedAreaDisplay.textContent = selectedZone.name; | |
| }); | |
| bodyMap.appendChild(zoneElement); | |
| }); | |
| // Lock button | |
| lockBtn.addEventListener('click', function() { | |
| if (!selectedZone) { | |
| alert('Veuillez sélectionner une zone d\'abord'); | |
| return; | |
| } | |
| isLocked = !isLocked; | |
| if (isLocked) { | |
| this.innerHTML = '<i class="fas fa-unlock"></i><span>Déverrouiller</span>'; | |
| this.classList.remove('bg-blue-600', 'hover:bg-blue-700'); | |
| this.classList.add('bg-green-600', 'hover:bg-green-700'); | |
| // Simulate device orientation tracking | |
| startTracking(); | |
| // Vibrate for feedback | |
| if (navigator.vibrate) { | |
| navigator.vibrate(100); | |
| } | |
| } else { | |
| this.innerHTML = '<i class="fas fa-lock"></i><span>Verrouiller</span>'; | |
| this.classList.remove('bg-green-600', 'hover:bg-green-700'); | |
| this.classList.add('bg-blue-600', 'hover:bg-blue-700'); | |
| // Stop tracking | |
| stopTracking(); | |
| } | |
| }); | |
| // Reset button | |
| resetBtn.addEventListener('click', function() { | |
| selectedZone = null; | |
| isLocked = false; | |
| // Reset UI | |
| targetArea.classList.add('hidden'); | |
| selectedAreaDisplay.textContent = 'Aucune zone sélectionnée'; | |
| document.querySelectorAll('.body-zone').forEach(el => { | |
| el.style.backgroundColor = 'rgba(239, 68, 68, 0.3)'; | |
| }); | |
| lockBtn.innerHTML = '<i class="fas fa-lock"></i><span>Verrouiller</span>'; | |
| lockBtn.classList.remove('bg-green-600', 'hover:bg-green-700'); | |
| lockBtn.classList.add('bg-blue-600', 'hover:bg-blue-700'); | |
| // Stop tracking | |
| stopTracking(); | |
| }); | |
| // Help modal | |
| helpBtn.addEventListener('click', function() { | |
| helpModal.classList.remove('hidden'); | |
| }); | |
| closeHelp.addEventListener('click', function() { | |
| helpModal.classList.add('hidden'); | |
| }); | |
| helpModal.addEventListener('click', function(e) { | |
| if (e.target === helpModal) { | |
| helpModal.classList.add('hidden'); | |
| } | |
| }); | |
| // Device orientation tracking (simulated) | |
| function startTracking() { | |
| // In a real app, you would use DeviceOrientationEvent | |
| // This is a simulation for demonstration | |
| let angle = 0; | |
| deviceOrientationHandler = setInterval(() => { | |
| // Simulate small movements | |
| angle += 0.05; | |
| const offsetX = Math.sin(angle) * 2; // 2% movement | |
| const offsetY = Math.cos(angle) * 1; // 1% movement | |
| targetArea.style.left = `${selectedZone.x + offsetX}%`; | |
| targetArea.style.top = `${selectedZone.y + offsetY}%`; | |
| }, 100); | |
| } | |
| function stopTracking() { | |
| if (deviceOrientationHandler) { | |
| clearInterval(deviceOrientationHandler); | |
| deviceOrientationHandler = null; | |
| // Reset target position | |
| if (selectedZone) { | |
| targetArea.style.left = `${selectedZone.x}%`; | |
| targetArea.style.top = `${selectedZone.y}%`; | |
| } | |
| } | |
| } | |
| // Request permission for device orientation (in a real app) | |
| function requestPermission() { | |
| if (typeof DeviceOrientationEvent !== 'undefined' && | |
| typeof DeviceOrientationEvent.requestPermission === 'function') { | |
| DeviceOrientationEvent.requestPermission() | |
| .then(response => { | |
| if (response === 'granted') { | |
| window.addEventListener('deviceorientation', handleOrientation); | |
| } | |
| }) | |
| .catch(console.error); | |
| } | |
| } | |
| // Handle real device orientation (not implemented in this demo) | |
| function handleOrientation(event) { | |
| // This would be implemented in a real app | |
| // Using beta (front-to-back) and gamma (left-to-right) angles | |
| // to adjust the target position | |
| } | |
| }); | |
| </script> | |
| <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=SulfuceNoir/ff-cheat" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> | |
| </html> |