@basmilius/sparkle 2.1.0 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +317 -459
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +1258 -949
- package/dist/index.mjs.map +1 -1
- package/package.json +6 -2
- package/src/aurora/index.ts +9 -3
- package/src/aurora/layer.ts +57 -29
- package/src/balloons/index.ts +9 -3
- package/src/balloons/layer.ts +50 -19
- package/src/bubbles/index.ts +9 -3
- package/src/bubbles/layer.ts +30 -17
- package/src/canvas.ts +92 -2
- package/src/color.ts +11 -2
- package/src/confetti/index.ts +15 -3
- package/src/confetti/layer.ts +8 -5
- package/src/confetti/particle.ts +12 -11
- package/src/confetti/shapes.ts +84 -97
- package/src/donuts/consts.ts +2 -2
- package/src/donuts/index.ts +9 -3
- package/src/donuts/layer.ts +43 -12
- package/src/effect.ts +107 -0
- package/src/fade.ts +87 -0
- package/src/fireflies/index.ts +9 -3
- package/src/fireflies/layer.ts +26 -9
- package/src/fireflies/particle.ts +2 -2
- package/src/firepit/index.ts +9 -3
- package/src/firepit/layer.ts +26 -7
- package/src/fireworks/create-explosion.ts +237 -0
- package/src/fireworks/explosion.ts +1 -1
- package/src/fireworks/index.ts +15 -3
- package/src/fireworks/layer.ts +55 -304
- package/src/fireworks/spark.ts +2 -2
- package/src/fireworks/types.ts +2 -2
- package/src/glitter/index.ts +9 -4
- package/src/glitter/layer.ts +15 -7
- package/src/glitter/types.ts +10 -0
- package/src/index.ts +3 -4
- package/src/lanterns/index.ts +9 -4
- package/src/lanterns/layer.ts +22 -10
- package/src/lanterns/types.ts +8 -0
- package/src/layer.ts +13 -11
- package/src/leaves/index.ts +9 -4
- package/src/leaves/layer.ts +21 -14
- package/src/leaves/types.ts +9 -0
- package/src/lightning/index.ts +9 -4
- package/src/lightning/layer.ts +4 -4
- package/src/lightning/system.ts +3 -3
- package/src/lightning/types.ts +10 -2
- package/src/matrix/index.ts +9 -4
- package/src/matrix/layer.ts +15 -7
- package/src/matrix/types.ts +9 -0
- package/src/orbits/index.ts +9 -4
- package/src/orbits/layer.ts +51 -21
- package/src/orbits/types.ts +12 -1
- package/src/particles/index.ts +9 -3
- package/src/particles/layer.ts +55 -12
- package/src/petals/index.ts +9 -3
- package/src/petals/layer.ts +29 -13
- package/src/plasma/index.ts +9 -3
- package/src/plasma/layer.ts +21 -6
- package/src/rain/index.ts +9 -3
- package/src/rain/layer.ts +30 -8
- package/src/sandstorm/index.ts +9 -3
- package/src/sandstorm/layer.ts +26 -9
- package/src/scene.ts +204 -0
- package/src/shooting-stars/system.ts +26 -24
- package/src/shooting-stars/types.ts +2 -1
- package/src/simulation-canvas.ts +45 -6
- package/src/snow/index.ts +9 -3
- package/src/snow/layer.ts +24 -11
- package/src/sparklers/index.ts +13 -3
- package/src/sparklers/layer.ts +61 -15
- package/src/stars/index.ts +9 -3
- package/src/stars/layer.ts +28 -22
- package/src/streamers/index.ts +9 -3
- package/src/streamers/layer.ts +18 -6
- package/src/streamers/types.ts +1 -1
- package/src/waves/index.ts +9 -3
- package/src/waves/layer.ts +42 -45
- package/src/waves/types.ts +1 -0
- package/src/wormhole/index.ts +9 -3
- package/src/wormhole/layer.ts +22 -6
- package/src/aurora/simulation.ts +0 -19
- package/src/balloons/simulation.ts +0 -19
- package/src/bubbles/simulation.ts +0 -20
- package/src/confetti/simulation.ts +0 -27
- package/src/donuts/simulation.ts +0 -25
- package/src/fireflies/simulation.ts +0 -18
- package/src/firepit/simulation.ts +0 -17
- package/src/fireworks/simulation.ts +0 -18
- package/src/glitter/simulation.ts +0 -19
- package/src/lanterns/simulation.ts +0 -17
- package/src/layered.ts +0 -185
- package/src/leaves/simulation.ts +0 -18
- package/src/lightning/simulation.ts +0 -17
- package/src/matrix/simulation.ts +0 -18
- package/src/orbits/simulation.ts +0 -19
- package/src/particles/simulation.ts +0 -26
- package/src/petals/simulation.ts +0 -18
- package/src/plasma/simulation.ts +0 -17
- package/src/rain/simulation.ts +0 -21
- package/src/sandstorm/simulation.ts +0 -18
- package/src/snow/simulation.ts +0 -17
- package/src/sparklers/simulation.ts +0 -30
- package/src/stars/simulation.ts +0 -22
- package/src/streamers/simulation.ts +0 -16
- package/src/waves/simulation.ts +0 -18
- package/src/wormhole/simulation.ts +0 -17
package/src/fireworks/layer.ts
CHANGED
|
@@ -1,30 +1,33 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Effect } from '../effect';
|
|
2
2
|
import type { Point } from '../point';
|
|
3
3
|
import { MULBERRY } from './consts';
|
|
4
|
+
import { createExplosion } from './create-explosion';
|
|
4
5
|
import { Explosion } from './explosion';
|
|
5
6
|
import { Firework } from './firework';
|
|
6
7
|
import { Spark } from './spark';
|
|
7
|
-
import {
|
|
8
|
+
import { FIREWORK_VARIANTS, type FireworksConfig, type FireworkVariant } from './types';
|
|
8
9
|
|
|
9
|
-
export class
|
|
10
|
+
export class Fireworks extends Effect<FireworksConfig> {
|
|
10
11
|
#explosions: Explosion[] = [];
|
|
11
12
|
#fireworks: Firework[] = [];
|
|
12
13
|
#sparks: Spark[] = [];
|
|
13
14
|
#hue: number = 120;
|
|
14
15
|
#spawnTimer: number = 0;
|
|
15
16
|
#positionRandom = MULBERRY.fork();
|
|
16
|
-
|
|
17
|
+
#autoSpawn: boolean;
|
|
18
|
+
#variants: FireworkVariant[];
|
|
17
19
|
readonly #baseSize: number;
|
|
18
|
-
|
|
20
|
+
#scale: number;
|
|
19
21
|
readonly #tailWidth: number;
|
|
20
22
|
#width: number = 960;
|
|
21
23
|
#height: number = 540;
|
|
22
24
|
|
|
23
|
-
constructor(config:
|
|
25
|
+
constructor(config: FireworksConfig = {}) {
|
|
24
26
|
super();
|
|
25
27
|
|
|
26
28
|
const scale = config.scale ?? 1;
|
|
27
29
|
this.#autoSpawn = config.autoSpawn ?? true;
|
|
30
|
+
this.#variants = config.variants?.length ? [...config.variants] : [...FIREWORK_VARIANTS];
|
|
28
31
|
this.#baseSize = 5 * scale;
|
|
29
32
|
this.#scale = scale;
|
|
30
33
|
this.#tailWidth = 2 * scale;
|
|
@@ -35,10 +38,22 @@ export class FireworkLayer extends SimulationLayer {
|
|
|
35
38
|
this.#height = height;
|
|
36
39
|
}
|
|
37
40
|
|
|
38
|
-
|
|
41
|
+
launch(variant: FireworkVariant, position?: Point): void {
|
|
39
42
|
const pos = position ?? {x: this.#width / 2, y: this.#height * 0.4};
|
|
40
43
|
this.#hue = MULBERRY.nextBetween(0, 360);
|
|
41
|
-
this.#
|
|
44
|
+
this.#spawnExplosion(pos, this.#hue, variant);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
configure(config: Partial<FireworksConfig>): void {
|
|
48
|
+
if (config.scale !== undefined) {
|
|
49
|
+
this.#scale = config.scale;
|
|
50
|
+
}
|
|
51
|
+
if (config.autoSpawn !== undefined) {
|
|
52
|
+
this.#autoSpawn = config.autoSpawn;
|
|
53
|
+
}
|
|
54
|
+
if (Array.isArray(config.variants) && config.variants.length > 0) {
|
|
55
|
+
this.#variants = [...config.variants];
|
|
56
|
+
}
|
|
42
57
|
}
|
|
43
58
|
|
|
44
59
|
tick(dt: number, width: number, height: number): void {
|
|
@@ -61,7 +76,10 @@ export class FireworkLayer extends SimulationLayer {
|
|
|
61
76
|
|
|
62
77
|
for (const firework of this.#fireworks) {
|
|
63
78
|
firework.tick(dt);
|
|
64
|
-
|
|
79
|
+
const collected = firework.collectSparks();
|
|
80
|
+
for (let i = 0; i < collected.length; i++) {
|
|
81
|
+
this.#sparks.push(collected[i]);
|
|
82
|
+
}
|
|
65
83
|
}
|
|
66
84
|
|
|
67
85
|
for (const explosion of this.#explosions) {
|
|
@@ -93,10 +111,15 @@ export class FireworkLayer extends SimulationLayer {
|
|
|
93
111
|
}
|
|
94
112
|
|
|
95
113
|
if (explosion.checkCrackle()) {
|
|
96
|
-
for (let j = 0; j <
|
|
114
|
+
for (let j = 0; j < 14; j++) {
|
|
115
|
+
const angle = MULBERRY.nextBetween(0, Math.PI * 2);
|
|
116
|
+
const speed = MULBERRY.nextBetween(3, 8);
|
|
117
|
+
|
|
97
118
|
newSparks.push(new Spark(
|
|
98
119
|
explosion.position,
|
|
99
|
-
explosion.hue + MULBERRY.nextBetween(-30, 30)
|
|
120
|
+
explosion.hue + MULBERRY.nextBetween(-30, 30),
|
|
121
|
+
Math.cos(angle) * speed,
|
|
122
|
+
Math.sin(angle) * speed
|
|
100
123
|
));
|
|
101
124
|
}
|
|
102
125
|
}
|
|
@@ -105,8 +128,21 @@ export class FireworkLayer extends SimulationLayer {
|
|
|
105
128
|
this.#explosions.push(...newExplosions);
|
|
106
129
|
this.#sparks.push(...newSparks);
|
|
107
130
|
|
|
108
|
-
|
|
109
|
-
|
|
131
|
+
let aliveExplosions = 0;
|
|
132
|
+
for (let i = 0; i < this.#explosions.length; i++) {
|
|
133
|
+
if (!this.#explosions[i].isDead) {
|
|
134
|
+
this.#explosions[aliveExplosions++] = this.#explosions[i];
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
this.#explosions.length = aliveExplosions;
|
|
138
|
+
|
|
139
|
+
let aliveSparks = 0;
|
|
140
|
+
for (let i = 0; i < this.#sparks.length; i++) {
|
|
141
|
+
if (!this.#sparks[i].isDead) {
|
|
142
|
+
this.#sparks[aliveSparks++] = this.#sparks[i];
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
this.#sparks.length = aliveSparks;
|
|
110
146
|
}
|
|
111
147
|
|
|
112
148
|
draw(ctx: CanvasRenderingContext2D, width: number, height: number): void {
|
|
@@ -132,278 +168,10 @@ export class FireworkLayer extends SimulationLayer {
|
|
|
132
168
|
ctx.globalCompositeOperation = 'source-over';
|
|
133
169
|
}
|
|
134
170
|
|
|
135
|
-
#
|
|
171
|
+
#spawnExplosion(position: Point, hue: number, variant?: FireworkVariant): void {
|
|
136
172
|
const selected = variant ?? this.#pickVariant();
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
this.#createSaturnExplosion(position, hue);
|
|
140
|
-
return;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
if (selected === 'dahlia') {
|
|
144
|
-
this.#createDahliaExplosion(position, hue);
|
|
145
|
-
return;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
if (selected === 'heart') {
|
|
149
|
-
this.#createHeartExplosion(position, hue);
|
|
150
|
-
return;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
if (selected === 'spiral') {
|
|
154
|
-
this.#createSpiralExplosion(position, hue);
|
|
155
|
-
return;
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
if (selected === 'flower') {
|
|
159
|
-
this.#createFlowerExplosion(position, hue);
|
|
160
|
-
return;
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
if (selected === 'concentric') {
|
|
164
|
-
this.#createConcentricExplosion(position, hue);
|
|
165
|
-
return;
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
const type: ExplosionType = selected;
|
|
169
|
-
const config = EXPLOSION_CONFIGS[type];
|
|
170
|
-
const particleCount = Math.floor(MULBERRY.nextBetween(config.particleCount[0], config.particleCount[1]));
|
|
171
|
-
|
|
172
|
-
const effectiveHue = type === 'brocade'
|
|
173
|
-
? MULBERRY.nextBetween(35, 50)
|
|
174
|
-
: hue;
|
|
175
|
-
|
|
176
|
-
for (let i = 0; i < particleCount; i++) {
|
|
177
|
-
let angle: number | undefined;
|
|
178
|
-
let speed: number | undefined;
|
|
179
|
-
|
|
180
|
-
if (type === 'ring') {
|
|
181
|
-
angle = (i / particleCount) * Math.PI * 2;
|
|
182
|
-
speed = MULBERRY.nextBetween(config.speed[0], config.speed[1]) * 0.5 + config.speed[0] * 0.5;
|
|
183
|
-
} else if (type === 'palm' || type === 'horsetail') {
|
|
184
|
-
const spread = type === 'horsetail' ? Math.PI / 8 : Math.PI / 5;
|
|
185
|
-
angle = -Math.PI / 2 + MULBERRY.nextBetween(-spread, spread);
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
this.#explosions.push(new Explosion(position, effectiveHue, this.#baseSize, type, this.#scale, angle, speed));
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
#createSaturnExplosion(position: Point, hue: number): void {
|
|
193
|
-
const velocity = MULBERRY.nextBetween(4, 6);
|
|
194
|
-
const shellCount = Math.floor(MULBERRY.nextBetween(25, 35));
|
|
195
|
-
|
|
196
|
-
for (let i = 0; i < shellCount; i++) {
|
|
197
|
-
const rad = (i / shellCount) * Math.PI * 2;
|
|
198
|
-
|
|
199
|
-
this.#explosions.push(new Explosion(
|
|
200
|
-
position,
|
|
201
|
-
hue,
|
|
202
|
-
this.#baseSize,
|
|
203
|
-
'peony',
|
|
204
|
-
this.#scale,
|
|
205
|
-
rad + MULBERRY.nextBetween(-0.05, 0.05),
|
|
206
|
-
velocity + MULBERRY.nextBetween(-0.25, 0.25)
|
|
207
|
-
));
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
const fillCount = Math.floor(MULBERRY.nextBetween(40, 60));
|
|
211
|
-
|
|
212
|
-
for (let i = 0; i < fillCount; i++) {
|
|
213
|
-
const rad = MULBERRY.nextBetween(0, Math.PI * 2);
|
|
214
|
-
const speed = velocity * MULBERRY.nextBetween(0, 1);
|
|
215
|
-
|
|
216
|
-
this.#explosions.push(new Explosion(
|
|
217
|
-
position,
|
|
218
|
-
hue,
|
|
219
|
-
this.#baseSize,
|
|
220
|
-
'peony',
|
|
221
|
-
this.#scale,
|
|
222
|
-
rad,
|
|
223
|
-
speed
|
|
224
|
-
));
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
const ringRotation = MULBERRY.nextBetween(0, Math.PI * 2);
|
|
228
|
-
const ringCount = Math.floor(MULBERRY.nextBetween(40, 55));
|
|
229
|
-
const ringVx = velocity * MULBERRY.nextBetween(2, 3);
|
|
230
|
-
const ringVy = velocity * 0.6;
|
|
231
|
-
|
|
232
|
-
for (let i = 0; i < ringCount; i++) {
|
|
233
|
-
const rad = (i / ringCount) * Math.PI * 2;
|
|
234
|
-
|
|
235
|
-
const cx = Math.cos(rad) * ringVx + MULBERRY.nextBetween(-0.25, 0.25);
|
|
236
|
-
const cy = Math.sin(rad) * ringVy + MULBERRY.nextBetween(-0.25, 0.25);
|
|
237
|
-
|
|
238
|
-
const cosR = Math.cos(ringRotation);
|
|
239
|
-
const sinR = Math.sin(ringRotation);
|
|
240
|
-
const vx = cx * cosR - cy * sinR;
|
|
241
|
-
const vy = cx * sinR + cy * cosR;
|
|
242
|
-
|
|
243
|
-
const screenAngle = Math.atan2(vy, vx);
|
|
244
|
-
const screenSpeed = Math.sqrt(vx * vx + vy * vy);
|
|
245
|
-
const vz = Math.sin(rad) * velocity * 0.8;
|
|
246
|
-
|
|
247
|
-
this.#explosions.push(new Explosion(
|
|
248
|
-
position,
|
|
249
|
-
hue + 60,
|
|
250
|
-
this.#baseSize,
|
|
251
|
-
'ring',
|
|
252
|
-
this.#scale,
|
|
253
|
-
screenAngle,
|
|
254
|
-
screenSpeed,
|
|
255
|
-
vz
|
|
256
|
-
));
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
#createDahliaExplosion(position: Point, hue: number): void {
|
|
261
|
-
const petalCount = Math.floor(MULBERRY.nextBetween(6, 9));
|
|
262
|
-
const particlesPerPetal = Math.floor(MULBERRY.nextBetween(8, 12));
|
|
263
|
-
|
|
264
|
-
for (let petal = 0; petal < petalCount; petal++) {
|
|
265
|
-
const baseAngle = (petal / petalCount) * Math.PI * 2;
|
|
266
|
-
const petalHue = hue + (petal % 2 === 0 ? 25 : -25);
|
|
267
|
-
|
|
268
|
-
for (let i = 0; i < particlesPerPetal; i++) {
|
|
269
|
-
const angle = baseAngle + MULBERRY.nextBetween(-0.3, 0.3);
|
|
270
|
-
|
|
271
|
-
this.#explosions.push(new Explosion(
|
|
272
|
-
position,
|
|
273
|
-
petalHue,
|
|
274
|
-
this.#baseSize,
|
|
275
|
-
'dahlia',
|
|
276
|
-
this.#scale,
|
|
277
|
-
angle
|
|
278
|
-
));
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
#createHeartExplosion(position: Point, hue: number): void {
|
|
284
|
-
const velocity = MULBERRY.nextBetween(3, 5);
|
|
285
|
-
const count = Math.floor(MULBERRY.nextBetween(60, 80));
|
|
286
|
-
const rotation = MULBERRY.nextBetween(-0.3, 0.3);
|
|
287
|
-
|
|
288
|
-
for (let i = 0; i < count; i++) {
|
|
289
|
-
const t = (i / count) * Math.PI * 2;
|
|
290
|
-
|
|
291
|
-
const hx = 16 * Math.pow(Math.sin(t), 3);
|
|
292
|
-
const hy = -(13 * Math.cos(t) - 5 * Math.cos(2 * t) - 2 * Math.cos(3 * t) - Math.cos(4 * t));
|
|
293
|
-
|
|
294
|
-
const scale = velocity / 16;
|
|
295
|
-
const vx = hx * scale;
|
|
296
|
-
const vy = hy * scale;
|
|
297
|
-
|
|
298
|
-
const cosR = Math.cos(rotation);
|
|
299
|
-
const sinR = Math.sin(rotation);
|
|
300
|
-
const rvx = vx * cosR - vy * sinR;
|
|
301
|
-
const rvy = vx * sinR + vy * cosR;
|
|
302
|
-
|
|
303
|
-
const angle = Math.atan2(rvy, rvx);
|
|
304
|
-
const speed = Math.sqrt(rvx * rvx + rvy * rvy);
|
|
305
|
-
|
|
306
|
-
this.#explosions.push(new Explosion(
|
|
307
|
-
position,
|
|
308
|
-
hue,
|
|
309
|
-
this.#baseSize,
|
|
310
|
-
'heart',
|
|
311
|
-
this.#scale,
|
|
312
|
-
angle,
|
|
313
|
-
Math.max(0.1, speed + MULBERRY.nextBetween(-0.15, 0.15))
|
|
314
|
-
));
|
|
315
|
-
}
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
#createSpiralExplosion(position: Point, hue: number): void {
|
|
319
|
-
const arms = Math.floor(MULBERRY.nextBetween(3, 5));
|
|
320
|
-
const particlesPerArm = Math.floor(MULBERRY.nextBetween(15, 20));
|
|
321
|
-
const twist = MULBERRY.nextBetween(2, 3.5);
|
|
322
|
-
const baseRotation = MULBERRY.nextBetween(0, Math.PI * 2);
|
|
323
|
-
|
|
324
|
-
for (let arm = 0; arm < arms; arm++) {
|
|
325
|
-
const baseAngle = baseRotation + (arm / arms) * Math.PI * 2;
|
|
326
|
-
const armHue = hue + arm * (360 / arms / 3);
|
|
327
|
-
|
|
328
|
-
for (let i = 0; i < particlesPerArm; i++) {
|
|
329
|
-
const progress = i / particlesPerArm;
|
|
330
|
-
const angle = baseAngle + progress * twist;
|
|
331
|
-
const speed = 2 + progress * 8;
|
|
332
|
-
|
|
333
|
-
this.#explosions.push(new Explosion(
|
|
334
|
-
position,
|
|
335
|
-
armHue,
|
|
336
|
-
this.#baseSize,
|
|
337
|
-
'spiral',
|
|
338
|
-
this.#scale,
|
|
339
|
-
angle,
|
|
340
|
-
speed + MULBERRY.nextBetween(-0.3, 0.3)
|
|
341
|
-
));
|
|
342
|
-
}
|
|
343
|
-
}
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
#createFlowerExplosion(position: Point, hue: number): void {
|
|
347
|
-
const velocity = MULBERRY.nextBetween(4, 7);
|
|
348
|
-
const count = Math.floor(MULBERRY.nextBetween(70, 90));
|
|
349
|
-
const petals = Math.floor(MULBERRY.nextBetween(2, 4));
|
|
350
|
-
const rotation = MULBERRY.nextBetween(0, Math.PI * 2);
|
|
351
|
-
|
|
352
|
-
for (let i = 0; i < count; i++) {
|
|
353
|
-
const t = (i / count) * Math.PI * 2;
|
|
354
|
-
const r = Math.abs(Math.cos(petals * t));
|
|
355
|
-
const speed = velocity * r;
|
|
356
|
-
|
|
357
|
-
if (speed < 0.3) {
|
|
358
|
-
continue;
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
this.#explosions.push(new Explosion(
|
|
362
|
-
position,
|
|
363
|
-
hue + MULBERRY.nextBetween(-15, 15),
|
|
364
|
-
this.#baseSize,
|
|
365
|
-
'flower',
|
|
366
|
-
this.#scale,
|
|
367
|
-
t + rotation,
|
|
368
|
-
speed + MULBERRY.nextBetween(-0.2, 0.2)
|
|
369
|
-
));
|
|
370
|
-
}
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
#createConcentricExplosion(position: Point, hue: number): void {
|
|
374
|
-
const outerCount = Math.floor(MULBERRY.nextBetween(35, 50));
|
|
375
|
-
const outerSpeed = MULBERRY.nextBetween(7, 10);
|
|
376
|
-
|
|
377
|
-
for (let i = 0; i < outerCount; i++) {
|
|
378
|
-
const angle = (i / outerCount) * Math.PI * 2;
|
|
379
|
-
|
|
380
|
-
this.#explosions.push(new Explosion(
|
|
381
|
-
position,
|
|
382
|
-
hue,
|
|
383
|
-
this.#baseSize,
|
|
384
|
-
'ring',
|
|
385
|
-
this.#scale,
|
|
386
|
-
angle + MULBERRY.nextBetween(-0.05, 0.05),
|
|
387
|
-
outerSpeed + MULBERRY.nextBetween(-0.25, 0.25)
|
|
388
|
-
));
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
const innerCount = Math.floor(MULBERRY.nextBetween(25, 35));
|
|
392
|
-
const innerSpeed = MULBERRY.nextBetween(3, 5);
|
|
393
|
-
|
|
394
|
-
for (let i = 0; i < innerCount; i++) {
|
|
395
|
-
const angle = (i / innerCount) * Math.PI * 2;
|
|
396
|
-
|
|
397
|
-
this.#explosions.push(new Explosion(
|
|
398
|
-
position,
|
|
399
|
-
hue + 120,
|
|
400
|
-
this.#baseSize,
|
|
401
|
-
'ring',
|
|
402
|
-
this.#scale,
|
|
403
|
-
angle + MULBERRY.nextBetween(-0.05, 0.05),
|
|
404
|
-
innerSpeed + MULBERRY.nextBetween(-0.25, 0.25)
|
|
405
|
-
));
|
|
406
|
-
}
|
|
173
|
+
const rng = () => MULBERRY.nextBetween(0, 1);
|
|
174
|
+
this.#explosions.push(...createExplosion(selected, position, hue, {lineWidth: this.#baseSize, scale: this.#scale}, rng));
|
|
407
175
|
}
|
|
408
176
|
|
|
409
177
|
#createFirework(position?: Point): void {
|
|
@@ -422,31 +190,14 @@ export class FireworkLayer extends SimulationLayer {
|
|
|
422
190
|
|
|
423
191
|
firework.addEventListener('remove', () => {
|
|
424
192
|
this.#fireworks.splice(this.#fireworks.indexOf(firework), 1);
|
|
425
|
-
this.#
|
|
193
|
+
this.#spawnExplosion(firework.position, hue);
|
|
426
194
|
}, {once: true});
|
|
427
195
|
|
|
428
196
|
this.#fireworks.push(firework);
|
|
429
197
|
}
|
|
430
198
|
|
|
431
199
|
#pickVariant(): FireworkVariant {
|
|
432
|
-
const
|
|
433
|
-
|
|
434
|
-
if (roll < 12) { return 'peony'; }
|
|
435
|
-
if (roll < 22) { return 'chrysanthemum'; }
|
|
436
|
-
if (roll < 29) { return 'willow'; }
|
|
437
|
-
if (roll < 34) { return 'ring'; }
|
|
438
|
-
if (roll < 39) { return 'palm'; }
|
|
439
|
-
if (roll < 44) { return 'crackle'; }
|
|
440
|
-
if (roll < 48) { return 'crossette'; }
|
|
441
|
-
if (roll < 55) { return 'saturn'; }
|
|
442
|
-
if (roll < 62) { return 'dahlia'; }
|
|
443
|
-
if (roll < 67) { return 'brocade'; }
|
|
444
|
-
if (roll < 71) { return 'horsetail'; }
|
|
445
|
-
if (roll < 75) { return 'strobe'; }
|
|
446
|
-
if (roll < 82) { return 'heart'; }
|
|
447
|
-
if (roll < 89) { return 'spiral'; }
|
|
448
|
-
if (roll < 94) { return 'flower'; }
|
|
449
|
-
|
|
450
|
-
return 'concentric';
|
|
200
|
+
const index = Math.floor(MULBERRY.nextBetween(0, this.#variants.length));
|
|
201
|
+
return this.#variants[index];
|
|
451
202
|
}
|
|
452
203
|
}
|
package/src/fireworks/spark.ts
CHANGED
|
@@ -8,7 +8,7 @@ export class Spark {
|
|
|
8
8
|
readonly #size: number;
|
|
9
9
|
readonly #decay: number;
|
|
10
10
|
readonly #friction: number = 0.94;
|
|
11
|
-
readonly #gravity: number = 0.
|
|
11
|
+
readonly #gravity: number = 0.3;
|
|
12
12
|
#alpha: number = 1;
|
|
13
13
|
|
|
14
14
|
get isDead(): boolean {
|
|
@@ -26,7 +26,7 @@ export class Spark {
|
|
|
26
26
|
this.#decay = MULBERRY.nextBetween(0.03, 0.08);
|
|
27
27
|
this.#velocity = {
|
|
28
28
|
x: velocityX + MULBERRY.nextBetween(-1.5, 1.5),
|
|
29
|
-
y: velocityY + MULBERRY.nextBetween(-2,
|
|
29
|
+
y: velocityY + MULBERRY.nextBetween(-2, 2)
|
|
30
30
|
};
|
|
31
31
|
}
|
|
32
32
|
|
package/src/fireworks/types.ts
CHANGED
|
@@ -21,10 +21,10 @@ export interface ExplosionConfig {
|
|
|
21
21
|
readonly glowSize: number;
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
export interface
|
|
24
|
+
export interface FireworksConfig {
|
|
25
25
|
readonly scale?: number;
|
|
26
26
|
readonly autoSpawn?: boolean;
|
|
27
|
-
readonly
|
|
27
|
+
readonly variants?: FireworkVariant[];
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
export const FIREWORK_VARIANTS: FireworkVariant[] = [
|
package/src/glitter/index.ts
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
import { Glitter } from './layer';
|
|
2
|
+
import type { GlitterConfig } from './types';
|
|
3
|
+
import type { Effect } from '../effect';
|
|
4
|
+
|
|
5
|
+
export function createGlitter(config?: GlitterConfig): Effect<GlitterConfig> {
|
|
6
|
+
return new Glitter(config);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export type { GlitterConfig, FallingGlitter, SettledGlitter } from './types';
|
package/src/glitter/layer.ts
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
import { hexToRGB } from '@basmilius/utils';
|
|
2
|
-
import {
|
|
2
|
+
import { Effect } from '../effect';
|
|
3
3
|
import { GLITTER_COLORS, MULBERRY } from './consts';
|
|
4
|
-
import type {
|
|
5
|
-
import type { FallingGlitter, SettledGlitter } from './types';
|
|
4
|
+
import type { FallingGlitter, GlitterConfig, SettledGlitter } from './types';
|
|
6
5
|
|
|
7
|
-
export class
|
|
6
|
+
export class Glitter extends Effect<GlitterConfig> {
|
|
8
7
|
readonly #scale: number;
|
|
9
8
|
readonly #size: number;
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
#speed: number;
|
|
10
|
+
#groundLevel: number;
|
|
12
11
|
readonly #maxSettled: number;
|
|
13
12
|
readonly #colorRGBs: [number, number, number][];
|
|
14
13
|
#maxCount: number;
|
|
@@ -16,7 +15,7 @@ export class GlitterLayer extends SimulationLayer {
|
|
|
16
15
|
#falling: FallingGlitter[] = [];
|
|
17
16
|
#settled: SettledGlitter[] = [];
|
|
18
17
|
|
|
19
|
-
constructor(config:
|
|
18
|
+
constructor(config: GlitterConfig = {}) {
|
|
20
19
|
super();
|
|
21
20
|
|
|
22
21
|
this.#scale = config.scale ?? 1;
|
|
@@ -38,6 +37,15 @@ export class GlitterLayer extends SimulationLayer {
|
|
|
38
37
|
}
|
|
39
38
|
}
|
|
40
39
|
|
|
40
|
+
configure(config: Partial<GlitterConfig>): void {
|
|
41
|
+
if (config.speed !== undefined) {
|
|
42
|
+
this.#speed = config.speed;
|
|
43
|
+
}
|
|
44
|
+
if (config.groundLevel !== undefined) {
|
|
45
|
+
this.#groundLevel = config.groundLevel;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
41
49
|
tick(dt: number, _width: number, _height: number): void {
|
|
42
50
|
this.#time += 0.03 * dt;
|
|
43
51
|
|
package/src/glitter/types.ts
CHANGED
|
@@ -1,3 +1,13 @@
|
|
|
1
|
+
export interface GlitterConfig {
|
|
2
|
+
readonly count?: number;
|
|
3
|
+
readonly colors?: string[];
|
|
4
|
+
readonly size?: number;
|
|
5
|
+
readonly speed?: number;
|
|
6
|
+
readonly groundLevel?: number;
|
|
7
|
+
readonly maxSettled?: number;
|
|
8
|
+
readonly scale?: number;
|
|
9
|
+
}
|
|
10
|
+
|
|
1
11
|
export type FallingGlitter = {
|
|
2
12
|
x: number;
|
|
3
13
|
y: number;
|
package/src/index.ts
CHANGED
|
@@ -1,18 +1,16 @@
|
|
|
1
1
|
export * from './aurora';
|
|
2
|
-
export * from './color';
|
|
3
2
|
export * from './balloons';
|
|
4
3
|
export * from './bubbles';
|
|
5
4
|
export * from './canvas';
|
|
5
|
+
export * from './color';
|
|
6
6
|
export * from './confetti';
|
|
7
7
|
export * from './donuts';
|
|
8
|
+
export * from './effect';
|
|
8
9
|
export * from './fireflies';
|
|
9
10
|
export * from './firepit';
|
|
10
11
|
export * from './fireworks';
|
|
11
12
|
export * from './glitter';
|
|
12
13
|
export * from './lanterns';
|
|
13
|
-
export * from './layer';
|
|
14
|
-
export * from './layered';
|
|
15
|
-
export * from './simulation-canvas';
|
|
16
14
|
export * from './leaves';
|
|
17
15
|
export * from './lightning';
|
|
18
16
|
export * from './matrix';
|
|
@@ -22,6 +20,7 @@ export * from './petals';
|
|
|
22
20
|
export * from './plasma';
|
|
23
21
|
export * from './rain';
|
|
24
22
|
export * from './sandstorm';
|
|
23
|
+
export * from './scene';
|
|
25
24
|
export * from './shooting-stars';
|
|
26
25
|
export * from './snow';
|
|
27
26
|
export * from './sparklers';
|
package/src/lanterns/index.ts
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
import { Lanterns } from './layer';
|
|
2
|
+
import type { LanternsConfig } from './types';
|
|
3
|
+
import type { Effect } from '../effect';
|
|
4
|
+
|
|
5
|
+
export function createLanterns(config?: LanternsConfig): Effect<LanternsConfig> {
|
|
6
|
+
return new Lanterns(config);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export type { LanternsConfig, Lantern } from './types';
|
package/src/lanterns/layer.ts
CHANGED
|
@@ -1,19 +1,20 @@
|
|
|
1
1
|
import { hexToRGB } from '@basmilius/utils';
|
|
2
|
-
import {
|
|
2
|
+
import { Effect } from '../effect';
|
|
3
3
|
import { LANTERN_COLORS, MULBERRY } from './consts';
|
|
4
|
-
import type {
|
|
5
|
-
import type { Lantern } from './types';
|
|
4
|
+
import type { Lantern, LanternsConfig } from './types';
|
|
6
5
|
|
|
7
|
-
export class
|
|
6
|
+
export class Lanterns extends Effect<LanternsConfig> {
|
|
8
7
|
readonly #scale: number;
|
|
9
|
-
|
|
8
|
+
#speed: number;
|
|
10
9
|
readonly #size: number;
|
|
11
10
|
readonly #colorRGBs: [number, number, number][];
|
|
12
11
|
#maxCount: number;
|
|
13
12
|
#time: number = 0;
|
|
14
13
|
#lanterns: Lantern[] = [];
|
|
14
|
+
#sortedLanterns: Lantern[] = [];
|
|
15
|
+
#sortDirty: boolean = true;
|
|
15
16
|
|
|
16
|
-
constructor(config:
|
|
17
|
+
constructor(config: LanternsConfig = {}) {
|
|
17
18
|
super();
|
|
18
19
|
|
|
19
20
|
this.#scale = config.scale ?? 1;
|
|
@@ -33,6 +34,12 @@ export class LanternLayer extends SimulationLayer {
|
|
|
33
34
|
}
|
|
34
35
|
}
|
|
35
36
|
|
|
37
|
+
configure(config: Partial<LanternsConfig>): void {
|
|
38
|
+
if (config.speed !== undefined) {
|
|
39
|
+
this.#speed = config.speed;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
36
43
|
tick(dt: number, width: number, height: number): void {
|
|
37
44
|
this.#time += 0.02 * dt * this.#speed;
|
|
38
45
|
|
|
@@ -46,13 +53,19 @@ export class LanternLayer extends SimulationLayer {
|
|
|
46
53
|
|
|
47
54
|
if (lantern.y < -0.15) {
|
|
48
55
|
this.#lanterns[i] = this.#createLantern(false);
|
|
56
|
+
this.#sortDirty = true;
|
|
49
57
|
}
|
|
50
58
|
}
|
|
51
59
|
}
|
|
52
60
|
|
|
53
61
|
draw(ctx: CanvasRenderingContext2D, width: number, height: number): void {
|
|
54
62
|
|
|
55
|
-
|
|
63
|
+
if (this.#sortDirty) {
|
|
64
|
+
this.#sortedLanterns = [...this.#lanterns].sort((a, b) => a.size - b.size);
|
|
65
|
+
this.#sortDirty = false;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const sorted = this.#sortedLanterns;
|
|
56
69
|
|
|
57
70
|
for (const lantern of sorted) {
|
|
58
71
|
const px = lantern.x * width;
|
|
@@ -75,8 +88,7 @@ export class LanternLayer extends SimulationLayer {
|
|
|
75
88
|
ctx.arc(px, py, glowRadius, 0, Math.PI * 2);
|
|
76
89
|
ctx.fill();
|
|
77
90
|
|
|
78
|
-
ctx.
|
|
79
|
-
ctx.translate(px, py);
|
|
91
|
+
ctx.setTransform(1, 0, 0, 1, px, py);
|
|
80
92
|
|
|
81
93
|
const bodyW = size * 0.8;
|
|
82
94
|
const bodyH = size;
|
|
@@ -140,7 +152,7 @@ export class LanternLayer extends SimulationLayer {
|
|
|
140
152
|
ctx.lineWidth = size * 0.04;
|
|
141
153
|
ctx.stroke();
|
|
142
154
|
|
|
143
|
-
ctx.
|
|
155
|
+
ctx.resetTransform();
|
|
144
156
|
}
|
|
145
157
|
}
|
|
146
158
|
|
package/src/lanterns/types.ts
CHANGED