纯CSS变量无法实现像素级鼠标跟随,必须用JavaScript实时更新--mouse-x/--mouse-y;监听mousemove或touchmove,用clientX/clientY或pageX/pageY配合坐标偏移与单位处理,再结合CSS transform等实现精准跟随。

纯 CSS 变量本身不产生跟随效果,它只是数据管道;真正实现鼠标跟随必须靠 JavaScript 实时更新 --mouse-x 和 --mouse-y,再由 CSS 读取并用于 transform 或 mask-position 等属性。绕过 JS 的“纯 CSS 跟随”全是区域感应或视差错觉,不是像素级同步。
监听 mousemove 并写入 :root 变量最稳妥
用 document.documentElement.style.setProperty() 更新根元素变量,能确保全局可访问且不受滚动影响。关键不是用 pageX 还是 clientX,而是目标效果需求:
- 要“贴着屏幕光标”动(比如全局指针),用
e.clientX和e.clientY - 要“固定在文档某处随滚动移动”(比如锚点高亮),才用
e.pageX/e.pageY,但需手动减去window.scrollX/window.scrollY - 别写到
document.body—— 滚动时 body 高度变化会导致坐标偏移
calc() 中单位混用是常见失效原因
var(--mouse-x) 存的是带单位的字符串(如 "123px"),不能直接参与百分比运算。下面这行会静默失败:
transform: translate(calc(var(--mouse-x) - 50%), calc(var(--mouse-y) - 50%));
正确做法有二:
立即学习“前端免费学习笔记(深入)”;
- JS 侧提前算好偏移:把
e.clientX - width/2直接设为--mouse-x,CSS 里直接transform: translate(var(--mouse-x), var(--mouse-y)) - CSS 侧用单位乘法:写成
transform: translate(calc(var(--mouse-x) * 1px), calc(var(--mouse-y) * 1px)),强制赋予长度语义
移动端 touchmove 必须单独处理
移动端没有 mousemove,只触发 touchmove,且默认行为可能被拦截。不加处理会导致真机上跟随完全失效:
- 监听
touchmove时,取e.touches[0].pageX,不是e.changedTouches[0].pageX(后者只在手指起落时触发) - 加
{ passive: false }选项,否则e.preventDefault()在部分安卓 WebView 中无效 - 务必加
touch-action: none到目标容器,否则系统手势(如双指缩放)会干扰坐标读取
性能陷阱:will-change 和合成层滥用
看似只改两个变量,但如果大量元素都用了 var(--mouse-x) 做 transform,浏览器每帧都要重算合成层,低端设备极易掉帧:
- 只对真正需要跟随的元素设
will-change: transform,别写在* {}或body上 - 避免在伪元素(
::before)里用background-position: calc(...)驱动发光效果——它会强制重绘整个背景图 - 如果用
mask-image做光斑,mask-size必须显式声明(如200px 200px),否则默认铺满容器导致光斑不可见
最容易被忽略的点:所有坐标计算都默认以左上角为原点,但人眼感知的“跟随”通常要求中心对齐。这意味着要么 JS 里减半宽高,要么 CSS 里用 margin-left: -50%; margin-top: -50% 配合 transform: translate(var(--x), var(--y)) 拆解实现——混用两种方案反而会错位。


















