radial-gradient圆心位置不能直接用css变量,需用js拼接字符串或background-position配合固定渐变;鼠标坐标须转为容器内相对值并处理边界;遮罩层必须设pointer-events:none且z-index合理;光斑边缘应使用像素值控制紧凑过渡区间。

radial-gradient 的圆心位置不能直接用 CSS 变量
你写 radial-gradient(circle at var(--x) var(--y), ...),浏览器会直接忽略整个 background-image 声明——CSS 函数体内不解析自定义属性,这是规范限制,不是兼容性问题。
真正能生效的方案只有两种:
- 用 JS 拼接字符串赋值给
element.style.backgroundImage,比如el.style.backgroundImage = `radial-gradient(circle at ${x}px ${y}px, ...)` - 改用
background-position配合固定尺寸的渐变图层(如radial-gradient(circle, transparent 0%, black 100%)),再通过--x/--y控制定位,此时变量只用于background-position,完全合法
前者更直观但需手动处理单位和坐标换算;后者更稳定,且天然支持 CSS 动画过渡(background-position 可被 transition)。
鼠标坐标必须转为容器内相对值
直接用 e.clientX 和 e.clientY 会出错:它们是相对于视口的,滚动后光斑就“飘走”了;如果容器本身有 padding、border 或 transform,也会偏移。
正确做法是:
- 提前调用
container.getBoundingClientRect()获取容器左上角在视口中的位置 - mousemove 中计算
x = e.clientX - rect.left,y = e.clientY - rect.top - 确保结果落在
[0, rect.width]和[0, rect.height]范围内(可加Math.max(0, Math.min(x, rect.width))) - 移动端必须监听
touchmove,并从e.touches[0]取坐标,不能只靠 mousemove
遮罩层必须设 pointer-events: none
否则鼠标悬停、点击会被遮罩层拦截,内容交互全部失效。这不是“看起来没反应”,而是真实事件被吞掉了。
常见错误写法:
- 把遮罩层写成独立
<div> 并忘记加 <code>pointer-events: none - 用伪元素
::before实现遮罩,但没加该样式,或加在了错误的选择器上(比如加在父容器而非伪元素本身) - z-index 设置不当:遮罩层 z-index 太高,盖住了可点击内容;太低又可能被其他层挡住,导致光斑不可见
- 透明中心区保留 5–10px(例如
transparent 0%, transparent 8px) - 柔化过渡控制在 20–40px 内(如
rgba(0,0,0,0.1) 12px, rgba(0,0,0,0.9) 48px) - 避免用百分比停靠点(如
50%),像素值更可控,尤其当容器缩放或响应式变化时
推荐结构:.container { position: relative; } .container::before { content: ""; position: absolute; top: 0; left: 0; width: 100%; height: 100%; background: radial-gradient(...); pointer-events: none; z-index: 1; },内容元素设 position: relative; z-index: 2;。
光斑边缘生硬?检查颜色停止点间距
写成 radial-gradient(circle at center, rgba(0,0,0,0) 0%, rgba(0,0,0,0.8) 50%, black 100%) 是常见错误——50% 到 100% 跨度过大,中间一大段是线性加深,缺乏自然衰减感。
更真实的探照灯效果依赖紧凑的过渡区间:
另外,background-size 设为 300% 300% 可防止光斑被拉伸变形,配合 background-position 移动时更稳定。











