Spaces:
Running
Running
| <html lang="it"> | |
| <head> | |
| <meta charset="UTF-8" /> | |
| <!-- Viewport ottimizzato per mobile: blocco totale dello zoom e gestione notch --> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover" /> | |
| <title>AR Neural Link | Vertical Native</title> | |
| <!-- Tailwind & Fonts --> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <link href="https://fonts.googleapis.com/css2?family=Fira+Code:wght@400;700&display=swap" rel="stylesheet"> | |
| <!-- AR Libraries --> | |
| <script src="https://aframe.io/releases/1.4.2/aframe.min.js"></script> | |
| <script src="https://cdn.jsdelivr.net/npm/mind-ar@1.2.2/dist/mindar-image.prod.js"></script> | |
| <script src="https://cdn.jsdelivr.net/npm/mind-ar@1.2.2/dist/mindar-image-aframe.prod.js"></script> | |
| <style> | |
| :root { | |
| --neon-green: #00ff41; | |
| --dark-bg: #050505; | |
| --error-red: #ff0055; | |
| } | |
| /* Reset totale per comportamento "App Nativa" */ | |
| html, body { | |
| margin: 0; | |
| padding: 0; | |
| width: 100%; | |
| height: 100%; | |
| background-color: transparent ; /* Importante per vedere la camera */ | |
| font-family: 'Fira Code', monospace; | |
| overflow: hidden; /* Blocca scroll elastico su iOS */ | |
| position: fixed; /* Blocca il body in posizione */ | |
| } | |
| /* --- ENGINE GRAFICO (Camera e Canvas) --- */ | |
| /* 1. Video Webcam: Forziamo il riempimento verticale */ | |
| video { | |
| position: absolute ; | |
| top: 0; left: 0; right: 0; bottom: 0; | |
| width: 100% ; | |
| height: 100% ; | |
| object-fit: cover ; /* Questo è il segreto per il full screen 9:16 */ | |
| z-index: -2 ; | |
| } | |
| /* 2. Canvas AR: Sovrapposto perfettamente */ | |
| .a-canvas { | |
| width: 100% ; | |
| height: 100% ; | |
| position: absolute ; | |
| top: 0; left: 0; | |
| z-index: -1 ; | |
| } | |
| /* FX: Scanlines (Ottimizzate per mobile) */ | |
| .scanlines { | |
| background: linear-gradient(to bottom, rgba(255,255,255,0), rgba(255,255,255,0) 50%, rgba(0,0,0,0.2) 50%, rgba(0,0,0,0.2)); | |
| background-size: 100% 4px; | |
| position: fixed; inset: 0; pointer-events: none; | |
| z-index: 10; | |
| opacity: 0.4; | |
| } | |
| /* UI Elements */ | |
| .tech-border { | |
| border: 1px solid var(--neon-green); | |
| box-shadow: 0 0 15px rgba(0, 255, 65, 0.15); | |
| background: rgba(0,0,0,0.85); | |
| backdrop-filter: blur(8px); | |
| } | |
| .a-enter-vr-button { display: none ; } | |
| /* --- LOADER STYLE --- */ | |
| #custom-loader { | |
| position: fixed; inset: 0; z-index: 9999; | |
| background: #000; | |
| display: flex; flex-direction: column; align-items: center; justify-content: center; | |
| transition: opacity 0.5s ease-out; | |
| padding: 20px; | |
| } | |
| .glitch-text { animation: glitch 2s infinite; } | |
| @keyframes glitch { | |
| 0% { transform: translate(0); } | |
| 20% { transform: translate(-2px, 2px); } | |
| 40% { transform: translate(-2px, -2px); } | |
| 60% { transform: translate(2px, 2px); } | |
| 80% { transform: translate(2px, -2px); } | |
| 100% { transform: translate(0); } | |
| } | |
| .loader-track { | |
| width: 100%; max-width: 280px; height: 6px; | |
| background: #111; margin-top: 20px; | |
| overflow: hidden; border-radius: 4px; | |
| } | |
| .loader-fill { | |
| height: 100%; background: var(--neon-green); width: 0%; | |
| transition: width 0.3s; | |
| box-shadow: 0 0 10px var(--neon-green); | |
| } | |
| /* Safe area per iPhone nuovi */ | |
| .safe-area-y { | |
| padding-top: env(safe-area-inset-top, 20px); | |
| padding-bottom: env(safe-area-inset-bottom, 20px); | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <!-- 1. LOADER --> | |
| <div id="custom-loader"> | |
| <div class="text-[#00ff41] text-5xl font-black tracking-tighter glitch-text mb-4"> | |
| LINK<br>START | |
| </div> | |
| <div class="text-white/50 text-xs font-mono uppercase tracking-[0.2em] mb-2">Initializing AR Core...</div> | |
| <div class="loader-track"><div class="loader-fill" id="progress-bar"></div></div> | |
| <div id="loading-percent" class="text-[#00ff41] font-mono text-xl mt-2 font-bold">0%</div> | |
| </div> | |
| <!-- 2. FX --> | |
| <div class="scanlines"></div> | |
| <!-- 3. UI LAYER (Vertical Native) --> | |
| <div id="ui-layer" class="fixed inset-0 z-[50] flex flex-col justify-between safe-area-y pointer-events-none"> | |
| <!-- TOP HEADER --> | |
| <div class="w-full px-6 py-4 flex justify-between items-center bg-gradient-to-b from-black/80 to-transparent pointer-events-auto"> | |
| <div class="flex flex-col"> | |
| <span class="text-[10px] text-white/60 font-mono tracking-widest">SYSTEM</span> | |
| <span id="sys-status" class="text-[#00ff41] font-bold text-lg tracking-wider animate-pulse">SCANNING</span> | |
| </div> | |
| <a href="./index.html" class="w-10 h-10 flex items-center justify-center border border-[#00ff41]/50 rounded-full bg-black/40 backdrop-blur-md active:scale-95 transition-transform"> | |
| <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="#00ff41" stroke-width="2"><path d="M18 6L6 18M6 6l12 12"/></svg> | |
| </a> | |
| </div> | |
| <!-- MIRINO (Adattato per Verticale) --> | |
| <div id="aim-overlay" class="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 flex flex-col items-center justify-center transition-opacity duration-300"> | |
| <!-- Frame Rettangolare Verticale (9:16 approx) --> | |
| <div class="relative w-[65vw] h-[100vw] max-w-[300px] max-h-[500px] border border-white/20 rounded-xl"> | |
| <!-- Angoli Tech --> | |
| <div class="absolute -top-1 -left-1 w-6 h-6 border-t-4 border-l-4 border-[#00ff41]"></div> | |
| <div class="absolute -top-1 -right-1 w-6 h-6 border-t-4 border-r-4 border-[#00ff41]"></div> | |
| <div class="absolute -bottom-1 -left-1 w-6 h-6 border-b-4 border-l-4 border-[#00ff41]"></div> | |
| <div class="absolute -bottom-1 -right-1 w-6 h-6 border-b-4 border-r-4 border-[#00ff41]"></div> | |
| <!-- Mirino centrale --> | |
| <div class="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-full flex items-center justify-center"> | |
| <div class="w-12 h-[1px] bg-[#00ff41]/50"></div> | |
| <div class="h-12 w-[1px] bg-[#00ff41]/50 absolute"></div> | |
| </div> | |
| </div> | |
| <div class="mt-4 bg-black/60 backdrop-blur px-4 py-2 rounded border border-[#00ff41]/30"> | |
| <p class="text-[#00ff41] text-xs font-bold tracking-widest uppercase">Inquadra il Marker</p> | |
| </div> | |
| </div> | |
| <!-- BOTTOM CONTROLS --> | |
| <div class="w-full px-6 pb-8 pointer-events-auto flex flex-col items-center bg-gradient-to-t from-black/90 to-transparent"> | |
| <!-- Pulsante Audio Enorme (Pollice friendly) --> | |
| <button id="audio-btn" class="w-full max-w-sm py-5 bg-[#00ff41]/10 border border-[#00ff41] text-[#00ff41] font-bold text-lg uppercase tracking-[0.2em] rounded active:bg-[#00ff41] active:text-black transition-all mb-4 flex items-center justify-center gap-3 backdrop-blur-md"> | |
| <span>🔊</span> | |
| <span id="audio-text">Attiva Audio</span> | |
| </button> | |
| <div class="text-[10px] text-white/40 font-mono">NEURAL_LINK v2.4.0 // VERTICAL_MODE</div> | |
| </div> | |
| </div> | |
| <!-- 4. AR SCENE (VERTICAL SETUP) --> | |
| <!-- Nota: missTolerance aumentato per mantenere il tracking più stabile mentre ci si muove --> | |
| <a-scene | |
| mindar-image="imageTargetSrc: img/targets.mind; filterMinCF:0.0001; filterBeta: 0.001; uiLoading: no; uiScanning: no; missTolerance: 10; warmupTolerance: 5" | |
| color-space="sRGB" | |
| renderer="colorManagement: true, physicallyCorrectLights, highRefreshRate: true" | |
| vr-mode-ui="enabled: false" | |
| device-orientation-permission-ui="enabled: false"> | |
| <a-assets> | |
| <video id="vid" src="./img/video.mp4" preload="auto" loop muted playsinline webkit-playsinline crossorigin="anonymous"></video> | |
| </a-assets> | |
| <a-camera position="0 0 0" look-controls="enabled: false"></a-camera> | |
| <a-entity id="example-target" mindar-image-target="targetIndex: 0"> | |
| <!-- | |
| GEOMETRIA VERTICALE (9:16) | |
| Width: 1 (Largo quanto il target fisico) | |
| Height: 1.777 (Proporzione 16:9 in verticale, cioè 1 * 16 / 9) | |
| Questo forza il video ad apparire come uno schermo smartphone verticale fluttuante. | |
| --> | |
| <a-entity id="myVid" | |
| geometry="primitive: plane; width: 1; height: 1.777" | |
| material="src: #vid; shader: flat; transparent: true; opacity: 1" | |
| position="0 0 0"> | |
| </a-entity> | |
| <!-- Cornice Cyberpunk Verticale --> | |
| <!-- Top Bar --> | |
| <a-plane position="0 0.9 0" width="1.05" height="0.02" color="#00ff41" material="shader: flat"></a-plane> | |
| <!-- Bottom Bar --> | |
| <a-plane position="0 -0.9 0" width="1.05" height="0.02" color="#00ff41" material="shader: flat"></a-plane> | |
| <!-- Left Bar --> | |
| <a-plane position="-0.52 0 0" width="0.02" height="1.8" color="#00ff41" material="shader: flat"></a-plane> | |
| <!-- Right Bar --> | |
| <a-plane position="0.52 0 0" width="0.02" height="1.8" color="#00ff41" material="shader: flat"></a-plane> | |
| <!-- Elementi decorativi agli angoli --> | |
| <a-plane position="-0.52 0.9 0" width="0.1" height="0.02" color="white" material="shader: flat"></a-plane> | |
| <a-plane position="0.52 -0.9 0" width="0.1" height="0.02" color="white" material="shader: flat"></a-plane> | |
| </a-entity> | |
| </a-scene> | |
| <!-- 5. LOGIC --> | |
| <script> | |
| const video = document.querySelector("#vid"); | |
| const target = document.querySelector("#example-target"); | |
| const statusText = document.querySelector("#sys-status"); | |
| const aimOverlay = document.querySelector("#aim-overlay"); | |
| const audioBtn = document.querySelector("#audio-btn"); | |
| const audioText = document.querySelector("#audio-text"); | |
| const loader = document.querySelector("#custom-loader"); | |
| const loadingPercent = document.querySelector("#loading-percent"); | |
| const progressBar = document.querySelector("#progress-bar"); | |
| const scene = document.querySelector("a-scene"); | |
| let isAudioEnabled = false; | |
| let loadProgress = 0; | |
| // Simulazione caricamento rapido | |
| const loadInterval = setInterval(() => { | |
| loadProgress += 2; | |
| if(loadProgress > 100) loadProgress = 100; | |
| loadingPercent.innerText = loadProgress + "%"; | |
| progressBar.style.width = loadProgress + "%"; | |
| if(loadProgress >= 100) clearInterval(loadInterval); | |
| }, 30); | |
| // AR PRONTA | |
| scene.addEventListener("arReady", () => { | |
| console.log("AR System Ready"); | |
| // Chiudi loader | |
| setTimeout(() => { | |
| loader.style.opacity = "0"; | |
| setTimeout(() => loader.style.display = "none", 500); | |
| }, 500); | |
| }); | |
| // ERROR HANDLING | |
| scene.addEventListener("arError", (ev) => { | |
| alert("Errore fotocamera: controlla i permessi!"); | |
| }); | |
| // TARGET FOUND | |
| target.addEventListener("targetFound", () => { | |
| console.log("Found"); | |
| video.play(); | |
| statusText.innerText = "LOCKED"; | |
| statusText.style.color = "#fff"; | |
| statusText.classList.remove("animate-pulse"); | |
| aimOverlay.style.opacity = "0"; | |
| }); | |
| // TARGET LOST | |
| target.addEventListener("targetLost", () => { | |
| console.log("Lost"); | |
| video.pause(); | |
| statusText.innerText = "SEARCHING"; | |
| statusText.style.color = "#00ff41"; | |
| statusText.classList.add("animate-pulse"); | |
| aimOverlay.style.opacity = "1"; | |
| }); | |
| // AUDIO TOGGLE | |
| audioBtn.addEventListener('click', () => { | |
| if (!isAudioEnabled) { | |
| video.muted = false; | |
| isAudioEnabled = true; | |
| audioText.innerText = "AUDIO ON"; | |
| audioBtn.classList.add("bg-[#00ff41]", "text-black"); | |
| audioBtn.classList.remove("bg-[#00ff41]/10", "text-[#00ff41]"); | |
| // Feedback aptico (vibrazione) se supportato | |
| if(navigator.vibrate) navigator.vibrate(50); | |
| setTimeout(() => { | |
| audioBtn.style.opacity = "0"; | |
| audioBtn.style.pointerEvents = "none"; | |
| }, 1500); | |
| } | |
| }); | |
| </script> | |
| </body> | |
| </html> |