
本文介绍一种无需额外 DOM 元素、基于 CSS ::before 伪元素与 CSS 自定义变量的高性能手电筒光照效果实现方案,通过径向渐变遮罩动态跟随鼠标位置,精准“照亮”黑色背景中的内容。
本文介绍一种无需额外 dom 元素、基于 css `::before` 伪元素与 css 自定义变量的高性能手电筒光照效果实现方案,通过径向渐变遮罩动态跟随鼠标位置,精准“照亮”黑色背景中的内容。
实现真正的“光照揭示”效果,关键在于遮罩而非叠加:不是在内容上方添加一个发光圆圈(这只会叠加亮色,无法透出底层内容),而是用一个带中心透明孔洞的暗色径向渐变作为遮罩层,仅在鼠标位置“挖空”该遮罩,从而让下方内容自然显露——这才是视觉上真实的“手电筒照射”效果。
以下为完整、可直接运行的实现方案:
✅ 核心思路
- 利用 .box::before 伪元素创建全屏(实际是全容器)遮罩层,背景为 radial-gradient:从中心透明 → 向外渐变为不透明黑色;
- 使用 CSS 自定义属性 --flashlight-x-pos 和 --flashlight-y-pos 动态控制该遮罩的 background-position,使其始终锚定在鼠标坐标;
- 将自定义光标(如手电筒图标)移至 ::after 伪元素中,避免 JS 创建/操作 DOM,提升性能;
- 预先计算 getBoundingClientRect(),避免在高频 mousemove 中重复调用,显著减少重排重绘开销。
? 完整代码示例
HTML
<div class="box"> <h1>Light</h1> </div>
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.2) 60px,
rgba(0, 0, 0, 0.95) 100%
);
background-position: var(--flashlight-x-pos) var(--flashlight-y-pos);
background-repeat: no-repeat;
pointer-events: none; /* 不拦截鼠标事件 */
}
/* 自定义光标(手电筒图标) */
.box::after {
content: '';
position: absolute;
width: 50px;
height: 50px;
background-image: url('https://picsum.photos/50');
background-size: cover;
border-radius: 50%;
transform: translate(var(--flashlight-x-pos), var(--flashlight-y-pos));
pointer-events: none;
z-index: 10;
}JavaScript
const box = document.querySelector('.box');
// ⚠️ 关键:只计算一次,避免在 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');
});? 注意事项与优化建议
- 性能优先:getBoundingClientRect() 已移出事件监听器,transform + background-position 均为 GPU 加速属性,确保 60fps 流畅;
- 光照尺寸可控:调整 radial-gradient 中 60px(透明半径)和 100px(过渡结束)可改变光斑大小与边缘柔和度;
- 兼容性:支持所有现代浏览器(Chrome 49+、Firefox 41+、Safari 9.1+、Edge 79+);
- 无障碍提示:若需支持键盘导航,可补充 focus/blur 逻辑或添加 aria-live 区域说明当前交互状态;
- 响应式适配:如需适配不同屏幕尺寸,可将 60px 替换为 clamp(30px, 8vw, 80px) 等相对单位。
此方案摒弃了低效的 DOM 操作与冗余图层,以纯 CSS 遮罩逻辑达成真实光照感,兼顾表现力与性能,是现代 Web 实现动态光照效果的推荐实践。



















