无jQuery依赖纯HTML/CSS/JS实现指定容器手电筒效果求助
手电筒特效故障排查与修复
核心故障原因
- JS运行时直接报错:通过
document.getElementById获取的单页DOM元素不存在documentElement属性,原代码中gameBox.documentElement实际取值为undefined,后续调用style.setProperty直接抛出类型错误,整个鼠标移动回调中断,这是跟随功能完全失效的根本原因。 - 遮罩定位脱离容器约束:伪元素使用
position: fixed,会直接脱离父容器定位铺满整个浏览器视口,无法实现“仅在指定容器内生效”的效果要求。 - 坐标计算逻辑偏差:直接读取视口层面的
clientX/clientY作为CSS变量值,没有换算为相对于游戏容器的内部相对坐标,会出现光圈位置与鼠标位置大幅偏移的问题。 - 事件绑定范围过宽:将鼠标移动事件绑定在全局
document上,即使鼠标移出游戏容器范围也会触发特效更新,不符合容器专属的效果要求。
修复后可直接运行的代码
原生JS逻辑
// 提前获取目标容器DOM,避免每次鼠标移动都重复查询节点 const gameBox = document.getElementById('gameBox'); function updateFlashlight(e) { // 获取游戏容器在视口中的位置边界 const boxRect = gameBox.getBoundingClientRect(); let relativeX, relativeY; // 兼容鼠标与触摸设备的坐标读取 if (e.type.includes('touch')) { relativeX = e.touches[0].clientX - boxRect.left; relativeY = e.touches[0].clientY - boxRect.top; } else { relativeX = e.clientX - boxRect.left; relativeY = e.clientY - boxRect.top; } // 判断鼠标/触摸点是否落在游戏容器范围内 const isInsideContainer = relativeX >= 0 && relativeX <= boxRect.width && relativeY >= 0 && relativeY <= boxRect.height; // 容器范围内正常更新光圈位置,范围外把光圈挪到容器外避免残留 gameBox.style.setProperty( '--cursorX', isInsideContainer ? `${relativeX}px` : '-9999px' ); gameBox.style.setProperty( '--cursorY', isInsideContainer ? `${relativeY}px` : '-9999px' ); } // 事件仅绑定在目标容器上,不监听全局文档事件 gameBox.addEventListener('mousemove', updateFlashlight); gameBox.addEventListener('touchmove', updateFlashlight); // 鼠标移出容器时直接隐藏光圈 gameBox.addEventListener('mouseleave', () => { gameBox.style.setProperty('--cursorX', '-9999px'); gameBox.style.setProperty('--cursorY', '-9999px'); });
对应CSS样式
#gameContent { cursor: none; } #gameBox { /* 保留原有布局参数,合并重复的margin属性 */ position: relative; height: 768px; width: 768px; border-style: solid; background-color: #EABF7D; border-color: #E1AA72; border-width: 3px; padding: 10px; margin: 0 12% 10px 12%; line-height: 1em; /* 新增:裁剪溢出容器的内容,保证特效不向外扩散 */ overflow: hidden; /* 新增:初始化CSS变量位置,避免页面加载初期光圈错位 */ --cursorX: -9999px; --cursorY: -9999px; } /* 手电筒遮罩层 */ #gameBox::before { content: ''; /* 从fixed改为absolute,相对于#gameBox容器定位 */ position: absolute; top: 0; left: 0; width: 100%; height: 100%; /* 不拦截容器内的图片点击事件,保证原有交互正常 */ pointer-events: none; /* 让遮罩层覆盖在所有游戏图片上方 */ z-index: 10; background: radial-gradient( circle 10vmax at var(--cursorX) var(--cursorY), rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 0.5) 80%, rgba(0, 0, 0, 0.95) 100% ); }
实现效果说明
- 特效完全限制在
#gameBox容器边界内,不会溢出到页面其他区域 - 鼠标在容器内移动时,半透明黑遮罩会跟随鼠标位置挖出圆形亮区,移出容器时特效自动隐藏
- 全程使用原生Web API实现,无jQuery或第三方依赖导入,不改动原有游戏DOM的交互逻辑
- 同时兼容桌面端鼠标操作与移动端触摸滑动场景
内容的提问来源于stack exchange,提问作者KyleE
相关产品推荐
相关产品推荐

