Girls / index.html
slionx's picture
Update index.html
201f35a verified
<!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>
// ========== 静态化:异步加载urls.json ==========
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并初始化播放器
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);
// 只在移动端用JS控制按钮/信息区显示隐藏,PC端不做任何干预
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);
// 初始3秒后自动隐藏
setTimeout(() => {
controls.style.opacity = '0';
controls.style.pointerEvents = 'none';
info.style.opacity = '0';
}, 3000);
}
// 智能操作提示弹窗
// window.addEventListener('DOMContentLoaded', function() {
// var tip = document.getElementById('guideTip');
// tip.classList.add('show');
// setTimeout(function() {
// tip.classList.remove('show');
// }, 3000);
// });
// 手势与快捷操作
// 移动端左右滑动切换视频,长按切换循环模式
// if (isMobile()) {
// let touchStartX = 0, touchEndX = 0, touchStartTime = 0, longPressTimer = null;
// const videoWrap = document.querySelector('.video-wrap');
// videoWrap.addEventListener('touchstart', function(e) {
// if (e.touches.length === 1) {
// touchStartX = e.touches[0].clientX;
// touchStartTime = Date.now();
// longPressTimer = setTimeout(() => {
// // 长按切换循环
// videoPlayer.loop = !videoPlayer.loop;
// loopText.textContent = '循环:' + (videoPlayer.loop ? '开' : '关');
// loopIcon.querySelector('svg').style.stroke = videoPlayer.loop ? '#4caf50' : '#fff';
// }, 600); // 600ms为长按
// }
// });
// videoWrap.addEventListener('touchmove', function(e) {
// if (e.touches.length === 1) {
// touchEndX = e.touches[0].clientX;
// if (longPressTimer) clearTimeout(longPressTimer);
// }
// });
// videoWrap.addEventListener('touchend', function(e) {
// if (longPressTimer) clearTimeout(longPressTimer);
// if (Math.abs(touchEndX - touchStartX) > 50) {
// // 左右滑动切换视频
// let nextIdx = getRandomNextIndex();
// updateVideo(nextIdx);
// }
// touchStartX = touchEndX = 0;
// });
// }
// PC端快捷键:左右键切换,空格暂停/播放
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();
}
});
}
// 按钮键盘Enter激活支持
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()) {
// 动态插入模糊背景Canvas
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>