
本文介绍在 HTML Canvas 中通过 globalCompositeOperation 配合 createRadialGradient 实现真正透明的径向光照效果,解决纯色遮盖底层图形的问题,适用于光线投射(ray casting)类游戏开发。
本文介绍在 html canvas 中通过 `globalcompositeoperation` 配合 `createradialgradient` 实现真正透明的径向光照效果,解决纯色遮盖底层图形的问题,适用于光线投射(ray casting)类游戏开发。
在使用 Canvas 实现动态光源(如鼠标位置为中心的圆形光晕)时,仅依赖 createRadialGradient 默认绘制行为往往达不到预期效果:即使设置了从 rgba(255,255,255,0.8) 到 rgba(0,0,0,1) 的渐变,若直接用 ctx.fillStyle 或 ctx.strokeStyle 填充/描边,Canvas 仍会以“覆盖模式”(source-over)绘制——即白色区域不透明,完全遮挡下方已绘制的迷宫墙壁(ctx.stroke() 线条),导致“光照穿透”失效。
关键在于改变图层混合方式。Canvas 提供了 globalCompositeOperation 属性,用于控制新绘内容与已有像素的合成逻辑。针对本场景(需让亮区“透出”背景、暗区“吞噬”背景),推荐使用:
ctx.globalCompositeOperation = 'darken';
该模式对每个像素取新绘制值与原像素值的较暗者:
- 白色(高亮度)区域 → 保留底层线条(因底层墙线通常为深色,
darken选深色,故白区“消失”,显露出墙); - 黑色(低亮度)区域 → 覆盖为黑,实现“不可见”的黑暗效果;
- 中间灰度则自然过渡,形成柔和光晕。
✅ 正确用法示例(整合进你的射线循环):
// 绘制所有迷宫墙壁(一次完成)
this.drawWalls(); // 此处 ctx.globalCompositeOperation 应为默认 'source-over'
// 切换混合模式,开始绘制光照
ctx.globalCompositeOperation = 'darken';
for (let i = 0; i < this.rays.length; i++) {
const { x, y } = this.rays[i];
const radius = this.circularRayRadius; // e.g., 200
const gradient = ctx.createRadialGradient(x, y, 0, x, y, radius);
// 关键:使用带 alpha 的 rgba,增强渐变层次感
gradient.addColorStop(0, 'rgba(255, 255, 255, 0.3)'); // 半透明白心
gradient.addColorStop(1, 'rgba(0, 0, 0, 0.9)'); // 高透明黑边(非全 opaque)
// 用渐变填充一个圆(非线条!因 radial gradient 对 stroke 效果差)
ctx.beginPath();
ctx.arc(x, y, radius, 0, Math.PI * 2);
ctx.fillStyle = gradient;
ctx.fill();
}
// 恢复默认模式,避免影响后续绘制
ctx.globalCompositeOperation = 'source-over';⚠️ 注意事项:
-
勿对
stroke()使用渐变模拟光晕:createRadialGradient作用于strokeStyle时,Canvas 无法沿路径方向正确映射径向中心,易产生断裂或偏移。应改用fill()绘制实心发光圆盘。 -
渐变色停止点务必含 alpha:仅写
'white'或'#fff'会导致完全不透明;必须显式使用rgba()或hsla()格式控制透明度。 -
性能提示:1000 条射线对应 1000 个
arc+fill调用开销较大。可优化为单次Path2D合并所有圆,或改用putImageData进行像素级光照计算(进阶方案)。 - 其他可用混合模式参考:
'multiply'(更自然的阴影叠加)、'screen'(提亮效果,适合反向光晕),但'darken'最贴合“可见光区透出、黑暗区遮蔽”的直觉需求。
通过合理组合 globalCompositeOperation 与带 Alpha 的径向渐变,你将获得真正具备视觉穿透性的动态光源——玩家能在光晕中清晰看见迷宫结构,而光晕外区域沉入可控的黑暗,完美支撑随机生成迷宫的探索体验。

















