直接用 translate() 跟随鼠标会偏移,因 clientX/Y 是视口坐标,需用 getBoundingClientRect() 归一化并减去容器偏移;CSS 变量需配合 calc() 驱动 box-shadow 和位置,且必须加 transition;requestAnimationFrame 可避免布局抖动;移动端需监听 touchmove 并阻止默认行为。

为什么直接用 transform: translate() 跟随鼠标会偏移失准
因为 clientX/clientY 返回的是视口坐标,而圆环若处于滚动容器(如 position: relative 的父容器)或有 CSS 缩放(scale)、transform 变换的祖先元素中,直接赋值会导致定位漂移。必须用 getBoundingClientRect() 做坐标归一化,再减去容器偏移。
- 监听
mousemove时,先获取目标容器(比如document.body或某个.canvas)的getBoundingClientRect() - 计算相对偏移:
mouseX - rect.left - window.scrollX(注意:Safari 下window.scrollX不可靠,建议用document.documentElement.scrollLeft) - 把结果设为 CSS 变量:
el.style.setProperty('--x', x + 'px')
如何用 CSS 变量驱动发光圆环的 box-shadow 和位置
CSS 变量不能直接用于 box-shadow 的坐标参数,但可以配合 calc() 在声明中动态组合。关键在于:圆环本身要绝对定位、透明无内容,靠 box-shadow 撑出光晕效果。
- HTML 只需一个空
<div class="cursor-ring"></div> - CSS 中定义:
left: calc(var(--x, 0px) - 20px); top: calc(var(--y, 0px) - 20px);(假设圆环宽高 40px) - 发光核心写法:
box-shadow: 0 0 0 0 var(--ring-color, #6366f1), 0 0 0 12px rgba(99, 102, 241, 0.3)—— 第一层是“虚化起点”,第二层是可见光晕;两层都依赖变量才可动画过渡 - 必须加
transition: left 0.15s ease-out, top 0.15s ease-out, box-shadow 0.2s ease,否则闪烁生硬
为什么 requestAnimationFrame 比直接在 mousemove 里更新更稳
高频 mousemove(尤其触控板)可能每秒触发上百次,直接改 style 会强制同步布局(Layout Thrashing),导致卡顿甚至光晕跳帧。用 requestAnimationFrame 把更新节流到屏幕刷新率(通常 60fps),且能合并多次偏移为最终一次渲染。
- 不要这样写:
el.style.left = x + 'px'; el.style.top = y + 'px'; - 改成缓存坐标 + rAF:
let pending = false; function updateRing(x, y) { if (!pending) { pending = true; requestAnimationFrame(() => { ringEl.style.setProperty('--x', x + 'px'); ringEl.style.setProperty('--y', y + 'px'); pending = false; }); } } - 注意:rAF 回调中不要再读取 DOM 尺寸(如
offsetWidth),否则触发重排
移动端适配时 touchmove 的坑怎么绕过
移动端没有 mousemove,且 touchmove 默认会触发页面滚动(尤其是 iOS Safari),导致 clientX/Y 失效。必须显式阻止默认行为,并从 touches[0] 取坐标。
立即学习“前端免费学习笔记(深入)”;
- 绑定事件时要同时监听:
document.addEventListener('mousemove', handler); document.addEventListener('touchmove', handler, { passive: false }); - 在 handler 中区分:
const x = e.touches ? e.touches[0].clientX : e.clientX; - 务必加
e.preventDefault()—— 但只在需要阻止滚动时加(比如你确实在拖拽 UI),否则影响页面正常滑动;更稳妥的做法是仅对特定区域(如 canvas)启用touchmove阻止 - Android Chrome 对
passive: false更敏感,漏掉会导致控制台警告并静默失效
实际最难调的不是逻辑,是光晕的衰减节奏和初始透明度。很多人把 box-shadow 的 alpha 设太高,导致多层叠加后发白;或者 transition 时间没和 rAF 帧率对齐,造成“拖影感”。建议先关掉所有动画,确认坐标完全准确后再逐级加特效。



















