
通过将自定义光标元素的 CSS position 改为 fixed,并配合使用 clientX/clientY 坐标替代 pageX/pageY,可确保光标始终锚定在视口内,不受页面滚动影响。
通过将自定义光标元素的 css `position` 改为 `fixed`,并配合使用 `clientx/clienty` 坐标替代 `pagex/pagey`,可确保光标始终锚定在视口内,不受页面滚动影响。
问题根源在于:当前光标使用 position: absolute 定位,其坐标基于文档流中的偏移量(pageX/pageY),而滚动会改变文档整体的可视区域,导致光标被“拖走”;同时 pageX/pageY 包含滚动偏移,进一步加剧了错位。
✅ 正确解法分两步:
1. 修改 CSS:将 .cursor 的定位方式改为 fixed
.cursor {
pointer-events: none;
position: fixed; /* 关键修改:从 absolute → fixed */
width: 80px;
height: 80px;
background: radial-gradient(circle, #000 0%, #000 3.99%, #fff 4%, #fff 100%);
border-radius: 50%;
mix-blend-mode: exclusion;
transform: translate(-50%, -50%); /* 推荐添加:实现精准居中定位 */
z-index: 9999;
}✅ position: fixed 使元素相对于视口(viewport)定位,滚动时坐标系不变,从根本上解决“光标随页面滚动移出视野”的问题。
2. 更新 JavaScript:使用 clientX/clientY 并修正偏移逻辑
const cursor = document.querySelector('.cursor');
document.addEventListener('mousemove', (e) => {
// clientX/clientY 始终相对于视口左上角(不含滚动偏移)
cursor.style.left = `${e.clientX}px`;
cursor.style.top = `${e.clientY}px`;
// 使用 transform 替代直接设置 top/left(性能更优,避免重排)
// cursor.style.transform = `translate(${e.clientX - 40}px, ${e.clientY - 40}px)`;
});
document.addEventListener('click', (e) => {
cursor.classList.add('click');
setTimeout(() => cursor.classList.remove('click'), 500);
});⚠️ 注意:pageX/pageY = clientX/clientY + 当前滚动距离,因此滚动后 pageX/pageY 会大幅增加,导致光标飞出视口;而 clientX/clientY 恒定反映鼠标在当前可视窗口内的真实位置,是 fixed 定位下的唯一正确选择。
? 进阶建议:
- 添加 transform: translate(-50%, -50%) 配合 left: x; top: y;,可让光标中心精准对准鼠标点(而非左上角);
- 若需兼容移动端,应监听 touchmove 并做相应适配;
- 确保 .cursor 元素位于 <body> 最顶层(如置于 body 开头),避免被其他 fixed 或 absolute 元素遮挡。
至此,无论用户如何滚动页面或快速拖拽,自定义光标都将稳定跟随鼠标,实现专业级交互体验。

















