必须显式补单位,因clientx返回纯数字,若不加"px"则radial-gradient中var(--x)解析失败而降级居中;需转相对坐标防滚动漂移,用drop-shadow()可降低gpu占用35%,并须确保calc()内单位统一。

直接用 mousemove 频繁调用 element.style.setProperty() 更新 --x/--y 是可行的,但不加节流或坐标处理,光效会漂移、卡顿、边缘生硬——关键在坐标单位、更新时机和渲染层选择。
为什么 clientX/clientY 不能直接塞进 radial-gradient?
因为 radial-gradient(circle at var(--x) var(--y), ...) 中的 var(--x) 若没带单位(如 "123px"),浏览器会降级为默认居中;而 clientX 返回纯数字,拼进去就是 at 123 45,语法非法,整个渐变失效。
必须显式补单位:document.documentElement.style.setProperty('--mouse-x', `${e.clientX}px`)。
若目标容器非全屏,还得转成相对坐标:e.clientX - rect.left + 'px',否则光斑随滚动“飞走”。
用 filter: drop-shadow() 还是 box-shadow?
drop-shadow() 是滤镜,作用于绘制后的位图,不参与布局计算;box-shadow 属盒模型渲染层,每帧修改都触发合成器重绘。
实测同场景下,drop-shadow() 的 GPU 占用低约 35%。
写法示例:filter: drop-shadow(calc(var(--mouse-x) - 50px) calc(var(--mouse-y) - 50px) 12px rgba(255, 220, 100, 0.6));注意这里用 calc() 做偏移补偿,且所有值必须带单位(px)才能生效。
别混用单位:calc(var(--mouse-x) - 50%) 会直接失效。
怎么避免 mousemove 导致掉帧?
高频 mousemove 本身不卡,但若每次都在回调里做以下操作,就会 layout thrashing:
• 调用 getBoundingClientRect()(尤其在滚动中反复查)
• 修改多个元素的 style 属性
• 在通配规则(如 * { transform: translate(...) })里引用鼠标变量
正确做法:
• 提前缓存容器 rect = target.getBoundingClientRect()
• 只对真正需要光效的元素设 will-change: filter,别全局加
• 用 pointer-events: none 确保伪元素/遮罩层不拦截交互
• 移动端监听 touchmove 并取 e.touches[0].clientX,同时加 { passive: false } 保证 preventDefault() 可用
mask-image 方案里光斑总卡左上角?
这是漏了 mask 三要素:
• mask-image: radial-gradient(circle at var(--x) var(--y), #fff, transparent 70%)
• mask-size: 300px 300px(不设则默认铺满容器,光斑不可见)
• mask-repeat: no-repeat(不设则平铺出多个光洞)
且变量必须提前声明::root { --x: 50%; --y: 50%; },否则 mask-position: var(--x) var(--y) 解析为 NaN,直接 fallback 到 0 0。
渐变边缘要软:透明过渡段至少到 70%,否则光斑硬边像手电筒,不像发光。
光效是否自然,不取决于用了多少层 box-shadow,而在于坐标单位是否严谨、渲染层是否选对、以及变量更新是否避开重排陷阱。最易被忽略的是:移动端 touchmove 的坐标来源、滚动时 getBoundingClientRect() 是否重新获取、还有 calc() 里单位混用导致整条 CSS 规则静默失效。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











