纯 CSS 无法实现鼠标跟随聚光灯,因 mask-image 不支持 calc() 或 CSS 变量动态解析渐变位置;必须用 JS 实时更新 --x/--y 变量并触发重绘,且需处理坐标映射、兼容性及性能优化。

mask-image 配合 CSS 变量实现鼠标跟随聚光灯的关键限制
直接说结论:mask-image 本身不支持用 calc() 或变量动态生成渐变位置,也不能直接绑定 cursor 坐标——所以“纯 CSS 实现鼠标跟随聚光灯”在绝大多数浏览器里是**不可行的**。你看到的所谓纯 CSS 案例,要么用了 radial-gradient 固定中心(不跟随),要么靠 JS 注入变量值再触发 mask 重绘。
为什么 mask-image + --x/--y 变量不能直接生效
mask-image 的 radial-gradient 函数里,at var(--x) var(--y) 这种写法在 Chrome/Firefox 中会被忽略,变量不会被解析为数值;即使你写成 at calc(var(--x) * 1px) calc(var(--y) * 1px),浏览器也会报 Invalid property value 错误。
- 原因:CSS 渐变函数中的位置参数不接受自定义属性(custom property)作为计算上下文
- 兼容性:Safari 15.4+ 开始部分支持,但行为不稳定,且不响应
mousemove动态更新 - 替代思路:必须用 JS 读取
event.clientX/clientY,然后实时设置style.setProperty('--x', x),再靠 CSS 触发 mask 重绘
最小可行方案:JS 注入 + mask-image 渐变遮罩
核心是让 JS 控制变量,CSS 负责渲染。注意两个关键点:容器需设 position: relative,mask 元素需设 position: absolute 覆盖全屏,并用 pointer-events: none 避免拦截鼠标。
.spotlight {
--x: 50%;
--y: 50%;
mask-image: radial-gradient(
circle at var(--x) var(--y),
transparent 0%,
black 60%
);
mask-size: 200% 200%;
mask-repeat: no-repeat;
}JS 部分只需监听并更新:
立即学习“前端免费学习笔记(深入)”;
- 监听
document.addEventListener('mousemove', e => {...}) - 计算相对于容器的百分比坐标:
const x = (e.clientX - container.offsetLeft) / container.offsetWidth * 100 - 用
element.style.setProperty('--x', x + '%')更新变量(不要用document.documentElement.style,避免全局污染) - 性能提示:加
throttle或用requestAnimationFrame,否则高频更新 mask 会掉帧
容易被忽略的 mask 兼容性与 fallback 问题
mask-image 在 Safari 旧版本、IE 和部分安卓 WebView 中完全不支持。如果你需要降级,得准备两套逻辑:
- 检测支持:
CSS.supports('mask-image', 'none')或用@supports (mask-image: none) - 不支持时,改用
background: radial-gradient(...)+mix-blend-mode: multiply模拟,但仅适用于浅色背景 - mask 的
mask-size必须设得足够大(如200% 200%),否则鼠标靠近边缘时会出现裁剪黑边 - 别忘了加
-webkit-mask-image前缀,Safari 16.4 之前仍需它
真正难的不是写几行 CSS,而是处理不同屏幕缩放、滚动偏移、iframe 嵌套下的坐标映射——这些都得 JS 算清楚,CSS 只管画圆。


















