231 lines
5.3 KiB
JavaScript
231 lines
5.3 KiB
JavaScript
const MAX_RINGS = 40;
|
|
const MAX_GLOWS = 60;
|
|
const MAX_STREAKS = 90;
|
|
const MAX_ARCS = 28;
|
|
|
|
const easeOut = (t) => 1 - (1 - t) * (1 - t);
|
|
|
|
export class Effects {
|
|
constructor() {
|
|
this.rings = [];
|
|
this.glows = [];
|
|
this.streaks = [];
|
|
this.arcs = [];
|
|
}
|
|
|
|
ring(x, y, {
|
|
endRadius = 140,
|
|
width = 5,
|
|
life = 0.5,
|
|
color = [1, 1, 1, 1],
|
|
additive = true,
|
|
} = {}) {
|
|
if (this.rings.length >= MAX_RINGS) this.rings.shift();
|
|
this.rings.push({
|
|
x, y,
|
|
radius: endRadius * 0.12,
|
|
endRadius,
|
|
width,
|
|
life,
|
|
maxLife: life,
|
|
color,
|
|
additive,
|
|
});
|
|
}
|
|
|
|
glow(x, y, {
|
|
radius = 90,
|
|
life = 0.35,
|
|
color = [1, 1, 1, 1],
|
|
additive = true,
|
|
} = {}) {
|
|
if (this.glows.length >= MAX_GLOWS) this.glows.shift();
|
|
this.glows.push({
|
|
x, y,
|
|
radius: radius * 0.2,
|
|
endRadius: radius,
|
|
life,
|
|
maxLife: life,
|
|
color,
|
|
additive,
|
|
});
|
|
}
|
|
|
|
streak(x, y, angle, length, {
|
|
width = 3,
|
|
life = 0.25,
|
|
fade = true,
|
|
color = [1, 1, 1, 1],
|
|
additive = true,
|
|
tail = 0.7,
|
|
} = {}) {
|
|
if (this.streaks.length >= MAX_STREAKS) this.streaks.shift();
|
|
this.streaks.push({
|
|
x: x - Math.cos(angle) * length * tail,
|
|
y: y - Math.sin(angle) * length * tail,
|
|
angle,
|
|
length,
|
|
width,
|
|
life,
|
|
maxLife: life,
|
|
fade,
|
|
color,
|
|
additive,
|
|
tail,
|
|
});
|
|
}
|
|
|
|
arc(x1, y1, x2, y2, {
|
|
segments = 8,
|
|
width = 3,
|
|
life = 0.3,
|
|
color = [1, 1, 1, 1],
|
|
jitter = 26,
|
|
additive = true,
|
|
} = {}) {
|
|
if (this.arcs.length >= MAX_ARCS) this.arcs.shift();
|
|
this.arcs.push({
|
|
x1, y1, x2, y2,
|
|
segments,
|
|
width,
|
|
life,
|
|
maxLife: life,
|
|
color,
|
|
jitter,
|
|
additive,
|
|
seed: Math.random() * 100,
|
|
});
|
|
}
|
|
|
|
update(dt) {
|
|
const tick = (list) => {
|
|
for (let i = list.length - 1; i >= 0; i -= 1) {
|
|
list[i].life -= dt;
|
|
if (list[i].life <= 0) list.splice(i, 1);
|
|
}
|
|
};
|
|
tick(this.rings);
|
|
tick(this.glows);
|
|
tick(this.streaks);
|
|
tick(this.arcs);
|
|
}
|
|
|
|
draw(renderer) {
|
|
for (const list of [this.glows, this.rings, this.streaks, this.arcs]) {
|
|
const additive = list.filter((effect) => effect.additive);
|
|
const normal = list.filter((effect) => !effect.additive);
|
|
|
|
if (additive.length > 0) renderer.setBlendMode('additive');
|
|
for (const effect of additive) this._drawEffect(renderer, effect);
|
|
if (additive.length > 0) renderer.setBlendMode('normal');
|
|
|
|
for (const effect of normal) this._drawEffect(renderer, effect);
|
|
}
|
|
}
|
|
|
|
_drawEffect(renderer, effect) {
|
|
const t = 1 - effect.life / effect.maxLife;
|
|
const alpha = effect.life / effect.maxLife;
|
|
|
|
if (effect.x1 !== undefined) {
|
|
this._drawArc(renderer, effect, t, alpha);
|
|
} else if (effect.angle !== undefined) {
|
|
this._drawStreak(renderer, effect, t, alpha);
|
|
} else if (effect.width !== undefined) {
|
|
this._drawRing(renderer, effect, t, alpha);
|
|
} else {
|
|
this._drawGlow(renderer, effect, t, alpha);
|
|
}
|
|
}
|
|
|
|
_drawGlow(renderer, effect, t, alpha) {
|
|
const radius = effect.radius + (effect.endRadius - effect.radius) * easeOut(t);
|
|
renderer.drawTexture(
|
|
renderer.glowTexture,
|
|
effect.x - radius,
|
|
effect.y - radius,
|
|
radius * 2,
|
|
radius * 2,
|
|
[
|
|
effect.color[0],
|
|
effect.color[1],
|
|
effect.color[2],
|
|
effect.color[3] * alpha,
|
|
],
|
|
);
|
|
}
|
|
|
|
_drawRing(renderer, effect, t, alpha) {
|
|
const radius = effect.radius + (effect.endRadius - effect.radius) * easeOut(t);
|
|
const fade = Math.min(1, alpha * 1.3);
|
|
renderer.drawTexture(
|
|
renderer.ringTexture,
|
|
effect.x - radius,
|
|
effect.y - radius,
|
|
radius * 2,
|
|
radius * 2,
|
|
[
|
|
effect.color[0],
|
|
effect.color[1],
|
|
effect.color[2],
|
|
Math.max(0, effect.color[3] * fade),
|
|
],
|
|
);
|
|
}
|
|
|
|
_drawStreak(renderer, effect, t, alpha) {
|
|
const progress = easeOut(t);
|
|
const len = effect.length * (1 - progress * (1 - effect.tail));
|
|
const fade = effect.fade ? alpha : 1;
|
|
renderer.drawTexture(
|
|
renderer.glowTexture,
|
|
effect.x,
|
|
effect.y,
|
|
len,
|
|
effect.width,
|
|
[
|
|
effect.color[0],
|
|
effect.color[1],
|
|
effect.color[2],
|
|
effect.color[3] * fade,
|
|
],
|
|
effect.angle,
|
|
);
|
|
}
|
|
|
|
_drawArc(renderer, effect, t, alpha) {
|
|
const { x1, y1, x2, y2, segments, width, jitter, seed, color } = effect;
|
|
const wobble = 0.45 + 0.55 * Math.abs(Math.sin(seed + t * 34));
|
|
const amp = jitter * alpha * wobble;
|
|
|
|
let prevX = x1;
|
|
let prevY = y1;
|
|
for (let i = 1; i <= segments; i += 1) {
|
|
const u = i / segments;
|
|
const edge = (i === segments) ? 0 : 1;
|
|
const px = x1 + (x2 - x1) * u + (Math.random() - 0.5) * 2 * amp * edge;
|
|
const py = y1 + (y2 - y1) * u + (Math.random() - 0.5) * 2 * amp * edge;
|
|
const len = Math.hypot(px - prevX, py - prevY);
|
|
if (len > 0) {
|
|
renderer.drawTexture(
|
|
renderer.whiteTexture,
|
|
prevX,
|
|
prevY,
|
|
len,
|
|
width,
|
|
[color[0], color[1], color[2], color[3] * alpha],
|
|
Math.atan2(py - prevY, px - prevX),
|
|
);
|
|
}
|
|
prevX = px;
|
|
prevY = py;
|
|
}
|
|
}
|
|
|
|
clear() {
|
|
this.rings.length = 0;
|
|
this.glows.length = 0;
|
|
this.streaks.length = 0;
|
|
this.arcs.length = 0;
|
|
}
|
|
} |