应使用 pointer-events: none 让自定义光标穿透,子元素设 auto;用 requestAnimationFrame + clientX/clientY 更新位置;优先 transform: translate() 替代 left/top;注意缩放、滚动、iframe 和移动端适配。

用 pointer-events: none 避免光标被遮挡或触发误交互
默认情况下,自定义光标元素(比如一个 <div class="cursor"></div>)会拦截鼠标事件,导致下方按钮点不动、链接无法 hover。必须给它加 pointer-events: none,让鼠标“穿透”过去。但注意:如果这个元素内部还要放可点击的子元素(比如放大镜图标),就得单独给子元素设 pointer-events: auto —— 否则整个定制光标就废了。
用 requestAnimationFrame + event.clientX/event.clientY 实时更新位置
监听 mousemove 本身没问题,但直接在事件回调里改 style.left 和 style.top 容易掉帧。更稳的做法是把坐标存到变量里,再用 requestAnimationFrame 统一更新 DOM。同时要注意:坐标得用 event.clientX 和 event.clientY,不是 pageX/pageY —— 后者受页面滚动影响,会导致光标在滚动时“漂移”。
示例关键逻辑:
let x = 0, y = 0;
document.addEventListener('mousemove', e => {
x = e.clientX;
y = e.clientY;
});
function render() {
cursor.style.left = `${x}px`;
cursor.style.top = `${y}px`;
requestAnimationFrame(render);
}
render();用 transform: translate() 替代 left/top 提升性能
直接改 left/top 会触发 layout,而 transform: translate(x, y) 只走合成层,GPU 加速,动画更顺。尤其在高刷新率屏幕或低端设备上差异明显。记得给光标元素加 will-change: transform 或 backface-visibility: hidden 做轻微提示,但别滥用——没动画时加了反而可能增加内存开销。
立即学习“前端免费学习笔记(深入)”;
- 正确写法:
cursor.style.transform = `translate(${x}px, ${y}px)` - 错误写法:
cursor.style.left = x + 'px'; cursor.style.top = y + 'px' - 如果要做平滑跟随(非瞬时贴合),可以在
render()里加简单插值,比如x = x * 0.8 + targetX * 0.2
处理缩放、滚动、iframe 等边界情况
页面有 zoom 或 CSS scale() 时,clientX/clientY 不变,但光标视觉位置会偏。此时得用 getBoundingClientRect() 或监听 resize + 检测 window.devicePixelRatio 来动态校正。更麻烦的是 iframe:跨域 iframe 内的 mousemove 事件根本收不到,只能靠 postMessage 协作,或者干脆放弃 iframe 内的跟随。另外,移动端没有稳定鼠标事件,touchmove 的 touches[0] 坐标要取 clientX/clientY,且需防 touch-action 干扰(比如加 touch-action: none 到光标元素上,但小心影响页面滚动)。
真正难的从来不是“怎么动起来”,而是“动得准、不卡、不打架”。坐标来源、图层策略、事件穿透、多端适配——漏掉任意一环,用户一滚动、一缩放、一点 iframe,效果就崩了。


















