| <!DOCTYPE html> |
| <html lang="zh-CN"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"> |
| <title>Girls</title> |
| <style> |
| html, body { height: 100%; margin: 0; padding: 0; background: #111; color: #fff; } |
| body { |
| min-height: 100vh; |
| display: flex; |
| flex-direction: column; |
| align-items: center; |
| justify-content: center; |
| font-family: 'PingFang SC', 'Microsoft YaHei', Arial, sans-serif; |
| background: linear-gradient(135deg, #181c24 0%, #232526 60%, #23243a 100%); |
| } |
| #player-box { |
| background: none; |
| padding: 0; |
| border-radius: 0; |
| box-shadow: none; |
| display: flex; |
| flex-direction: column; |
| align-items: center; |
| width: 100vw; |
| max-width: 100vw; |
| } |
| .video-wrap { |
| position: relative; |
| width: 100vw; |
| max-width: 420px; |
| aspect-ratio: 9/16; |
| background: |
| radial-gradient(ellipse at 60% 20%, rgba(60,70,110,0.18) 0%, rgba(30,30,40,0.12) 60%, rgba(20,20,20,0.92) 100%), |
| linear-gradient(135deg, rgba(40,44,60,0.85) 0%, rgba(30,30,40,0.92) 100%); |
| border-radius: 28px; |
| box-shadow: |
| 0 8px 32px 0 rgba(31, 38, 135, 0.18), |
| 0 2px 8px 0 rgba(0,0,0,0.18), |
| 0 0 0 4px rgba(80,120,255,0.08) inset, |
| 0 1.5px 8px 0 rgba(0,0,0,0.10); |
| overflow: hidden; |
| display: flex; |
| align-items: center; |
| justify-content: center; |
| backdrop-filter: blur(3.5px) saturate(1.15); |
| } |
| .video-wrap::before { |
| content: ''; |
| position: absolute; |
| inset: 0; |
| border-radius: 28px; |
| pointer-events: none; |
| box-shadow: |
| 0 0 32px 8px rgba(255,255,255,0.08) inset, |
| 0 0 0 2px rgba(255,255,255,0.10) inset; |
| } |
| .video-fade { |
| opacity: 0; |
| transition: opacity 0.4s; |
| } |
| .video-fade-in { |
| opacity: 1 !important; |
| transition: opacity 0.4s; |
| } |
| .loading-spinner { |
| position: absolute; |
| left: 50%; |
| top: 50%; |
| transform: translate(-50%, -50%); |
| width: 48px; |
| height: 48px; |
| border: 4px solid rgba(255,255,255,0.18); |
| border-top: 4px solid #fff; |
| border-radius: 50%; |
| animation: spin 1s linear infinite; |
| z-index: 20; |
| background: none; |
| pointer-events: none; |
| } |
| @keyframes spin { |
| 0% { transform: translate(-50%, -50%) rotate(0deg); } |
| 100% { transform: translate(-50%, -50%) rotate(360deg); } |
| } |
| .load-fail-tip { |
| position: absolute; |
| left: 50%; |
| top: 60%; |
| transform: translate(-50%, -50%); |
| background: rgba(30,30,40,0.92); |
| color: #fff; |
| padding: 12px 24px; |
| border-radius: 14px; |
| font-size: 15px; |
| z-index: 21; |
| box-shadow: 0 2px 8px rgba(0,0,0,0.12); |
| display: none; |
| } |
| video { |
| width: 100%; |
| height: 100%; |
| border-radius: 20px; |
| background: #111; |
| display: block; |
| object-fit: cover; |
| box-shadow: 0 2px 12px 0 rgba(0,0,0,0.10); |
| transition: box-shadow 0.2s; |
| } |
| .controls { |
| position: absolute; |
| bottom: 60px; |
| left: 0; |
| width: 100%; |
| display: flex; |
| flex-direction: row; |
| justify-content: center; |
| gap: 8px; |
| padding: 0; |
| background: none; |
| box-sizing: border-box; |
| opacity: 0; |
| pointer-events: none; |
| transition: opacity 0.3s, transform 0.3s; |
| z-index: 10; |
| transform: translateY(20px); |
| } |
| .info { |
| position: absolute; |
| top: 10px; |
| left: 14px; |
| color: #fff; |
| font-size: 13px; |
| background: rgba(0,0,0,0.05); |
| padding: 2px 10px; |
| border-radius: 12px; |
| z-index: 2; |
| pointer-events: none; |
| opacity: 0; |
| transition: opacity 0.3s, transform 0.3s; |
| transform: translateY(-10px); |
| } |
| .video-wrap:hover .controls { |
| opacity: 1; |
| pointer-events: auto; |
| transform: translateY(0); |
| } |
| .video-wrap:hover .info { |
| opacity: 1; |
| transform: translateY(0); |
| } |
| @media (max-width: 600px) { |
| html, body { |
| width: 100vw; |
| height: 100vh; |
| min-height: 100vh; |
| margin: 0; |
| padding: 0; |
| overflow: hidden; |
| border-radius: 0 !important; |
| } |
| .video-wrap { |
| width: 100vw; |
| height: 100vh; |
| max-width: 100vw; |
| max-height: 100vh; |
| border-radius: 0 !important; |
| box-shadow: none; |
| aspect-ratio: unset; |
| } |
| .video-wrap::before { |
| box-shadow: none !important; |
| } |
| video { |
| width: 100vw; |
| height: 100vh; |
| border-radius: 0 !important; |
| object-fit: cover; |
| } |
| #player-box { |
| width: 100vw; |
| height: 100vh; |
| max-width: 100vw; |
| max-height: 100vh; |
| border-radius: 0 !important; |
| } |
| .controls { |
| bottom: 15%; |
| left: 0; |
| width: 100vw; |
| justify-content: center; |
| padding-bottom: env(safe-area-inset-bottom, 0); |
| } |
| button { |
| font-size: 15px; |
| padding: 10px 0; |
| width: 36vw; |
| max-width: 120px; |
| } |
| .info { |
| font-size: 12px; |
| left: 8px; |
| top: 8px; |
| } |
| } |
| @media (hover: none) and (pointer: coarse) { |
| .controls { |
| opacity: 1 !important; |
| pointer-events: auto !important; |
| } |
| .info { |
| opacity: 1 !important; |
| } |
| } |
| button { |
| padding: 6px 0; |
| width: 28vw; |
| max-width: 80px; |
| font-size: 13px; |
| border: none; |
| border-radius: 14px; |
| background: rgba(40,40,40,0.05); |
| color: #fff; |
| cursor: pointer; |
| transition: background 0.2s; |
| letter-spacing: 1px; |
| } |
| button:active { |
| background: rgba(64,158,255,0.05); |
| } |
| .guide-tip { |
| position: fixed; |
| top: 18%; |
| left: 50%; |
| transform: translateX(-50%); |
| background: rgba(30,30,40,0.92); |
| color: #fff; |
| padding: 18px 28px; |
| border-radius: 18px; |
| font-size: 17px; |
| box-shadow: 0 4px 24px rgba(0,0,0,0.18); |
| z-index: 9999; |
| opacity: 0; |
| pointer-events: none; |
| transition: opacity 0.5s; |
| } |
| .guide-tip.show { |
| opacity: 1; |
| pointer-events: auto; |
| } |
| #blur-bg-canvas { |
| position: fixed; |
| left: 0; top: 0; width: 100vw; height: 100vh; |
| z-index: 0; |
| pointer-events: none; |
| filter: blur(36px) brightness(0.82) saturate(1.18) hue-rotate(-6deg); |
| opacity: 0.82; |
| transition: opacity 0.5s; |
| } |
| </style> |
| </head> |
| <body> |
| <div id="player-box"> |
| <div class="video-wrap"> |
| <video id="videoPlayer" controls autoplay playsinline webkit-playsinline> |
| <source id="videoSource" src="" type="video/mp4"> |
| 您的浏览器不支持 video 标签。 |
| </video> |
| <div class="loading-spinner" id="loadingSpinner" style="display:none;"></div> |
| <div class="load-fail-tip" id="loadFailTip">视频加载失败,已自动切换下一个</div> |
| <div class="controls"> |
| <button id="loopBtn" aria-label="切换循环模式" tabindex="0"> |
| <span id="loopText">loop:关</span> |
| </button> |
| <button id="nextBtn" aria-label="下一个视频" tabindex="0"> |
| <span style="vertical-align:middle;display:inline-block;width:18px;height:18px;line-height:0;"> |
| <svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="#fff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="5 3 19 12 5 21 5 3"/></svg> |
| </span> |
| <span style="margin-left:4px;">Next</span> |
| </button> |
| </div> |
| <div class="info" id="videoInfo"></div> |
| </div> |
| </div> |
| <div class="guide-tip" id="guideTip">手机点击屏幕可切换视频,长按可切换循环模式</div> |
| <script> |
| |
| let videoList = []; |
| let currentIndex = 0; |
| const videoPlayer = document.getElementById('videoPlayer'); |
| const videoSource = document.getElementById('videoSource'); |
| const loopBtn = document.getElementById('loopBtn'); |
| const nextBtn = document.getElementById('nextBtn'); |
| const videoInfo = document.getElementById('videoInfo'); |
| const loadingSpinner = document.getElementById('loadingSpinner'); |
| const loadFailTip = document.getElementById('loadFailTip'); |
| const loopText = document.getElementById('loopText'); |
| |
| function showLoading() { |
| loadingSpinner.style.display = 'block'; |
| } |
| function hideLoading() { |
| loadingSpinner.style.display = 'none'; |
| } |
| function showFailTip() { |
| loadFailTip.style.display = 'block'; |
| setTimeout(() => { loadFailTip.style.display = 'none'; }, 2000); |
| } |
| |
| |
| fetch('urls.json').then(r => r.json()).then(list => { |
| videoList = list; |
| if (videoList.length === 0) { |
| videoInfo.textContent = '未找到可播放的视频。'; |
| videoPlayer.style.display = 'none'; |
| nextBtn.disabled = true; |
| loopBtn.disabled = true; |
| return; |
| } |
| |
| let initialIdx = 0; |
| if (videoList.length > 1) { |
| initialIdx = Math.floor(Math.random() * videoList.length); |
| } |
| updateVideo(initialIdx); |
| }); |
| |
| function updateVideo(idx) { |
| if (videoList.length === 0) return; |
| currentIndex = idx; |
| videoPlayer.classList.remove('video-fade-in'); |
| videoPlayer.classList.add('video-fade'); |
| setTimeout(() => { |
| videoSource.src = videoList[currentIndex]; |
| videoPlayer.load(); |
| videoInfo.textContent = `当前视频:${currentIndex + 1} / ${videoList.length}`; |
| }, 200); |
| } |
| videoPlayer.addEventListener('canplay', function() { |
| videoPlayer.classList.remove('video-fade'); |
| videoPlayer.classList.add('video-fade-in'); |
| }); |
| videoSource.addEventListener('error', function() { |
| setTimeout(() => { |
| let nextIdx = getRandomNextIndex(); |
| updateVideo(nextIdx); |
| }, 200); |
| }); |
| loopBtn.onclick = function() { |
| videoPlayer.loop = !videoPlayer.loop; |
| loopText.textContent = 'loop:' + (videoPlayer.loop ? '开' : '关'); |
| }; |
| function getRandomNextIndex() { |
| if (videoList.length <= 1) return 0; |
| let nextIdx; |
| do { |
| nextIdx = Math.floor(Math.random() * videoList.length); |
| } while (nextIdx === currentIndex); |
| return nextIdx; |
| } |
| nextBtn.onclick = function() { |
| if (videoList.length === 0) return; |
| let nextIdx = getRandomNextIndex(); |
| updateVideo(nextIdx); |
| }; |
| videoPlayer.addEventListener('ended', function() { |
| if (!videoPlayer.loop) { |
| let nextIdx = getRandomNextIndex(); |
| updateVideo(nextIdx); |
| } |
| }); |
| |
| videoPlayer.loop = false; |
| loopText.textContent = 'loop:关'; |
| |
| let initialIdx = 0; |
| if (videoList.length > 1) { |
| initialIdx = Math.floor(Math.random() * videoList.length); |
| } |
| updateVideo(initialIdx); |
| |
| |
| function isMobile() { |
| return /Mobi|Android|iPhone|iPad|iPod|Mobile/i.test(navigator.userAgent); |
| } |
| if (isMobile()) { |
| const controls = document.querySelector('.controls'); |
| const info = document.querySelector('.info'); |
| let hideTimer = null; |
| function showControls() { |
| controls.style.opacity = '1'; |
| controls.style.pointerEvents = 'auto'; |
| info.style.opacity = '1'; |
| if (hideTimer) clearTimeout(hideTimer); |
| hideTimer = setTimeout(() => { |
| controls.style.opacity = '0'; |
| controls.style.pointerEvents = 'none'; |
| info.style.opacity = '0'; |
| }, 3000); |
| } |
| document.querySelector('.video-wrap').addEventListener('touchstart', showControls); |
| document.querySelector('.video-wrap').addEventListener('click', showControls); |
| |
| setTimeout(() => { |
| controls.style.opacity = '0'; |
| controls.style.pointerEvents = 'none'; |
| info.style.opacity = '0'; |
| }, 3000); |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| if (!isMobile()) { |
| window.addEventListener('keydown', function(e) { |
| if (e.target.tagName.toLowerCase() === 'input' || e.target.tagName.toLowerCase() === 'textarea') return; |
| if (e.code === 'ArrowRight' || e.key === 'ArrowRight') { |
| let nextIdx = getRandomNextIndex(); |
| updateVideo(nextIdx); |
| } else if (e.code === 'ArrowLeft' || e.key === 'ArrowLeft') { |
| let nextIdx = getRandomNextIndex(); |
| updateVideo(nextIdx); |
| } else if (e.code === 'Space' || e.key === ' ') { |
| e.preventDefault(); |
| if (videoPlayer.paused) videoPlayer.play(); |
| else videoPlayer.pause(); |
| } |
| }); |
| } |
| |
| |
| loopBtn.addEventListener('keydown', function(e) { |
| if (e.key === 'Enter' || e.keyCode === 13) { |
| loopBtn.click(); |
| } |
| }); |
| nextBtn.addEventListener('keydown', function(e) { |
| if (e.key === 'Enter' || e.keyCode === 13) { |
| nextBtn.click(); |
| } |
| }); |
| |
| |
| if (!isMobile()) { |
| |
| let blurBgCanvas = document.getElementById('blur-bg-canvas'); |
| if (!blurBgCanvas) { |
| blurBgCanvas = document.createElement('canvas'); |
| blurBgCanvas.id = 'blur-bg-canvas'; |
| document.body.insertBefore(blurBgCanvas, document.body.firstChild); |
| } |
| function updateBlurBg() { |
| try { |
| |
| const w = window.innerWidth, h = window.innerHeight; |
| const scale = window.devicePixelRatio > 1.5 ? 0.25 : 0.33; |
| blurBgCanvas.width = Math.round(w * scale); |
| blurBgCanvas.height = Math.round(h * scale); |
| const ctx = blurBgCanvas.getContext('2d'); |
| ctx.drawImage(videoPlayer, 0, 0, blurBgCanvas.width, blurBgCanvas.height); |
| } catch(e) {} |
| } |
| let blurInterval = 50; |
| let blurTimer = null; |
| videoPlayer.addEventListener('play', function() { |
| if(blurTimer) clearInterval(blurTimer); |
| blurTimer = setInterval(updateBlurBg, blurInterval); |
| }); |
| videoPlayer.addEventListener('pause', function(){ |
| if(blurTimer) clearInterval(blurTimer); |
| }); |
| videoPlayer.addEventListener('ended', function(){ |
| if(blurTimer) clearInterval(blurTimer); |
| }); |
| window.addEventListener('resize', updateBlurBg); |
| |
| setTimeout(updateBlurBg, blurInterval); |
| } |
| </script> |
| </body> |
| </html> |