d
This commit is contained in:
@@ -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>
|
||||
Reference in New Issue
Block a user