Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Video Comparison Slider</title> | |
| <link href="https://fonts.googleapis.com/css2?family=Saira:wght@400;500;600;700&display=swap" rel="stylesheet"> | |
| <style> | |
| :root { | |
| --container-width: 80vw; | |
| --primary-color: rgb(0, 80, 150); | |
| --transition-speed: 0.3s; | |
| } | |
| * { | |
| margin: 0; | |
| padding: 0; | |
| box-sizing: border-box; | |
| } | |
| body { | |
| display: flex; | |
| flex-direction: column; | |
| justify-content: center; | |
| align-items: center; | |
| min-height: 100vh; | |
| background: black; | |
| color: white; | |
| font-family: 'Saira', sans-serif; | |
| } | |
| /* Navigation */ | |
| .tabs { | |
| display: flex; | |
| gap: 10px; | |
| margin-bottom: 20px; | |
| } | |
| .tab { | |
| padding: 10px 20px; | |
| background: rgba(255, 255, 255, 0.1); | |
| border-radius: 5px; | |
| cursor: pointer; | |
| transition: background var(--transition-speed); | |
| font-weight: 500; | |
| letter-spacing: 0.5px; | |
| } | |
| .tab:hover { | |
| background: rgba(255, 255, 255, 0.2); | |
| } | |
| .tab.active { | |
| background: var(--primary-color); | |
| } | |
| /* Video Container */ | |
| .video-container { | |
| position: relative; | |
| width: var(--container-width); | |
| height: calc(var(--container-width) * 9 / 16); | |
| overflow: hidden; | |
| } | |
| video { | |
| position: absolute; | |
| inset: 0; | |
| width: 100%; | |
| height: 100%; | |
| object-fit: cover; | |
| } | |
| .second-video { | |
| clip-path: inset(0 50% 0 0); | |
| } | |
| /* Slider */ | |
| .slider { | |
| position: absolute; | |
| top: 0; | |
| left: 30%; | |
| width: 2px; | |
| height: 100%; | |
| background: var(--primary-color); | |
| cursor: ew-resize; | |
| z-index: 10; | |
| animation: slide-demo 5s ease-in-out forwards; | |
| border-radius: 4px; | |
| box-shadow: 0 0 10px rgba(0, 0, 0, 0.2); | |
| } | |
| .slider::before, | |
| .slider::after { | |
| content: ''; | |
| position: absolute; | |
| top: 80%; | |
| transform: translateY(-50%); | |
| width: 30px; | |
| height: 30px; | |
| background-size: contain; | |
| background-repeat: no-repeat; | |
| transition: all var(--transition-speed); | |
| opacity: 0; | |
| } | |
| .slider::before { | |
| left: -27px; | |
| background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="rgb(0, 80, 150)"><polygon points="14,7 8,12 14,17"/></svg>'); | |
| } | |
| .slider::after { | |
| right: -27px; | |
| background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="rgb(0, 80, 150)"><polygon points="10,7 16,12 10,17"/></svg>'); | |
| } | |
| .slider.ready::before, | |
| .slider.ready::after { | |
| opacity: 0.7; | |
| } | |
| .slider.ready:hover::before, | |
| .slider.ready:hover::after { | |
| opacity: 1; | |
| transform: translateY(-50%) scale(1.2); | |
| } | |
| /* Watermark */ | |
| .watermark { | |
| position: absolute; | |
| top: 4%; | |
| right: 1%; | |
| z-index: 15; | |
| opacity: 0; | |
| width: 12%; | |
| pointer-events: none; | |
| transition: opacity var(--transition-speed) ease; | |
| } | |
| .watermark img { | |
| width: 100%; | |
| height: auto; | |
| filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.3)); | |
| } | |
| /* Animation */ | |
| @keyframes slide-demo { | |
| 0% { | |
| left: 100%; | |
| } | |
| 10% { | |
| left: 80%; | |
| } | |
| 20% { | |
| left: 85%; | |
| } | |
| 50% { | |
| left: 20%; | |
| } | |
| 70% { | |
| left: 60%; | |
| } | |
| 100% { | |
| left: 30%; | |
| } | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <nav class="tabs" id="tabs"> | |
| <div class="tab" data-page="SAILING_BOAT">Sailing Boat</div> | |
| <div class="tab" data-page="MOB_NIGHT">MOB Night</div> | |
| <div class="tab" data-page="BUOYS">Buoys</div> | |
| </nav> | |
| <main class="video-container" id="videoContainer"> | |
| <video id="video1" playsinline autoplay loop muted></video> | |
| <video id="video2" class="second-video" playsinline autoplay loop muted></video> | |
| <div class="slider" id="slider"></div> | |
| <div class="watermark"> | |
| <img src="Logo-SEA-AI.png" alt="SEA.AI Logo"> | |
| </div> | |
| </main> | |
| <script> | |
| // DOM Elements | |
| const elements = { | |
| slider: document.getElementById('slider'), | |
| container: document.getElementById('videoContainer'), | |
| video1: document.getElementById('video1'), | |
| video2: document.getElementById('video2'), | |
| watermark: document.querySelector('.watermark') | |
| }; | |
| // State | |
| let isDragging = false; | |
| const currentPage = new URLSearchParams(window.location.search).get('page') || 'SAILING_BOAT'; | |
| // Video Management | |
| function updateVideos(page) { | |
| elements.video1.src = `${page}/SEA-AI-Brain.mp4`; | |
| elements.video2.src = `${page}/FLIR.mp4`; | |
| // Update URL and active tab | |
| const url = new URL(window.location); | |
| url.searchParams.set('page', page); | |
| window.history.pushState({}, '', url); | |
| document.querySelectorAll('.tab').forEach(tab => { | |
| tab.classList.toggle('active', tab.dataset.page === page); | |
| }); | |
| } | |
| // Slider Logic | |
| function handleSlide(positionX) { | |
| const rect = elements.container.getBoundingClientRect(); | |
| const sliderPosition = Math.max(0, Math.min(positionX - rect.left, rect.width)); | |
| elements.slider.style.left = `${sliderPosition}px`; | |
| elements.video2.style.clipPath = `inset(0 ${rect.width - sliderPosition}px 0 0)`; | |
| // Update watermark visibility | |
| const watermarkLeft = elements.watermark.getBoundingClientRect().left - rect.left; | |
| elements.watermark.style.opacity = sliderPosition > watermarkLeft ? '0' : '0.9'; | |
| } | |
| function updateClipPath() { | |
| const rect = elements.container.getBoundingClientRect(); | |
| const sliderPosition = parseFloat(window.getComputedStyle(elements.slider).left); | |
| elements.video2.style.clipPath = `inset(0 ${rect.width - sliderPosition}px 0 0)`; | |
| // Update watermark during animation | |
| const watermarkLeft = elements.watermark.getBoundingClientRect().left - rect.left; | |
| elements.watermark.style.opacity = sliderPosition > watermarkLeft ? '0' : '0.9'; | |
| requestAnimationFrame(updateClipPath); | |
| } | |
| // Event Listeners | |
| document.querySelectorAll('.tab').forEach(tab => { | |
| tab.addEventListener('click', () => updateVideos(tab.dataset.page)); | |
| }); | |
| // Mouse events | |
| elements.slider.addEventListener('mousedown', () => isDragging = true); | |
| window.addEventListener('mouseup', () => isDragging = false); | |
| window.addEventListener('mousemove', e => isDragging && handleSlide(e.clientX)); | |
| // Touch events | |
| elements.slider.addEventListener('touchstart', () => isDragging = true); | |
| window.addEventListener('touchend', () => isDragging = false); | |
| window.addEventListener('touchmove', e => isDragging && handleSlide(e.touches[0].clientX)); | |
| // Animation end | |
| elements.slider.addEventListener('animationend', () => { | |
| elements.slider.classList.add('ready'); | |
| elements.slider.style.animation = 'none'; | |
| }); | |
| // Initialize | |
| updateVideos(currentPage); | |
| window.addEventListener('load', updateClipPath); | |
| </script> | |
| </body> | |
| </html> |