利用CSS径向渐变与伪元素创建黑底透光遮罩,通过JavaScript动态更新CSS自定义变量控制光斑位置,实现鼠标跟随聚光灯效果,避免频繁DOM操作,性能更优,精准揭示底层内容。
本文详解如何用 CSS radial-gradient 配合 CSS 变量和伪元素,创建可随鼠标移动、精准居中且真正“揭示内容”的聚光灯效果,避免 DOM 操作性能损耗。
如果你曾被那种仅叠加一个发光圆、却无法穿透遮罩显示内容的“伪聚光灯”坑过,那么这篇文章将帮你解决真正的问题。核心思路并不复杂:使用 CSS 的 正确做法是:利用 长期稳定更新的攒劲资源: >>>点此立即查看<<< 下面直接上步骤。 这样一来,你就能得到一个轻量、流畅、语义清晰的聚光灯交互效果——它不只是视觉装饰,而是真正能“照亮并揭示内容”的实用 UI 组件。 侠游戏发布此文仅为了传递信息,不代表侠游戏网站认同其观点或证实其描述radial-gradient 配合伪元素,构建一个“黑底+中心透光”的遮罩层,然后通过动态更新 background-position 让光斑跟随鼠标移动。直接插入一个 .box::before 伪元素充当全屏遮罩层,其背景为“中心透明 → 外圈全黑”的径向渐变;再通过 JavaScript 实时更新 --flashlight-x-pos 和 --flashlight-y-pos 两个 CSS 自定义变量,驱动背景定位,使“光洞”始终对准鼠标位置。1. HTML 结构(极简)
Light
2. CSS 样式(核心遮罩逻辑)
.box {
display: flex;
align-items: center;
justify-content: center;
cursor: none;
width: 100%;
padding: 5rem;
background-color: #000;
overflow: hidden;
position: relative;
/* 初始光斑偏移至可视区外,实现“隐藏”效果 */
--flashlight-x-pos: -200px;
--flashlight-y-pos: -200px;
--flashlight-inset: -300px; /* 控制遮罩层大小,确保完全覆盖 */
}
/* 聚光灯遮罩层(伪元素) */
.box::before {
content: '';
position: absolute;
inset: var(--flashlight-inset); /* 全向扩展遮罩 */
background-image: radial-gradient(
circle,
transparent 0%, /* 中心完全透明 → 显示内容 */
rgba(0, 0, 0, 0.8) 60px, /* 过渡到半透明灰黑 */
#000 70px, /* 快速过渡为纯黑 */
#000 100% /* 完全覆盖 */
);
background-position: var(--flashlight-x-pos) var(--flashlight-y-pos);
background-repeat: no-repeat;
z-index: 1;
}
/* 自定义光标(也用伪元素,减少 DOM 节点) */
.box::after {
content: '';
position: absolute;
width: 50px;
height: 50px;
background: url('https://picsum.photos/50') no-repeat center / contain;
transform: translate(var(--flashlight-x-pos), var(--flashlight-y-pos));
pointer-events: none;
z-index: 2;
}
3. JavaScript(高效定位 + 变量驱动)
const box = document.querySelector('.box');
// 关键:getBoundingClientRect 在事件外计算一次,避免 mousemove 中重复调用(性能敏感!)
const rect = box.getBoundingClientRect();
box.addEventListener('mousemove', (e) => {
const x = e.clientX - rect.left;
const y = e.clientY - rect.top;
// 计算相对于容器中心的偏移量(使光斑自然居中)
box.style.setProperty('--flashlight-x-pos', `${x - rect.width / 2}px`);
box.style.setProperty('--flashlight-y-pos', `${y - rect.height / 2}px`);
});
box.addEventListener('mouseleave', () => {
// 鼠标离开时,将光斑移出视区,实现平滑隐藏
box.style.setProperty('--flashlight-x-pos', '-300px');
box.style.setProperty('--flashlight-y-pos', '-300px');
});
关键优势说明:
.box::before 的黑色遮罩层加上中心透明区域,天然支持“透过光洞看到下方 ”,完全不需要额外图层叠加。appendChild 或 style.left/top 操作,只更新 CSS 变量,浏览器自己就能优化渲染。background-position 会自动适配缩放与滚动,完全不用重算坐标。radial-gradient 的参数,即可调整光斑大小、边缘柔硬度、遮罩深浅(比如 rgba(0,0,0,0.9) 会更暗)。 注意事项:
.box 有明确的尺寸(比如 width/height 或 padding),否则 getBoundingClientRect() 的返回值可能不稳定。scroll 并重新计算 rect,或者改用 e.offsetX/e.offsetY(但兼容性略低)。background-image 而不是 标签,避免 pointer-events: none 导致的点击穿透问题。