/** * 文件功能:聊天室雷电特效 * * 使用递归分叉算法在 Canvas 上绘制真实感闪电路径, * 配合全屏闪白效果模拟雷闪。总持续约 5 秒,结束后回调。 */ const LightningEffect = (() => { /** * 递归绘制闪电路径(分裂算法) * * @param {CanvasRenderingContext2D} ctx * @param {number} x1 起点 x * @param {number} y1 起点 y * @param {number} x2 终点 x * @param {number} y2 终点 y * @param {number} depth 当前递归深度(控制分叉层数) * @param {number} width 线条宽度 */ function _drawBolt(ctx, x1, y1, x2, y2, depth, width) { if (depth <= 0) return; // 中点随机偏移(越深层偏移越小,产生流畅感) const mx = (x1 + x2) / 2 + (Math.random() - 0.5) * 80 * depth; const my = (y1 + y2) / 2 + (Math.random() - 0.5) * 20 * depth; const glow = ctx.createLinearGradient(x1, y1, x2, y2); glow.addColorStop(0, "rgba(200, 220, 255, 0.9)"); glow.addColorStop(1, "rgba(150, 180, 255, 0.6)"); ctx.save(); ctx.strokeStyle = glow; ctx.lineWidth = width; ctx.shadowColor = "#aaccff"; ctx.shadowBlur = 18; ctx.beginPath(); ctx.moveTo(x1, y1); ctx.quadraticCurveTo(mx, my, x2, y2); ctx.stroke(); ctx.restore(); // 递归绘制两段子路径 _drawBolt(ctx, x1, y1, mx, my, depth - 1, width * 0.65); _drawBolt(ctx, mx, my, x2, y2, depth - 1, width * 0.65); // 随机在中途分叉一条小支路(50% 概率) if (depth > 1 && Math.random() > 0.5) { const bx = mx + (Math.random() - 0.5) * 120; const by = my + Math.random() * 80 + 40; _drawBolt(ctx, mx, my, bx, by, depth - 2, width * 0.4); } } /** * 渲染一次闪电 + 闪屏效果 * * @param {HTMLCanvasElement} canvas * @param {CanvasRenderingContext2D} ctx */ function _flash(canvas, ctx) { const w = canvas.width; const h = canvas.height; // 清空画布 ctx.clearRect(0, 0, w, h); // 闪屏:全屏短暂泛白 ctx.fillStyle = "rgba(220, 235, 255, 0.55)"; ctx.fillRect(0, 0, w, h); // 绘制 1-3 条主闪电(从随机顶部位置向下延伸) const boltCount = Math.floor(Math.random() * 2) + 1; for (let i = 0; i < boltCount; i++) { const x1 = w * (0.2 + Math.random() * 0.6); const y1 = 0; const x2 = x1 + (Math.random() - 0.5) * 300; const y2 = h * (0.5 + Math.random() * 0.4); _drawBolt(ctx, x1, y1, x2, y2, 4, 3); } // 50ms 后让画布逐渐消退(模拟闪电短促感) setTimeout(() => { ctx.clearRect(0, 0, w, h); }, 80); } /** * 启动雷电特效 * * @param {HTMLCanvasElement} canvas 全屏 Canvas * @param {Function} onEnd 特效结束回调 */ function start(canvas, onEnd) { const ctx = canvas.getContext("2d"); const FLASHES = 10; // 总闪电次数(增加密度) const DURATION = 7000; // 总时长(ms,相应延长) let count = 0; // 间隔不规则触发多次闪电(模拟真实雷电节奏) function nextFlash() { if (count >= FLASHES) { // 全部闪完,结束特效 setTimeout(() => { ctx.clearRect(0, 0, canvas.width, canvas.height); onEnd(); }, 400); return; } _flash(canvas, ctx); count++; // 下次闪电间隔:400ms ~ 800ms 之间随机(更频繁) const delay = 400 + Math.random() * 400; setTimeout(nextFlash, delay); } // 短暂延迟后开始第一次闪电 setTimeout(nextFlash, 300); // 安全兜底:超时强制结束 setTimeout(() => { ctx.clearRect(0, 0, canvas.width, canvas.height); onEnd(); }, DURATION + 500); } return { start }; })();