Files
chatroom/public/js/effects/lightning.js

127 lines
4.2 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 文件功能:聊天室雷电特效
*
* 使用递归分叉算法在 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 = 5; // 总闪电次数
const DURATION = 5000; // 总时长ms
let count = 0;
// 间隔不规则触发多次闪电(模拟真实雷电节奏)
function nextFlash() {
if (count >= FLASHES) {
// 全部闪完,结束特效
setTimeout(() => {
ctx.clearRect(0, 0, canvas.width, canvas.height);
onEnd();
}, 500);
return;
}
_flash(canvas, ctx);
count++;
// 下次闪电间隔800ms ~ 1200ms 之间随机
const delay = 700 + Math.random() * 500;
setTimeout(nextFlash, delay);
}
// 短暂延迟后开始第一次闪电
setTimeout(nextFlash, 300);
// 安全兜底:超时强制结束
setTimeout(() => {
ctx.clearRect(0, 0, canvas.width, canvas.height);
onEnd();
}, DURATION + 500);
}
return { start };
})();