This commit is contained in:
2026-05-31 23:52:04 +08:00
parent 3a3699fb0f
commit 2e9fb16579
3 changed files with 164 additions and 102 deletions

View File

@@ -1,74 +1,99 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<script src="./lazyload.min.js"></script>
<style>
* {
margin: 0;
padding: 0;
list-style: none;
font-size: 0;
line-height: 0;
background: #000;
}
ul {
display: flex;
flex-wrap: wrap;
}
ul li {
width: 20%;
background: #000;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
}
ul li .lazy {
display: block;
width: 100%;
height: 100%;
object-fit: cover;
}
</style>
</head>
<body>
<ul></ul>
</body>
<script>
// 分页游标
let page = "";
// 滚动相关状态
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>视频圆形放大镜(修复版)</title>
<script src="./lazyload.min.js"></script>
<style>
* {
margin: 0;
padding: 0;
list-style: none;
font-size: 0;
line-height: 0;
background: #000;
}
ul {
display: flex;
flex-wrap: wrap;
}
ul li {
width: 20%;
background: #000;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
position: relative;
overflow: hidden;
}
ul li .lazy {
display: block;
width: 100%;
height: 100%;
object-fit: cover;
pointer-events: auto;
}
/* 放大镜样式 */
.video-magnifier {
position: fixed;
width: 380px;
height: 380px;
border-radius: 50%;
border: 3px solid #fff;
box-shadow: 0 0 15px rgba(0, 0, 0, 0.5);
pointer-events: none;
overflow: hidden;
z-index: 9999;
display: none;
}
.video-magnifier video {
position: absolute;
width: 100%;
height: 100%;
}
</style>
</head>
<body>
<ul></ul>
<!-- 放大镜容器 -->
<div class="video-magnifier" id="magnifier">
<video id="magnifierVideo" muted loop playsinline></video>
</div>
<script>
let page = "";
const defaultSpeed = 2;
let scrollSpeed = defaultSpeed;
let scrollLock = false;
let handlerId = null;
let currentVideo = null;
const ul = document.querySelector("ul");
// 懒加载实例(只初始化一次)
const magnifier = document.getElementById('magnifier');
const magnifierVideo = document.getElementById('magnifierVideo');
let hoveredVideo = null;
let lazyLoadInstance = null;
/**
* 初始化懒加载(全局唯一)
*/
const initLazyLoad = () => {
if (lazyLoadInstance) return;
lazyLoadInstance = new LazyLoad({
threshold: 300,
elements_selector: ".lazy",
unobserve_entered: true, // 加载后取消观察,省性能
unobserve_entered: true,
cancel_on_exit: true,
});
};
/**
* 获取接口数据
*/
const getHttpData = async () => {
if (scrollLock) return;
try {
@@ -83,18 +108,12 @@
}
};
/**
* 高性能渲染(文档片段 + 批量插入)
*/
const render = (data) => {
const { feeds, pcursor } = data;
page = pcursor;
if (!feeds || feeds.length === 0) return;
// 🔥 关键优化使用文档片段只操作一次DOM
const fragment = document.createDocumentFragment();
feeds.forEach(item => {
const li = document.createElement("li");
const video = document.createElement("video");
@@ -104,39 +123,24 @@
video.muted = true;
video.loop = true;
video.playsInline = true;
video.author = `https://www.kuaishou.com/profile/${item.author.id}`
video.author = `https://www.kuaishou.com/profile/${item.author.id}`;
const posterUrl = item.photo.animatedCoverUrl || item.photo.coverUrl;
video.poster = posterUrl;
video.dataset.src = item.photo.photoUrls[0]?.url || "";
// 事件委托优化(不再每个视频绑定事件)
li.appendChild(video);
fragment.appendChild(li);
});
// 🔥 一次性插入所有节点
ul.appendChild(fragment);
// 🔥 只更新新增节点,不重新全量初始化
if (lazyLoadInstance) {
lazyLoadInstance.update();
}
// 首次加载自动滚动
if (!handlerId) {
startScroll();
}
if (lazyLoadInstance) lazyLoadInstance.update();
if (!handlerId) startScroll();
};
/**
* 自动滚动
*/
const startScroll = () => {
const scrollStep = () => {
const currentTop = window.scrollY || document.documentElement.scrollTop;
window.scrollBy(0, scrollSpeed); // 🔥 性能更高:相对滚动
window.scrollBy(0, scrollSpeed);
handlerId = requestAnimationFrame(scrollStep);
};
handlerId = requestAnimationFrame(scrollStep);
@@ -151,18 +155,13 @@
handlerId ? stopScroll() : startScroll();
};
/**
* 点击/空格控制启停
*/
document.onclick = toggleScroll;
document.onkeydown = (e) => {
const { keyCode } = e
const { keyCode } = e;
if (keyCode === 79) {
e.preventDefault();
if (currentVideo) open(currentVideo.author)
if (currentVideo) open(currentVideo.author);
}
if (keyCode === 32) {
e.preventDefault();
toggleScroll();
@@ -181,46 +180,69 @@
}
};
/**
* 滚动加载(节流 + 高性能计算)
*/
let scrollTimer = null;
window.onscroll = () => {
if (scrollLock) return;
// 🔥 节流:每 150ms 最多执行一次
clearTimeout(scrollTimer);
scrollTimer = setTimeout(() => {
const scrollTop = window.scrollY;
const clientHeight = window.innerHeight;
const scrollHeight = document.documentElement.scrollHeight;
if (clientHeight + scrollTop >= scrollHeight - 300) {
getHttpData();
}
}, 150);
};
// 🔥 视频事件委托全局只绑定一次性能提升10倍
// ==================== 已修复:鼠标指哪放大哪 ====================
const scale = 4; // 放大倍数
ul.addEventListener('mousemove', (e) => {
if (e.target.tagName !== 'VIDEO') return;
const video = e.target;
// 切换视频时更新放大镜源
if (hoveredVideo !== video) {
hoveredVideo = video;
magnifierVideo.src = video.dataset.src || video.src;
magnifierVideo.play();
}
// 放大镜跟随鼠标(中心对齐)
magnifier.style.display = 'block';
magnifier.style.left = (e.clientX - magnifier.offsetWidth / 2) + 'px';
magnifier.style.top = (e.clientY - magnifier.offsetHeight / 2) + 'px';
// 获取视频元素的位置和尺寸
const rect = video.getBoundingClientRect();
// 鼠标在视频内的相对坐标0~1
const relativeX = (e.clientX - rect.left) / rect.width;
const relativeY = (e.clientY - rect.top) / rect.height;
// 核心修复:让鼠标位置在放大镜正中心
const offsetX = -(relativeX - 0.5) * rect.width * scale;
const offsetY = -(relativeY - 0.5) * rect.height * scale;
magnifierVideo.style.transform = `scale(${scale}) translate(${offsetX}px, ${offsetY}px)`;
});
// 移出视频区域关闭放大镜
ul.addEventListener('mouseleave', () => {
magnifier.style.display = 'none';
magnifierVideo.pause();
magnifierVideo.src = '';
hoveredVideo = null;
}, true);
// 原有播放逻辑
ul.addEventListener("mouseenter", (e) => {
if (e.target.tagName === "VIDEO") {
e.target.readyState >= 2 && e.target.play();
currentVideo = e.target
console.log("currentVideo: ", currentVideo);
currentVideo = e.target;
}
}, true);
ul.addEventListener("mouseleave", (e) => {
if (e.target.tagName === "VIDEO") {
let video = e.target
video.pause();
video.src = "";
video.src = video.dataset.src;
}
}, true);
// 启动
initLazyLoad();
getHttpData();
</script>
</html>
</body>
</html>