最直接纯CSS实现“跟随鼠标”的探照灯效果,是用radial-gradient(circle at var(--x) var(--y), rgba(255,255,255,0.8) 0%, rgba(0,0,0,0.2) 60%, rgba(0,0,0,1) 100%)作为背景遮罩,配合JS监听mousemove/touchmove动态更新--x/--y变量(需相对于容器坐标),并设background-size: 300% 300%、pointer-events: none及合理z-index。

用 radial-gradient 模拟探照灯光斑最直接
纯 CSS 实现“跟随鼠标”的探照灯,本质是把一个圆形渐变遮罩层的中心点实时对齐到鼠标坐标。关键不是真追踪鼠标(CSS 本身无事件响应能力),而是借助 radial-gradient + background-position 动态控制光斑位置,再配合 pointer-events: none 让遮罩不拦截交互。
常见错误是试图用 transform: translate() 移动一个绝对定位的伪元素——这会导致光斑边缘生硬、缩放失真,且无法自然实现中心聚光+外围柔化过渡。
- 光斑必须用
radial-gradient(circle at Xpx Ypx, ...),其中X和Y需由 JS 注入为 CSS 变量 - 遮罩层需覆盖整个容器(
width: 100%; height: 100%),背景色设为纯黑或深灰 - 内容区域要设为相对定位,且 z-index 高于遮罩层
- 遮罩层自身加
pointer-events: none,否则鼠标悬停会失效
JS 必须监听 mousemove 并更新 --x / --y CSS 变量
没有 JS 就没法响应鼠标移动,这是不可绕过的环节。但只需极简逻辑:监听 mousemove,读取 e.clientX 和 e.clientY,写入自定义属性。注意坐标必须相对于目标容器,而非视口。
容易踩的坑是直接用 clientX/clientY 而没减去容器 offset —— 导致光斑漂移;或者在滚动页面时没监听 scroll 重算偏移,造成定位错乱。
立即学习“前端免费学习笔记(深入)”;
- 获取容器
getBoundingClientRect(),用e.clientX - rect.left算出相对 X - 同理计算 Y,确保值始终在
0到containerWidth/Height范围内 - 用
el.style.setProperty('--x', x + 'px')更新变量,不要用document.documentElement.style(全局污染) - 若容器可滚动,需在
scroll后重新计算rect,或改用elementFromPoint辅助校准
radial-gradient 的参数顺序和颜色停靠点决定光效质感
光斑是否“像探照灯”,取决于渐变里透明度与颜色的组合方式。错误写法如 radial-gradient(circle at var(--x) var(--y), white, transparent) 会得到一个边缘过快衰减的硬边光斑,缺乏真实感。
真正可用的配置要分段控制:中心高亮 → 中间过渡 → 外围全黑。同时注意 background-size 必须设为足够大(比如 300% 300%),否则光斑会被裁切。
- 推荐写法:
radial-gradient(circle at var(--x) var(--y), rgba(255,255,255,0.8) 0%, rgba(0,0,0,0.2) 60%, rgba(0,0,0,1) 100%) - 若想增强“聚焦感”,可叠加第二层更小的纯白圆:
radial-gradient(circle at var(--x) var(--y), #fff 0%, transparent 30%), [上面那层] -
background-blend-mode: screen可让多层渐变叠加更自然,但 IE 不支持 - 避免用
%单位写停靠点(如50%),应统一用px或无单位数值,防止在不同缩放下错位
移动端需额外处理 touchmove 且禁用缩放干扰
在 iOS/Android 上,mousemove 不触发,必须监听 touchmove 并取 touches[0]。更大的问题是双指缩放会改变视口尺寸,导致 getBoundingClientRect() 返回值突变,光斑瞬间跳走。
这不是 CSS 能解决的问题,得靠 JS 层防御:监听 resize 和 orientationchange,并在缩放后强制重置一次坐标;同时给容器加 touch-action: manipulation 减少延迟。
- 触摸事件中,用
e.touches[0].clientX替代e.clientX - 禁用双指缩放:
<meta name="viewport" content="user-scalable=no">(仅限必要场景) - 若允许缩放,应在
resize后重新调用getBoundingClientRect()并更新变量 - 安卓部分浏览器 touchmove 频率低,可加
{ passive: false }并手动e.preventDefault()(慎用,影响滚动)
光斑边缘柔化程度、JS 坐标采样频率、以及容器 transform 属性的存在,都会让定位产生像素级偏差——这些细节在开发时几乎看不见,上线后在高 DPI 屏或滚动中才暴露。


















