用mask-image配合CSS变量实现鼠标跟随光斑,本质是用radial-gradient创建模糊圆形遮罩,通过--x/--y变量动态控制mask-position,使光斑随鼠标偏移;需设mask-size和mask-repeat: no-repeat,JS实时更新相对百分比坐标,渐变边缘须软化以模拟发光。

mask-image 配合 CSS 变量实现鼠标跟随光斑的核心逻辑
直接说结论:用 mask-image 做鼠标跟随发光效果,本质是把一个高斯模糊的圆形光斑(用 radial-gradient)作为遮罩,再通过 --x 和 --y 两个 CSS 变量动态控制它的 background-position,从而让光斑“跟”着鼠标走。不是靠 JS 拖动元素,而是靠遮罩层偏移模拟发光位移。
关键点在于:遮罩本身不移动,移动的是遮罩下背景图(或伪元素背景)的位置;而发光感来自遮罩边缘的模糊过渡,必须配合 blur() 滤镜或模糊渐变本身。
常见错误现象:mask-image 写了但没效果——多半是忘了设 mask-size 或 mask-repeat: no-repeat,或者变量没在 :root 或目标元素上声明初始值。
-
mask-image必须搭配mask-size(如200px 200px),否则默认铺满容器,光斑就“看不见”了 - 变量需提前声明,比如
:root { --x: 50%; --y: 50%; },否则计算会失效,光斑卡在左上角 - 使用
background: radial-gradient(circle at var(--x) var(--y), #fff, transparent 70%)作遮罩源时,注意单位一致性(% / px 混用会导致错位)
监听鼠标并实时更新 CSS 变量的 JS 写法
不能只靠 CSS 实现跟随,JS 是必须的——但只需最简绑定,不用 requestAnimationFrame 或节流(除非性能敏感)。
立即学习“前端免费学习笔记(深入)”;
核心是 element.style.setProperty() 更新变量,且坐标要转成相对于目标元素的百分比(更稳)或像素值(更准),推荐百分比,避免缩放/滚动偏移问题。
- 监听
mousemove事件,用getBoundingClientRect()算出鼠标在元素内的相对位置 - 更新变量时,用
(clientX - rect.left) / rect.width * 100 + '%'得到横向百分比,同理算--y - 别用
pageX/pageY,它们受页面滚动影响,会导致光斑漂移 - 如果目标元素有
transform: scale(),需额外除以缩放系数,否则光斑会“跑快”
示例片段:
const el = document.querySelector('.glow-container');
el.addEventListener('mousemove', (e) => {
const rect = el.getBoundingClientRect();
const x = ((e.clientX - rect.left) / rect.width * 100).toFixed(2);
const y = ((e.clientY - rect.top) / rect.height * 100).toFixed(2);
el.style.setProperty('--x', `${x}%`);
el.style.setProperty('--y', `${y}%`);
});发光效果不自然?检查 blur 与 gradient 的配合方式
纯 radial-gradient 遮罩边缘生硬,没有“光晕感”。真正的发光依赖两层模糊:一是渐变自身的软边(用透明度过渡),二是外层 filter: blur()(慎用,性能差)。
更优解是把模糊做进渐变里——用多层 radial-gradient 叠加,或改用 conic-gradient + blur() 模拟,但最稳的是提升渐变扩散范围 + 降低透明度衰减速度。
- 避免写
transparent 50%,改成transparent 80%,让光斑更“散” - 起始色别用纯白,用
hsl(0 0% 100% / 0.6)控制整体亮度和通透感 - 不要给遮罩元素加
filter: blur(10px)—— 它会模糊整个元素内容,而不仅是光斑 - 若需更强发光,可在同一元素上叠加两个
mask-image(用逗号分隔),一层主光斑 + 一层更大更淡的辅助晕
移动端适配与 pointer-events 干扰问题
手机端没 mousemove,得 fallback 到 touchmove;更麻烦的是,某些场景下光斑会“卡住”,原因常是 pointer-events: none 被误加在遮罩层或父容器上。
- 触摸事件中,
e.touches[0]替代e.clientX,其余计算逻辑一致 - 确保触发区域的
pointer-events是auto或all,否则 touchmove 不会触发 - 如果光斑在 iOS 上闪烁,大概率是 Safari 对
mask-image的硬件加速限制,可加transform: translateZ(0)强制开启 - 不需要兼容 IE,
mask-image在 Chrome/Firefox/Safari(15.4+)已稳定支持,Edge 也早跟进了
最易被忽略的一点:CSS 变量名大小写敏感,--X 和 --x 是两个变量,JS 里写错一个字母,光斑就彻底不动了——调试时先 console.log 变量值是否更新,再查 mask 是否生效。


















