核心是监听mousemove事件,用getboundingclientrect()获取容器宽高计算鼠标百分比位置,再通过style.setproperty()动态设置css变量,并在css中用合成变量或calc()配合radial-gradient实现平滑背景过渡。

怎么用 JS 动态改 --mouse-x 和 --mouse-y 变量
核心就一句话:监听 mousemove,把 event.clientX / event.clientY 转成相对容器的百分比值,再设到 style.setProperty()。别直接写死像素值,否则 CSS 里用 calc() 或渐变时会错位。
常见错误是直接用 window.innerWidth 做分母——如果背景只作用在某个 <div class="hero"> 上,就得用它的 <code>getBoundingClientRect() 宽高。
- 用
element.addEventListener('mousemove', e => { ... }),别用document全局监听(除非真要全屏响应) - 计算百分比时记得
Math.max(0, Math.min(100, ...))防止鼠标移出区域导致负值或超 100% - 加
{ passive: true }提升滚动流畅度,但注意:如果后续要阻止默认行为(一般不需要),就不能设 passive
CSS 里怎么用这两个变量做背景过渡
变量本身不触发过渡,得靠 background 属性的可动画性。CSS 渐变(radial-gradient 或 linear-gradient)支持数值插值,所以把 --mouse-x 当作位置参数塞进去就行。
容易踩的坑是写成 background: radial-gradient(circle at var(--mouse-x) var(--mouse-y), ...) —— 这样无效,因为 at 后面不接受两个独立变量拼接。得用一个合成变量,或者用 calc() 拼字符串(但 calc 不支持变量拼接)。
立即学习“前端免费学习笔记(深入)”;
- 推荐方案:JS 里直接设置一个合成变量,比如
style.setProperty('--mouse-pos', `${x}% ${y}%`),CSS 里写radial-gradient(circle at var(--mouse-pos), ...) - 如果要用双变量,CSS 得写成
radial-gradient(circle at calc(var(--mouse-x) * 1vw) calc(var(--mouse-y) * 1vh), ...),但单位要对齐,vw/vh比%更稳 -
transition: background 0.3s ease必须写在同一个选择器里,且不能被!important或更高优先级规则覆盖
为什么鼠标一动就卡,过渡不顺
不是 JS 慢,是浏览器在每帧都重算渐变+重绘整个背景层。尤其当容器大、设备分辨率高时,GPU 负担明显。
性能关键点不在“设变量”,而在“怎么用变量”。用 radial-gradient 比 linear-gradient 更吃资源;用 rgba() 半透明色比纯色更耗;背景图叠加也会雪上加霜。
- 把
will-change: background加到元素上,提前告诉浏览器这个属性会变(但别滥用,只加在真正需要的元素) - 过渡时间别低于
0.2s,太快反而让浏览器丢帧 - 移动端慎用——触摸事件
touchmove触发频率低,且坐标需从touches[0]取,clientX/Y在某些安卓 WebView 里不准
兼容性要注意哪些硬伤
CSS 自定义属性(--xxx)IE 完全不支持,Edge 15+ 才行;radial-gradient 在旧 Safari 需要 -webkit- 前缀;transition 对 background 的支持,Safari 12.1 之前有 bug,可能跳变。
最现实的底线是:如果你的项目还要支持 IE 或老 iOS,这条路基本走不通,得降级为 canvas 绘制或固定几个热点位切换 class。
- 检查是否启用了
prefers-reduced-motion,用户开了“减少动画”时,直接关掉 transition 或设为0.01s - Firefox 对
background中含var()的 transition 支持稍晚,100+ 版本才稳定 - 不要依赖
background-position+transition模拟——它只能平移,做不出以鼠标为中心的扩散效果
事情说清了就结束










