Add new chat effects and shop items
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
/**
|
||||
* 文件功能:聊天室雷电特效
|
||||
*
|
||||
* 使用递归分叉算法在 Canvas 上绘制真实感闪电路径,
|
||||
* 配合全屏闪白效果模拟雷闪。总持续约 5 秒,结束后回调。
|
||||
* 使用递归分叉算法叠加云层闪光、主闪电、余辉残影,
|
||||
* 在聊天室中模拟更有压迫感的雷暴闪电效果。
|
||||
*/
|
||||
|
||||
const LightningEffect = (() => {
|
||||
@@ -51,10 +51,41 @@ const LightningEffect = (() => {
|
||||
}
|
||||
|
||||
/**
|
||||
* 渲染一次闪电 + 闪屏效果
|
||||
* 绘制顶部乌云压光层,让闪电更有“雷暴”氛围。
|
||||
*
|
||||
* @param {HTMLCanvasElement} canvas
|
||||
* @param {CanvasRenderingContext2D} ctx
|
||||
* @param {HTMLCanvasElement} canvas
|
||||
* @param {CanvasRenderingContext2D} ctx
|
||||
*/
|
||||
function _drawStormGlow(canvas, ctx) {
|
||||
const w = canvas.width;
|
||||
const h = canvas.height;
|
||||
const sky = ctx.createLinearGradient(0, 0, 0, h * 0.8);
|
||||
sky.addColorStop(0, "rgba(7, 18, 38, 0.34)");
|
||||
sky.addColorStop(0.45, "rgba(15, 23, 42, 0.18)");
|
||||
sky.addColorStop(1, "rgba(15, 23, 42, 0)");
|
||||
ctx.fillStyle = sky;
|
||||
ctx.fillRect(0, 0, w, h);
|
||||
|
||||
for (let i = 0; i < 3; i++) {
|
||||
const cloudX = w * (0.12 + Math.random() * 0.76);
|
||||
const cloudY = h * (0.05 + Math.random() * 0.22);
|
||||
const cloudR = 120 + Math.random() * 160;
|
||||
const cloud = ctx.createRadialGradient(cloudX, cloudY, 0, cloudX, cloudY, cloudR);
|
||||
cloud.addColorStop(0, "rgba(210, 226, 255, 0.18)");
|
||||
cloud.addColorStop(0.38, "rgba(168, 196, 255, 0.1)");
|
||||
cloud.addColorStop(1, "rgba(168, 196, 255, 0)");
|
||||
ctx.fillStyle = cloud;
|
||||
ctx.beginPath();
|
||||
ctx.arc(cloudX, cloudY, cloudR, 0, Math.PI * 2);
|
||||
ctx.fill();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 渲染一次闪电 + 闪屏效果。
|
||||
*
|
||||
* @param {HTMLCanvasElement} canvas
|
||||
* @param {CanvasRenderingContext2D} ctx
|
||||
*/
|
||||
function _flash(canvas, ctx) {
|
||||
const w = canvas.width;
|
||||
@@ -63,24 +94,44 @@ const LightningEffect = (() => {
|
||||
// 清空画布
|
||||
ctx.clearRect(0, 0, w, h);
|
||||
|
||||
// 闪屏:全屏短暂泛白
|
||||
ctx.fillStyle = "rgba(220, 235, 255, 0.55)";
|
||||
// 先铺一层压暗天空,再叠加闪白,让明暗反差更明显。
|
||||
_drawStormGlow(canvas, ctx);
|
||||
ctx.fillStyle = "rgba(228, 239, 255, 0.46)";
|
||||
ctx.fillRect(0, 0, w, h);
|
||||
|
||||
// 绘制 1-3 条主闪电(从随机顶部位置向下延伸)
|
||||
const boltCount = Math.floor(Math.random() * 2) + 1;
|
||||
// 绘制 1-3 条主闪电,并给出明显的白色核心线。
|
||||
const boltCount = Math.floor(Math.random() * 3) + 1;
|
||||
for (let i = 0; i < boltCount; i++) {
|
||||
const x1 = w * (0.2 + Math.random() * 0.6);
|
||||
const x1 = w * (0.12 + Math.random() * 0.76);
|
||||
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);
|
||||
const x2 = x1 + (Math.random() - 0.5) * 360;
|
||||
const y2 = h * (0.55 + Math.random() * 0.35);
|
||||
const width = 3.6 + Math.random() * 1.8;
|
||||
_drawBolt(ctx, x1, y1, x2, y2, 5, width);
|
||||
|
||||
ctx.save();
|
||||
ctx.strokeStyle = "rgba(255,255,255,0.92)";
|
||||
ctx.lineWidth = Math.max(1.2, width * 0.34);
|
||||
ctx.shadowColor = "rgba(255,255,255,0.95)";
|
||||
ctx.shadowBlur = 22;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(x1, y1);
|
||||
ctx.lineTo(x2, y2);
|
||||
ctx.stroke();
|
||||
ctx.restore();
|
||||
}
|
||||
|
||||
// 50ms 后让画布逐渐消退(模拟闪电短促感)
|
||||
// 短促残影:闪电消失后保留一层很淡的余辉,避免“闪一下就没了”。
|
||||
setTimeout(() => {
|
||||
ctx.clearRect(0, 0, w, h);
|
||||
}, 80);
|
||||
_drawStormGlow(canvas, ctx);
|
||||
ctx.fillStyle = "rgba(185, 205, 255, 0.12)";
|
||||
ctx.fillRect(0, 0, w, h);
|
||||
}, 90);
|
||||
|
||||
setTimeout(() => {
|
||||
ctx.clearRect(0, 0, w, h);
|
||||
}, 190);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -91,24 +142,40 @@ const LightningEffect = (() => {
|
||||
*/
|
||||
function start(canvas, onEnd) {
|
||||
const ctx = canvas.getContext("2d");
|
||||
const FLASHES = 10; // 总闪电次数(增加密度)
|
||||
const DURATION = 7000; // 总时长(ms,相应延长)
|
||||
const FLASHES = 9;
|
||||
const DURATION = 7600;
|
||||
let count = 0;
|
||||
let finished = false;
|
||||
|
||||
/**
|
||||
* 统一结束特效,避免多次触发 onEnd。
|
||||
*/
|
||||
function finish() {
|
||||
if (finished) {
|
||||
return;
|
||||
}
|
||||
|
||||
finished = true;
|
||||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||
onEnd();
|
||||
}
|
||||
|
||||
// 间隔不规则触发多次闪电(模拟真实雷电节奏)
|
||||
function nextFlash() {
|
||||
if (count >= FLASHES) {
|
||||
// 全部闪完,结束特效
|
||||
setTimeout(() => {
|
||||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||
onEnd();
|
||||
}, 400);
|
||||
finish();
|
||||
}, 520);
|
||||
return;
|
||||
}
|
||||
|
||||
_flash(canvas, ctx);
|
||||
count++;
|
||||
// 下次闪电间隔:400ms ~ 800ms 之间随机(更频繁)
|
||||
const delay = 400 + Math.random() * 400;
|
||||
|
||||
// 让雷电节奏有“成组爆发”的感觉:有时连续两下,有时间隔更久。
|
||||
const delay = Math.random() > 0.65
|
||||
? 140 + Math.random() * 140
|
||||
: 420 + Math.random() * 520;
|
||||
setTimeout(nextFlash, delay);
|
||||
}
|
||||
|
||||
@@ -117,8 +184,7 @@ const LightningEffect = (() => {
|
||||
|
||||
// 安全兜底:超时强制结束
|
||||
setTimeout(() => {
|
||||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||
onEnd();
|
||||
finish();
|
||||
}, DURATION + 500);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user