收口聊天室安全边界并优化特效生命周期

This commit is contained in:
2026-04-25 02:52:30 +08:00
parent 4d3f4f7a4b
commit 855d031b04
26 changed files with 1219 additions and 175 deletions
+70 -15
View File
@@ -358,6 +358,22 @@ const FireworksEffect = (() => {
}
}
/**
* 在粒子预算内追加粒子,避免主爆炸阶段瞬间超量。
*
* @param {Particle[]} target
* @param {Particle[]} incoming
* @param {number} budget
*/
function _appendParticlesWithinBudget(target, incoming, budget) {
const remaining = Math.max(0, budget - target.length);
if (remaining <= 0) {
return;
}
_appendParticles(target, incoming.slice(0, remaining));
}
/**
* 统一发射一枚火箭。
*
@@ -388,9 +404,14 @@ const FireworksEffect = (() => {
const ctx = canvas.getContext("2d");
const w = canvas.width;
const h = canvas.height;
const isMobile = window.matchMedia?.("(max-width: 640px)")?.matches || window.innerWidth <= 640;
const mobileScale = isMobile ? 0.72 : 1;
const duration = config.duration;
const hardStopAt = duration + 2600;
const peakParticleBudget = config.peakParticleBudget ?? 1650;
const peakParticleBudget = Math.round((config.peakParticleBudget ?? 1650) * mobileScale);
const maxLaunches = Math.max(8, Math.round(config.maxLaunches * mobileScale));
const particleDensity = config.particleDensity * mobileScale;
const secondaryDensity = config.secondaryDensity * mobileScale;
let rockets = [];
let particles = [];
@@ -398,15 +419,17 @@ const FireworksEffect = (() => {
let scheduledBursts = [];
let animId = null;
let launchCnt = 0;
let finished = false;
const timers = [];
const launchInterval = setInterval(() => {
if (launchCnt >= config.maxLaunches) {
if (launchCnt >= maxLaunches) {
clearInterval(launchInterval);
return;
}
const batchSize = config.getBatchSize(launchCnt);
for (let i = 0; i < batchSize && launchCnt < config.maxLaunches; i++) {
for (let i = 0; i < batchSize && launchCnt < maxLaunches; i++) {
_launchRocket(
rockets,
w,
@@ -421,9 +444,9 @@ const FireworksEffect = (() => {
// 开场礼炮先把气氛撑起来,避免一开始太空。
if (typeof config.openingVolley === "function") {
setTimeout(() => {
timers.push(setTimeout(() => {
config.openingVolley(rockets, w, h);
}, 120);
}, 120));
}
const startTime = performance.now();
@@ -440,9 +463,10 @@ const FireworksEffect = (() => {
for (let i = scheduledBursts.length - 1; i >= 0; i--) {
if (scheduledBursts[i].triggerAt <= now) {
const burst = scheduledBursts[i];
_appendParticles(
_appendParticlesWithinBudget(
particles,
_burst(burst.x, burst.y, burst.color, burst.type, burst.density),
peakParticleBudget,
);
halos.push(new Halo(burst.x, burst.y, burst.color, burst.haloRadius));
scheduledBursts.splice(i, 1);
@@ -452,15 +476,16 @@ const FireworksEffect = (() => {
for (let i = rockets.length - 1; i >= 0; i--) {
const rocket = rockets[i];
if (rocket.done) {
_appendParticles(
_appendParticlesWithinBudget(
particles,
_burst(
rocket.x,
rocket.y,
rocket.color,
rocket.type,
config.particleDensity,
particleDensity,
),
peakParticleBudget,
);
halos.push(new Halo(rocket.x, rocket.y, rocket.color, config.primaryHaloRadius));
@@ -475,7 +500,7 @@ const FireworksEffect = (() => {
y: rocket.y + (Math.random() - 0.5) * 26,
color: _pick(config.colors),
type: Math.random() > 0.5 ? "sphere" : "ring",
density: config.secondaryDensity,
density: secondaryDensity,
haloRadius: config.secondaryHaloRadius,
});
}
@@ -502,14 +527,44 @@ const FireworksEffect = (() => {
if (shouldContinue && elapsed < hardStopAt) {
animId = requestAnimationFrame(animate);
} else {
clearInterval(launchInterval);
cancelAnimationFrame(animId);
ctx.clearRect(0, 0, w, h);
onEnd();
finish(false);
}
}
animId = requestAnimationFrame(animate);
/**
* 统一结束烟花演出,取消时不再回调管理器。
*
* @param {boolean} canceled 是否为手动取消
*/
function finish(canceled) {
if (finished) {
return;
}
finished = true;
clearInterval(launchInterval);
timers.forEach((timer) => clearTimeout(timer));
if (animId) {
cancelAnimationFrame(animId);
}
rockets = [];
particles = [];
halos = [];
scheduledBursts = [];
ctx.clearRect(0, 0, w, h);
if (!canceled) {
onEnd();
}
}
return {
cancel() {
finish(true);
},
};
}
/**
@@ -519,7 +574,7 @@ const FireworksEffect = (() => {
* @param {Function} onEnd 特效结束回调
*/
function start(canvas, onEnd) {
_runShow(canvas, onEnd, {
return _runShow(canvas, onEnd, {
duration: 10500,
launchEvery: 340,
maxLaunches: 24,
@@ -577,7 +632,7 @@ const FireworksEffect = (() => {
"#00ddff", // 其他
];
_runShow(canvas, onEnd, {
return _runShow(canvas, onEnd, {
duration: 12400,
launchEvery: 280,
maxLaunches: 34,