必须用js将鼠标坐标写入css变量,再通过calc()在box-shadow中动态计算偏移;直接translate会移动元素而非模拟光源,且易被overflow裁剪,而变量驱动阴影才能实现鼠标即光源的精准映射。

直接用 box-shadow 跟鼠标动?不行——CSS 本身读不到坐标,必须靠 JS 把 clientX/clientY 写进 CSS 变量,再在 box-shadow 里用 calc() 算偏移。关键不是“怎么写阴影”,而是“坐标怎么映射过去不翻车”。
为什么不能直接用 transform: translate() 模拟光源
因为 translate() 移的是整个元素,而阴影是视觉层效果:它得从鼠标点发散,不是让按钮“跑向鼠标”。一旦用了 translate,box-shadow 的偏移方向就固定在元素自身坐标系里,失去“鼠标即光源”的语义。更糟的是,如果按钮在 overflow: hidden 容器里,translate 还可能把阴影裁掉。
真正可控的方式是只动阴影参数:box-shadow: calc(var(--mouse-x) - 50% * 1px) calc(var(--mouse-y) - 50% * 1px) 20px #00f —— 这样光从鼠标点投出,且单位运算安全(* 1px 防止浏览器把百分比当无单位值解析失败)。
mousemove 里怎么设变量才不会偏移或跳变
常见错误是直接用 e.clientX 和 e.clientY 写进 :root,结果滚动后光标“漂”到页面外。原因:这些值相对于视口,而阴影偏移要对齐的是目标容器左上角。
- 监听目标容器(不是
document),用e.currentTarget.getBoundingClientRect()拿容器位置 - 算相对坐标:
const x = e.clientX - rect.left,const y = e.clientY - rect.top - 加边界检查:
x = Math.max(0, Math.min(x, rect.width)),避免超出后阴影飞走 - 写入时统一用
px:e.currentTarget.style.setProperty('--mouse-x', x + 'px')(别混用 % 和 px)
box-shadow 动态计算的三个硬限制
CSS 对 box-shadow 里的表达式支持很保守,以下三点不处理,阴影直接失效或卡死:
-
calc()里不能出现var(--x) - 50%这种混合单位减法——必须写成calc(var(--mouse-x) - 50% * 1px),强制转换单位 - 阴影模糊值(第三个参数)超过
40px在 iOS Safari 上会明显掉帧,建议压在20px–30px区间 - 别在同一个选择器里又写
background简写又写transition: box-shadow——background简写会重置所有子属性,导致过渡找不到起始值
移动端 touchmove 怎么无缝接上
触摸屏没有持续 mousemove,只监听 touchmove 就够了,但有两处必须改:
- 坐标取法不同:
e.touches[0].clientX/e.touches[0].clientY,不是e.clientX - 默认行为要阻止:
{ passive: false }+e.preventDefault(),否则滚动时变量不更新 - 别删掉
mousemove监听——桌面端仍需它,用'ontouchstart' in window判断环境后绑定对应事件即可
最易被忽略的是单位一致性:JS 写入时漏了 'px',CSS 里 calc() 又没补 * 1px,浏览器就静默失败,阴影不动也不报错。这种问题查起来极慢,建议一开始就定死单位流:JS 全用像素写入,CSS 全用 calc(var(--x) * 1px) 消费。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











