@tsparticles/stencil 4.0.5

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.
Files changed (42) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +73 -0
  3. package/dist/cjs/Container-DFed5jPA.js +3556 -0
  4. package/dist/cjs/app-globals-V2Kpy_OQ.js +5 -0
  5. package/dist/cjs/index-BY5-j3YK.js +1143 -0
  6. package/dist/cjs/index-Dwz1YgEM.js +1229 -0
  7. package/dist/cjs/index.cjs.js +9 -0
  8. package/dist/cjs/loader.cjs.js +13 -0
  9. package/dist/cjs/stencil-particles.cjs.entry.js +76 -0
  10. package/dist/cjs/tsparticlesstencil.cjs.js +25 -0
  11. package/dist/collection/collection-manifest.json +13 -0
  12. package/dist/collection/components/stencil-particles/stencil-particles.js +144 -0
  13. package/dist/collection/index.js +1 -0
  14. package/dist/collection/initParticlesEngine.js +38 -0
  15. package/dist/esm/Container-Di-pVL5B.js +3554 -0
  16. package/dist/esm/app-globals-DQuL1Twl.js +3 -0
  17. package/dist/esm/index-BzK79uqU.js +1223 -0
  18. package/dist/esm/index-CPcFDf6d.js +1052 -0
  19. package/dist/esm/index.js +1 -0
  20. package/dist/esm/loader.js +11 -0
  21. package/dist/esm/stencil-particles.entry.js +74 -0
  22. package/dist/esm/tsparticlesstencil.js +21 -0
  23. package/dist/index.cjs.js +1 -0
  24. package/dist/index.js +1 -0
  25. package/dist/tsparticlesstencil/index.esm.js +1 -0
  26. package/dist/tsparticlesstencil/p-2a10755a.entry.js +1 -0
  27. package/dist/tsparticlesstencil/p-BzK79uqU.js +2 -0
  28. package/dist/tsparticlesstencil/p-C6McIuSD.js +1 -0
  29. package/dist/tsparticlesstencil/p-DQuL1Twl.js +1 -0
  30. package/dist/tsparticlesstencil/p-x9G6hxBi.js +1 -0
  31. package/dist/tsparticlesstencil/tsparticlesstencil.esm.js +1 -0
  32. package/dist/types/components/stencil-particles/stencil-particles.d.ts +16 -0
  33. package/dist/types/components.d.ts +52 -0
  34. package/dist/types/index.d.ts +2 -0
  35. package/dist/types/initParticlesEngine.d.ts +5 -0
  36. package/dist/types/stencil-public-runtime.d.ts +1860 -0
  37. package/loader/cdn.js +1 -0
  38. package/loader/index.cjs.js +1 -0
  39. package/loader/index.d.ts +24 -0
  40. package/loader/index.es2017.js +1 -0
  41. package/loader/index.js +2 -0
  42. package/package.json +64 -0
@@ -0,0 +1,3554 @@
1
+ import { R as RangeType, c as checkDistance, s as squareExp, a as isNull, b as setRangeValue, h as hMin, d as hMax, l as lMin, e as lMax, f as sMin, g as sMax, j as isString, k as isArray, m as deepExtend, P as PixelMode, O as OutMode, M as MoveDirection, n as isNumber, o as isObject, L as LimitMode, p as executeOnSingleOrMultiple, q as isBoolean, r as originPoint, u as minStrokeWidth, v as defaultZoom, w as itemFromArray, x as defaultOpacity, y as randomColorValue, z as half, A as AlterType, B as lFactor, C as double, D as hPhase, E as rgbMax, F as getRandomInRange, G as identity, H as defaultRgbMin, I as sextuple, J as triple, K as phaseNumerator, N as sNormalizedOffset, Q as minimumSize, S as defaultCompositeValue, T as defaultTransformValue, U as zIndexFactorOffset, V as safeMutationObserver, W as generatedAttribute, X as cloneStyle, Y as safeMatchMedia, Z as getFullScreenStyle, _ as safeDocument, $ as millisecondsToSeconds, a0 as manageListener, a1 as visibilityChangeEvent, a2 as resizeEvent, a3 as Vector3d, a4 as EventType, a5 as defaultAngle, a6 as defaultTransform, a7 as ParticleOutType, a8 as itemFromSingleOrMultiple, a9 as getRandom, aa as defaultRetryCount, ab as tryCountIncrement, ac as getParticleBaseVelocity, ad as randomInRangeValue, ae as OutModeDirection, af as clamp, ag as minZ, ah as getPosition, ai as getParticleDirectionAngle, aj as Vector, ak as getRangeValue, al as doublePI, am as calcExactPositionOrRandomFromSize, an as isInArray, ao as degToRad, ap as spatialHashGridCellSize, aq as minLimit, ar as minCount, as as getLogger, at as minIndex, au as defaultRemoveQuantity, av as defaultDensityFactor, aw as empty, ax as deleteCount, ay as countOffset, az as one, aA as defaultRatio, aB as defaultReduceFactor, aC as animate, aD as defaultFpsLimit, aE as minFpsLimit, aF as cancelAnimation, aG as defaultFps } from './index-CPcFDf6d.js';
2
+
3
+ class BaseRange {
4
+ position;
5
+ type;
6
+ constructor(x, y, type) {
7
+ this.position = {
8
+ x: x,
9
+ y: y,
10
+ };
11
+ this.type = type;
12
+ }
13
+ _resetPosition(x, y) {
14
+ this.position.x = x;
15
+ this.position.y = y;
16
+ }
17
+ }
18
+ class Circle extends BaseRange {
19
+ radius;
20
+ constructor(x, y, radius) {
21
+ super(x, y, RangeType.circle);
22
+ this.radius = radius;
23
+ }
24
+ contains(point) {
25
+ return checkDistance(point, this.position, this.radius);
26
+ }
27
+ intersects(range) {
28
+ const pos1 = this.position, pos2 = range.position, r = this.radius, dx = Math.abs(pos2.x - pos1.x), dy = Math.abs(pos2.y - pos1.y);
29
+ if (range instanceof Circle || range.type === RangeType.circle) {
30
+ const circleRange = range, rSum = r + circleRange.radius, dist = Math.hypot(dx, dy);
31
+ return rSum > dist;
32
+ }
33
+ else if (range instanceof Rectangle || range.type === RangeType.rectangle) {
34
+ const rectRange = range, { width, height } = rectRange.size, edges = Math.pow(dx - width, squareExp) + Math.pow(dy - height, squareExp);
35
+ return edges <= r ** squareExp || (dx <= r + width && dy <= r + height) || dx <= width || dy <= height;
36
+ }
37
+ return false;
38
+ }
39
+ reset(x, y, radius) {
40
+ this._resetPosition(x, y);
41
+ this.radius = radius;
42
+ return this;
43
+ }
44
+ }
45
+ class Rectangle extends BaseRange {
46
+ size;
47
+ constructor(x, y, width, height) {
48
+ super(x, y, RangeType.rectangle);
49
+ this.size = {
50
+ height: height,
51
+ width: width,
52
+ };
53
+ }
54
+ contains(point) {
55
+ const w = this.size.width, h = this.size.height, pos = this.position;
56
+ return point.x >= pos.x && point.x <= pos.x + w && point.y >= pos.y && point.y <= pos.y + h;
57
+ }
58
+ intersects(range) {
59
+ if (range instanceof Circle) {
60
+ return range.intersects(this);
61
+ }
62
+ if (!(range instanceof Rectangle)) {
63
+ return false;
64
+ }
65
+ const w = this.size.width, h = this.size.height, pos1 = this.position, pos2 = range.position, size2 = range.size, w2 = size2.width, h2 = size2.height;
66
+ return pos2.x < pos1.x + w && pos2.x + w2 > pos1.x && pos2.y < pos1.y + h && pos2.y + h2 > pos1.y;
67
+ }
68
+ reset(x, y, width, height) {
69
+ this._resetPosition(x, y);
70
+ this.size.width = width;
71
+ this.size.height = height;
72
+ return this;
73
+ }
74
+ }
75
+
76
+ class AnimationOptions {
77
+ count;
78
+ decay;
79
+ delay;
80
+ enable;
81
+ speed;
82
+ sync;
83
+ constructor() {
84
+ this.count = 0;
85
+ this.enable = false;
86
+ this.speed = 1;
87
+ this.decay = 0;
88
+ this.delay = 0;
89
+ this.sync = false;
90
+ }
91
+ load(data) {
92
+ if (isNull(data)) {
93
+ return;
94
+ }
95
+ if (data.count !== undefined) {
96
+ this.count = setRangeValue(data.count);
97
+ }
98
+ if (data.enable !== undefined) {
99
+ this.enable = data.enable;
100
+ }
101
+ if (data.speed !== undefined) {
102
+ this.speed = setRangeValue(data.speed);
103
+ }
104
+ if (data.decay !== undefined) {
105
+ this.decay = setRangeValue(data.decay);
106
+ }
107
+ if (data.delay !== undefined) {
108
+ this.delay = setRangeValue(data.delay);
109
+ }
110
+ if (data.sync !== undefined) {
111
+ this.sync = data.sync;
112
+ }
113
+ }
114
+ }
115
+
116
+ class ColorAnimation extends AnimationOptions {
117
+ max;
118
+ min;
119
+ offset;
120
+ constructor(min, max) {
121
+ super();
122
+ this.min = min;
123
+ this.max = max;
124
+ this.offset = 0;
125
+ this.sync = true;
126
+ }
127
+ load(data) {
128
+ super.load(data);
129
+ if (isNull(data)) {
130
+ return;
131
+ }
132
+ if (data.max !== undefined) {
133
+ this.max = data.max;
134
+ }
135
+ if (data.min !== undefined) {
136
+ this.min = data.min;
137
+ }
138
+ if (data.offset !== undefined) {
139
+ this.offset = setRangeValue(data.offset);
140
+ }
141
+ }
142
+ }
143
+
144
+ class HslAnimation {
145
+ h = new ColorAnimation(hMin, hMax);
146
+ l = new ColorAnimation(lMin, lMax);
147
+ s = new ColorAnimation(sMin, sMax);
148
+ load(data) {
149
+ if (isNull(data)) {
150
+ return;
151
+ }
152
+ this.h.load(data.h);
153
+ this.s.load(data.s);
154
+ this.l.load(data.l);
155
+ }
156
+ }
157
+
158
+ class OptionsColor {
159
+ value;
160
+ constructor() {
161
+ this.value = "";
162
+ }
163
+ static create(source, data) {
164
+ const color = new OptionsColor();
165
+ color.load(source);
166
+ if (data !== undefined) {
167
+ if (isString(data) || isArray(data)) {
168
+ color.load({ value: data });
169
+ }
170
+ else {
171
+ color.load(data);
172
+ }
173
+ }
174
+ return color;
175
+ }
176
+ load(data) {
177
+ if (isNull(data)) {
178
+ return;
179
+ }
180
+ if (!isNull(data.value)) {
181
+ this.value = data.value;
182
+ }
183
+ }
184
+ }
185
+
186
+ class AnimatableColor extends OptionsColor {
187
+ animation;
188
+ constructor() {
189
+ super();
190
+ this.animation = new HslAnimation();
191
+ }
192
+ static create(source, data) {
193
+ const color = new AnimatableColor();
194
+ color.load(source);
195
+ if (data !== undefined) {
196
+ if (isString(data) || isArray(data)) {
197
+ color.load({ value: data });
198
+ }
199
+ else {
200
+ color.load(data);
201
+ }
202
+ }
203
+ return color;
204
+ }
205
+ load(data) {
206
+ super.load(data);
207
+ if (isNull(data)) {
208
+ return;
209
+ }
210
+ const colorAnimation = data.animation;
211
+ if (colorAnimation !== undefined) {
212
+ if (colorAnimation.enable === undefined) {
213
+ this.animation.load(data.animation);
214
+ }
215
+ else {
216
+ this.animation.h.load(colorAnimation);
217
+ }
218
+ }
219
+ }
220
+ }
221
+
222
+ class Background {
223
+ color;
224
+ image;
225
+ opacity;
226
+ position;
227
+ repeat;
228
+ size;
229
+ constructor() {
230
+ this.color = new OptionsColor();
231
+ this.color.value = "";
232
+ this.image = "";
233
+ this.position = "";
234
+ this.repeat = "";
235
+ this.size = "";
236
+ this.opacity = 1;
237
+ }
238
+ load(data) {
239
+ if (isNull(data)) {
240
+ return;
241
+ }
242
+ if (data.color !== undefined) {
243
+ this.color = OptionsColor.create(this.color, data.color);
244
+ }
245
+ if (data.image !== undefined) {
246
+ this.image = data.image;
247
+ }
248
+ if (data.position !== undefined) {
249
+ this.position = data.position;
250
+ }
251
+ if (data.repeat !== undefined) {
252
+ this.repeat = data.repeat;
253
+ }
254
+ if (data.size !== undefined) {
255
+ this.size = data.size;
256
+ }
257
+ if (data.opacity !== undefined) {
258
+ this.opacity = data.opacity;
259
+ }
260
+ }
261
+ }
262
+
263
+ class FullScreen {
264
+ enable;
265
+ zIndex;
266
+ constructor() {
267
+ this.enable = true;
268
+ this.zIndex = 0;
269
+ }
270
+ load(data) {
271
+ if (isNull(data)) {
272
+ return;
273
+ }
274
+ if (data.enable !== undefined) {
275
+ this.enable = data.enable;
276
+ }
277
+ if (data.zIndex !== undefined) {
278
+ this.zIndex = data.zIndex;
279
+ }
280
+ }
281
+ }
282
+
283
+ class ResizeEvent {
284
+ delay;
285
+ enable;
286
+ constructor() {
287
+ this.delay = 0.5;
288
+ this.enable = true;
289
+ }
290
+ load(data) {
291
+ if (isNull(data)) {
292
+ return;
293
+ }
294
+ if (data.delay !== undefined) {
295
+ this.delay = data.delay;
296
+ }
297
+ if (data.enable !== undefined) {
298
+ this.enable = data.enable;
299
+ }
300
+ }
301
+ }
302
+
303
+ class Effect {
304
+ close;
305
+ options;
306
+ type;
307
+ constructor() {
308
+ this.close = true;
309
+ this.options = {};
310
+ this.type = [];
311
+ }
312
+ load(data) {
313
+ if (isNull(data)) {
314
+ return;
315
+ }
316
+ const options = data.options;
317
+ if (options !== undefined) {
318
+ for (const effect in options) {
319
+ const item = options[effect];
320
+ if (item) {
321
+ this.options[effect] = deepExtend(this.options[effect] ?? {}, item);
322
+ }
323
+ }
324
+ }
325
+ if (data.close !== undefined) {
326
+ this.close = data.close;
327
+ }
328
+ if (data.type !== undefined) {
329
+ this.type = data.type;
330
+ }
331
+ }
332
+ }
333
+
334
+ class Fill {
335
+ color;
336
+ enable;
337
+ opacity;
338
+ constructor() {
339
+ this.enable = true;
340
+ this.opacity = 1;
341
+ }
342
+ load(data) {
343
+ if (isNull(data)) {
344
+ return;
345
+ }
346
+ if (data.color !== undefined) {
347
+ this.color = AnimatableColor.create(this.color, data.color);
348
+ }
349
+ if (data.enable !== undefined) {
350
+ this.enable = data.enable;
351
+ }
352
+ if (data.opacity !== undefined) {
353
+ this.opacity = setRangeValue(data.opacity);
354
+ }
355
+ }
356
+ }
357
+
358
+ class MoveAngle {
359
+ offset;
360
+ value;
361
+ constructor() {
362
+ this.offset = 0;
363
+ this.value = 90;
364
+ }
365
+ load(data) {
366
+ if (isNull(data)) {
367
+ return;
368
+ }
369
+ if (data.offset !== undefined) {
370
+ this.offset = setRangeValue(data.offset);
371
+ }
372
+ if (data.value !== undefined) {
373
+ this.value = setRangeValue(data.value);
374
+ }
375
+ }
376
+ }
377
+
378
+ class MoveCenter {
379
+ mode;
380
+ radius;
381
+ x;
382
+ y;
383
+ constructor() {
384
+ this.x = 50;
385
+ this.y = 50;
386
+ this.mode = PixelMode.percent;
387
+ this.radius = 0;
388
+ }
389
+ load(data) {
390
+ if (isNull(data)) {
391
+ return;
392
+ }
393
+ if (data.x !== undefined) {
394
+ this.x = data.x;
395
+ }
396
+ if (data.y !== undefined) {
397
+ this.y = data.y;
398
+ }
399
+ if (data.mode !== undefined) {
400
+ this.mode = data.mode;
401
+ }
402
+ if (data.radius !== undefined) {
403
+ this.radius = data.radius;
404
+ }
405
+ }
406
+ }
407
+
408
+ class MoveGravity {
409
+ acceleration;
410
+ enable;
411
+ inverse;
412
+ maxSpeed;
413
+ constructor() {
414
+ this.acceleration = 9.81;
415
+ this.enable = false;
416
+ this.inverse = false;
417
+ this.maxSpeed = 50;
418
+ }
419
+ load(data) {
420
+ if (isNull(data)) {
421
+ return;
422
+ }
423
+ if (data.acceleration !== undefined) {
424
+ this.acceleration = setRangeValue(data.acceleration);
425
+ }
426
+ if (data.enable !== undefined) {
427
+ this.enable = data.enable;
428
+ }
429
+ if (data.inverse !== undefined) {
430
+ this.inverse = data.inverse;
431
+ }
432
+ if (data.maxSpeed !== undefined) {
433
+ this.maxSpeed = setRangeValue(data.maxSpeed);
434
+ }
435
+ }
436
+ }
437
+
438
+ class ValueWithRandom {
439
+ value;
440
+ constructor() {
441
+ this.value = 0;
442
+ }
443
+ load(data) {
444
+ if (isNull(data)) {
445
+ return;
446
+ }
447
+ if (!isNull(data.value)) {
448
+ this.value = setRangeValue(data.value);
449
+ }
450
+ }
451
+ }
452
+
453
+ class MovePath {
454
+ clamp;
455
+ delay;
456
+ enable;
457
+ generator;
458
+ options;
459
+ constructor() {
460
+ this.clamp = true;
461
+ this.delay = new ValueWithRandom();
462
+ this.enable = false;
463
+ this.options = {};
464
+ }
465
+ load(data) {
466
+ if (isNull(data)) {
467
+ return;
468
+ }
469
+ if (data.clamp !== undefined) {
470
+ this.clamp = data.clamp;
471
+ }
472
+ this.delay.load(data.delay);
473
+ if (data.enable !== undefined) {
474
+ this.enable = data.enable;
475
+ }
476
+ this.generator = data.generator;
477
+ if (data.options) {
478
+ this.options = deepExtend(this.options, data.options);
479
+ }
480
+ }
481
+ }
482
+
483
+ class OutModes {
484
+ bottom;
485
+ default;
486
+ left;
487
+ right;
488
+ top;
489
+ constructor() {
490
+ this.default = OutMode.out;
491
+ }
492
+ load(data) {
493
+ if (isNull(data)) {
494
+ return;
495
+ }
496
+ if (data.default !== undefined) {
497
+ this.default = data.default;
498
+ }
499
+ this.bottom = data.bottom ?? data.default;
500
+ this.left = data.left ?? data.default;
501
+ this.right = data.right ?? data.default;
502
+ this.top = data.top ?? data.default;
503
+ }
504
+ }
505
+
506
+ class Spin {
507
+ acceleration;
508
+ enable;
509
+ position;
510
+ constructor() {
511
+ this.acceleration = 0;
512
+ this.enable = false;
513
+ }
514
+ load(data) {
515
+ if (isNull(data)) {
516
+ return;
517
+ }
518
+ if (data.acceleration !== undefined) {
519
+ this.acceleration = setRangeValue(data.acceleration);
520
+ }
521
+ if (data.enable !== undefined) {
522
+ this.enable = data.enable;
523
+ }
524
+ if (data.position) {
525
+ this.position = deepExtend({}, data.position);
526
+ }
527
+ }
528
+ }
529
+
530
+ class Move {
531
+ angle;
532
+ center;
533
+ decay;
534
+ direction;
535
+ distance;
536
+ drift;
537
+ enable;
538
+ gravity;
539
+ outModes;
540
+ path;
541
+ random;
542
+ size;
543
+ speed;
544
+ spin;
545
+ straight;
546
+ vibrate;
547
+ warp;
548
+ constructor() {
549
+ this.angle = new MoveAngle();
550
+ this.center = new MoveCenter();
551
+ this.decay = 0;
552
+ this.distance = {};
553
+ this.direction = MoveDirection.none;
554
+ this.drift = 0;
555
+ this.enable = false;
556
+ this.gravity = new MoveGravity();
557
+ this.path = new MovePath();
558
+ this.outModes = new OutModes();
559
+ this.random = false;
560
+ this.size = false;
561
+ this.speed = 2;
562
+ this.spin = new Spin();
563
+ this.straight = false;
564
+ this.vibrate = false;
565
+ this.warp = false;
566
+ }
567
+ load(data) {
568
+ if (isNull(data)) {
569
+ return;
570
+ }
571
+ this.angle.load(isNumber(data.angle) ? { value: data.angle } : data.angle);
572
+ this.center.load(data.center);
573
+ if (data.decay !== undefined) {
574
+ this.decay = setRangeValue(data.decay);
575
+ }
576
+ if (data.direction !== undefined) {
577
+ this.direction = data.direction;
578
+ }
579
+ if (data.distance !== undefined) {
580
+ this.distance = isNumber(data.distance)
581
+ ? {
582
+ horizontal: data.distance,
583
+ vertical: data.distance,
584
+ }
585
+ : { ...data.distance };
586
+ }
587
+ if (data.drift !== undefined) {
588
+ this.drift = setRangeValue(data.drift);
589
+ }
590
+ if (data.enable !== undefined) {
591
+ this.enable = data.enable;
592
+ }
593
+ this.gravity.load(data.gravity);
594
+ const outModes = data.outModes;
595
+ if (outModes !== undefined) {
596
+ if (isObject(outModes)) {
597
+ this.outModes.load(outModes);
598
+ }
599
+ else {
600
+ this.outModes.load({
601
+ default: outModes,
602
+ });
603
+ }
604
+ }
605
+ this.path.load(data.path);
606
+ if (data.random !== undefined) {
607
+ this.random = data.random;
608
+ }
609
+ if (data.size !== undefined) {
610
+ this.size = data.size;
611
+ }
612
+ if (data.speed !== undefined) {
613
+ this.speed = setRangeValue(data.speed);
614
+ }
615
+ this.spin.load(data.spin);
616
+ if (data.straight !== undefined) {
617
+ this.straight = data.straight;
618
+ }
619
+ if (data.vibrate !== undefined) {
620
+ this.vibrate = data.vibrate;
621
+ }
622
+ if (data.warp !== undefined) {
623
+ this.warp = data.warp;
624
+ }
625
+ }
626
+ }
627
+
628
+ class Stroke {
629
+ color;
630
+ opacity;
631
+ width;
632
+ constructor() {
633
+ this.width = 0;
634
+ }
635
+ load(data) {
636
+ if (isNull(data)) {
637
+ return;
638
+ }
639
+ if (data.color !== undefined) {
640
+ this.color = AnimatableColor.create(this.color, data.color);
641
+ }
642
+ if (data.width !== undefined) {
643
+ this.width = setRangeValue(data.width);
644
+ }
645
+ if (data.opacity !== undefined) {
646
+ this.opacity = setRangeValue(data.opacity);
647
+ }
648
+ }
649
+ }
650
+
651
+ class Paint {
652
+ color;
653
+ fill;
654
+ stroke;
655
+ load(data) {
656
+ if (isNull(data)) {
657
+ return;
658
+ }
659
+ if (data.color !== undefined) {
660
+ this.color = AnimatableColor.create(this.color, data.color);
661
+ }
662
+ if (data.fill !== undefined) {
663
+ this.fill ??= new Fill();
664
+ this.fill.load(data.fill);
665
+ }
666
+ if (data.stroke !== undefined) {
667
+ this.stroke ??= new Stroke();
668
+ this.stroke.load(data.stroke);
669
+ }
670
+ }
671
+ }
672
+
673
+ class ParticlesBounceFactor extends ValueWithRandom {
674
+ constructor() {
675
+ super();
676
+ this.value = 1;
677
+ }
678
+ }
679
+
680
+ class ParticlesBounce {
681
+ horizontal;
682
+ vertical;
683
+ constructor() {
684
+ this.horizontal = new ParticlesBounceFactor();
685
+ this.vertical = new ParticlesBounceFactor();
686
+ }
687
+ load(data) {
688
+ if (isNull(data)) {
689
+ return;
690
+ }
691
+ this.horizontal.load(data.horizontal);
692
+ this.vertical.load(data.vertical);
693
+ }
694
+ }
695
+
696
+ class ParticlesDensity {
697
+ enable;
698
+ height;
699
+ width;
700
+ constructor() {
701
+ this.enable = false;
702
+ this.width = 1920;
703
+ this.height = 1080;
704
+ }
705
+ load(data) {
706
+ if (isNull(data)) {
707
+ return;
708
+ }
709
+ if (data.enable !== undefined) {
710
+ this.enable = data.enable;
711
+ }
712
+ const width = data.width;
713
+ if (width !== undefined) {
714
+ this.width = width;
715
+ }
716
+ const height = data.height;
717
+ if (height !== undefined) {
718
+ this.height = height;
719
+ }
720
+ }
721
+ }
722
+
723
+ class ParticlesNumberLimit {
724
+ mode;
725
+ value;
726
+ constructor() {
727
+ this.mode = LimitMode.delete;
728
+ this.value = 0;
729
+ }
730
+ load(data) {
731
+ if (isNull(data)) {
732
+ return;
733
+ }
734
+ if (data.mode !== undefined) {
735
+ this.mode = data.mode;
736
+ }
737
+ if (data.value !== undefined) {
738
+ this.value = data.value;
739
+ }
740
+ }
741
+ }
742
+
743
+ class ParticlesNumber {
744
+ density;
745
+ limit;
746
+ value;
747
+ constructor() {
748
+ this.density = new ParticlesDensity();
749
+ this.limit = new ParticlesNumberLimit();
750
+ this.value = 0;
751
+ }
752
+ load(data) {
753
+ if (isNull(data)) {
754
+ return;
755
+ }
756
+ this.density.load(data.density);
757
+ this.limit.load(data.limit);
758
+ if (data.value !== undefined) {
759
+ this.value = data.value;
760
+ }
761
+ }
762
+ }
763
+
764
+ class Shape {
765
+ close;
766
+ options;
767
+ type;
768
+ constructor() {
769
+ this.close = true;
770
+ this.options = {};
771
+ this.type = "circle";
772
+ }
773
+ load(data) {
774
+ if (isNull(data)) {
775
+ return;
776
+ }
777
+ const options = data.options;
778
+ if (options !== undefined) {
779
+ for (const shape in options) {
780
+ const item = options[shape];
781
+ if (item) {
782
+ this.options[shape] = deepExtend(this.options[shape] ?? {}, item);
783
+ }
784
+ }
785
+ }
786
+ if (data.close !== undefined) {
787
+ this.close = data.close;
788
+ }
789
+ if (data.type !== undefined) {
790
+ this.type = data.type;
791
+ }
792
+ }
793
+ }
794
+
795
+ class ZIndex extends ValueWithRandom {
796
+ opacityRate;
797
+ sizeRate;
798
+ velocityRate;
799
+ constructor() {
800
+ super();
801
+ this.opacityRate = 1;
802
+ this.sizeRate = 1;
803
+ this.velocityRate = 1;
804
+ }
805
+ load(data) {
806
+ super.load(data);
807
+ if (isNull(data)) {
808
+ return;
809
+ }
810
+ if (data.opacityRate !== undefined) {
811
+ this.opacityRate = data.opacityRate;
812
+ }
813
+ if (data.sizeRate !== undefined) {
814
+ this.sizeRate = data.sizeRate;
815
+ }
816
+ if (data.velocityRate !== undefined) {
817
+ this.velocityRate = data.velocityRate;
818
+ }
819
+ }
820
+ }
821
+
822
+ class ParticlesOptions {
823
+ bounce;
824
+ effect;
825
+ groups;
826
+ move;
827
+ number;
828
+ paint;
829
+ palette;
830
+ reduceDuplicates;
831
+ shape;
832
+ zIndex;
833
+ #container;
834
+ #pluginManager;
835
+ constructor(pluginManager, container) {
836
+ this.#pluginManager = pluginManager;
837
+ this.#container = container;
838
+ this.bounce = new ParticlesBounce();
839
+ this.effect = new Effect();
840
+ this.groups = {};
841
+ this.move = new Move();
842
+ this.number = new ParticlesNumber();
843
+ this.paint = new Paint();
844
+ this.paint.color = new AnimatableColor();
845
+ this.paint.color.value = "#fff";
846
+ this.paint.fill = new Fill();
847
+ this.paint.fill.enable = true;
848
+ this.reduceDuplicates = false;
849
+ this.shape = new Shape();
850
+ this.zIndex = new ZIndex();
851
+ }
852
+ load(data) {
853
+ if (isNull(data)) {
854
+ return;
855
+ }
856
+ if (data.palette) {
857
+ this.palette = data.palette;
858
+ this.#importPalette(this.palette);
859
+ }
860
+ if (data.groups !== undefined) {
861
+ for (const group of Object.keys(data.groups)) {
862
+ if (!(group in data.groups)) {
863
+ continue;
864
+ }
865
+ const item = data.groups[group];
866
+ if (item !== undefined) {
867
+ this.groups[group] = deepExtend(this.groups[group] ?? {}, item);
868
+ }
869
+ }
870
+ }
871
+ if (data.reduceDuplicates !== undefined) {
872
+ this.reduceDuplicates = data.reduceDuplicates;
873
+ }
874
+ this.bounce.load(data.bounce);
875
+ this.effect.load(data.effect);
876
+ this.move.load(data.move);
877
+ this.number.load(data.number);
878
+ const paintToLoad = data.paint;
879
+ if (paintToLoad) {
880
+ if (isArray(paintToLoad)) {
881
+ this.paint = executeOnSingleOrMultiple(paintToLoad, t => {
882
+ const tmp = new Paint();
883
+ tmp.load(t);
884
+ return tmp;
885
+ });
886
+ }
887
+ else if (isArray(this.paint)) {
888
+ this.paint = new Paint();
889
+ this.paint.load(paintToLoad);
890
+ }
891
+ else {
892
+ this.paint.load(paintToLoad);
893
+ }
894
+ }
895
+ this.shape.load(data.shape);
896
+ this.zIndex.load(data.zIndex);
897
+ if (this.#container) {
898
+ for (const plugin of this.#pluginManager.plugins) {
899
+ if (plugin.loadParticlesOptions) {
900
+ plugin.loadParticlesOptions(this.#container, this, data);
901
+ }
902
+ }
903
+ const updaters = this.#pluginManager.updaters.get(this.#container);
904
+ if (updaters) {
905
+ for (const updater of updaters) {
906
+ if (updater.loadOptions) {
907
+ updater.loadOptions(this, data);
908
+ }
909
+ }
910
+ }
911
+ }
912
+ }
913
+ #importPalette = (palette) => {
914
+ const paletteData = this.#pluginManager.getPalette(palette);
915
+ if (!paletteData) {
916
+ return;
917
+ }
918
+ const paletteColors = paletteData.colors, defaultPaintStrokeWidth = 0, defaultPaintVariantsLength = 1, firstPaintVariantIndex = 0, defaultPalettePaintVariant = {}, colorVariants = isArray(paletteColors) ? paletteColors : [paletteColors], palettePaintVariants = colorVariants.flatMap(variant => {
919
+ const paletteFill = variant.fill, paletteStroke = variant.stroke, fillPart = paletteFill
920
+ ? {
921
+ color: {
922
+ value: paletteFill.value,
923
+ },
924
+ enable: paletteFill.enable,
925
+ opacity: paletteFill.opacity,
926
+ }
927
+ : undefined;
928
+ if (!paletteStroke) {
929
+ return [
930
+ {
931
+ fill: fillPart,
932
+ },
933
+ ];
934
+ }
935
+ return [
936
+ {
937
+ fill: fillPart,
938
+ stroke: {
939
+ color: {
940
+ value: paletteStroke.value,
941
+ },
942
+ opacity: paletteStroke.opacity,
943
+ width: paletteStroke.width || defaultPaintStrokeWidth,
944
+ },
945
+ },
946
+ ];
947
+ }), palettePaint = palettePaintVariants.length > defaultPaintVariantsLength
948
+ ? palettePaintVariants
949
+ : (palettePaintVariants[firstPaintVariantIndex] ?? defaultPalettePaintVariant);
950
+ this.load({
951
+ paint: palettePaint,
952
+ blend: {
953
+ enable: true,
954
+ mode: paletteData.blendMode,
955
+ },
956
+ });
957
+ };
958
+ }
959
+
960
+ function loadOptions(options, ...sourceOptionsArr) {
961
+ for (const sourceOptions of sourceOptionsArr) {
962
+ options.load(sourceOptions);
963
+ }
964
+ }
965
+ function loadParticlesOptions(pluginManager, container, ...sourceOptionsArr) {
966
+ const options = new ParticlesOptions(pluginManager, container);
967
+ loadOptions(options, ...sourceOptionsArr);
968
+ return options;
969
+ }
970
+
971
+ class Options {
972
+ autoPlay;
973
+ background;
974
+ clear;
975
+ defaultThemes;
976
+ delay;
977
+ detectRetina;
978
+ duration;
979
+ fpsLimit;
980
+ fullScreen;
981
+ hdr;
982
+ key;
983
+ name;
984
+ palette;
985
+ particles;
986
+ pauseOnBlur;
987
+ pauseOnOutsideViewport;
988
+ preset;
989
+ resize;
990
+ smooth;
991
+ style;
992
+ zLayers;
993
+ #container;
994
+ #pluginManager;
995
+ constructor(pluginManager, container) {
996
+ this.#pluginManager = pluginManager;
997
+ this.#container = container;
998
+ this.autoPlay = true;
999
+ this.background = new Background();
1000
+ this.clear = true;
1001
+ this.defaultThemes = {};
1002
+ this.delay = 0;
1003
+ this.fullScreen = new FullScreen();
1004
+ this.detectRetina = true;
1005
+ this.duration = 0;
1006
+ this.fpsLimit = 120;
1007
+ this.hdr = true;
1008
+ this.particles = loadParticlesOptions(this.#pluginManager, this.#container);
1009
+ this.pauseOnBlur = true;
1010
+ this.pauseOnOutsideViewport = true;
1011
+ this.resize = new ResizeEvent();
1012
+ this.smooth = false;
1013
+ this.style = {};
1014
+ this.zLayers = 100;
1015
+ }
1016
+ load(data) {
1017
+ if (isNull(data)) {
1018
+ return;
1019
+ }
1020
+ if (data.preset !== undefined) {
1021
+ this.preset = data.preset;
1022
+ executeOnSingleOrMultiple(this.preset, preset => {
1023
+ this.#importPreset(preset);
1024
+ });
1025
+ }
1026
+ if (data.palette !== undefined) {
1027
+ this.palette = data.palette;
1028
+ this.#importPalette(this.palette);
1029
+ }
1030
+ if (data.autoPlay !== undefined) {
1031
+ this.autoPlay = data.autoPlay;
1032
+ }
1033
+ if (data.clear !== undefined) {
1034
+ this.clear = data.clear;
1035
+ }
1036
+ if (data.key !== undefined) {
1037
+ this.key = data.key;
1038
+ }
1039
+ if (data.name !== undefined) {
1040
+ this.name = data.name;
1041
+ }
1042
+ if (data.delay !== undefined) {
1043
+ this.delay = setRangeValue(data.delay);
1044
+ }
1045
+ const detectRetina = data.detectRetina;
1046
+ if (detectRetina !== undefined) {
1047
+ this.detectRetina = detectRetina;
1048
+ }
1049
+ if (data.duration !== undefined) {
1050
+ this.duration = setRangeValue(data.duration);
1051
+ }
1052
+ const fpsLimit = data.fpsLimit;
1053
+ if (fpsLimit !== undefined) {
1054
+ this.fpsLimit = fpsLimit;
1055
+ }
1056
+ if (data.hdr !== undefined) {
1057
+ this.hdr = data.hdr;
1058
+ }
1059
+ if (data.pauseOnBlur !== undefined) {
1060
+ this.pauseOnBlur = data.pauseOnBlur;
1061
+ }
1062
+ if (data.pauseOnOutsideViewport !== undefined) {
1063
+ this.pauseOnOutsideViewport = data.pauseOnOutsideViewport;
1064
+ }
1065
+ if (data.zLayers !== undefined) {
1066
+ this.zLayers = data.zLayers;
1067
+ }
1068
+ this.background.load(data.background);
1069
+ const fullScreen = data.fullScreen;
1070
+ if (isBoolean(fullScreen)) {
1071
+ this.fullScreen.enable = fullScreen;
1072
+ }
1073
+ else {
1074
+ this.fullScreen.load(fullScreen);
1075
+ }
1076
+ this.particles.load(data.particles);
1077
+ this.resize.load(data.resize);
1078
+ this.style = deepExtend(this.style, data.style);
1079
+ if (data.smooth !== undefined) {
1080
+ this.smooth = data.smooth;
1081
+ }
1082
+ this.#pluginManager.plugins.forEach(plugin => {
1083
+ plugin.loadOptions(this.#container, this, data);
1084
+ });
1085
+ }
1086
+ #importPalette = palette => {
1087
+ const paletteData = this.#pluginManager.getPalette(palette);
1088
+ if (!paletteData) {
1089
+ return;
1090
+ }
1091
+ this.load({
1092
+ background: {
1093
+ color: paletteData.background,
1094
+ },
1095
+ blend: {
1096
+ enable: true,
1097
+ mode: paletteData.blendMode,
1098
+ },
1099
+ particles: {
1100
+ palette,
1101
+ },
1102
+ });
1103
+ };
1104
+ #importPreset = preset => {
1105
+ this.load(this.#pluginManager.getPreset(preset));
1106
+ };
1107
+ }
1108
+
1109
+ function paintBase(context, dimension, baseColor) {
1110
+ context.fillStyle = baseColor ?? "rgba(0,0,0,0)";
1111
+ context.fillRect(originPoint.x, originPoint.y, dimension.width, dimension.height);
1112
+ }
1113
+ function paintImage(context, dimension, image, opacity) {
1114
+ if (!image) {
1115
+ return;
1116
+ }
1117
+ const prevAlpha = context.globalAlpha;
1118
+ context.globalAlpha = opacity;
1119
+ context.drawImage(image, originPoint.x, originPoint.y, dimension.width, dimension.height);
1120
+ context.globalAlpha = prevAlpha;
1121
+ }
1122
+ function clear(context, dimension) {
1123
+ context.clearRect(originPoint.x, originPoint.y, dimension.width, dimension.height);
1124
+ }
1125
+ function drawParticle(data) {
1126
+ const { container, context, particle, delta, colorStyles, radius, opacity, transform } = data, { effectDrawers, shapeDrawers } = container, pos = particle.getPosition(), transformData = particle.getTransformData(transform), drawScale = defaultZoom, drawPosition = {
1127
+ x: pos.x,
1128
+ y: pos.y,
1129
+ };
1130
+ context.setTransform(transformData.a, transformData.b, transformData.c, transformData.d, pos.x, pos.y);
1131
+ if (colorStyles.fill) {
1132
+ context.fillStyle = colorStyles.fill;
1133
+ }
1134
+ const fillEnabled = !!particle.fillEnabled, strokeWidth = particle.strokeWidth ?? minStrokeWidth;
1135
+ context.lineWidth = strokeWidth;
1136
+ if (colorStyles.stroke) {
1137
+ context.strokeStyle = colorStyles.stroke;
1138
+ }
1139
+ const drawData = {
1140
+ context,
1141
+ particle,
1142
+ radius,
1143
+ drawRadius: radius * drawScale,
1144
+ opacity,
1145
+ delta,
1146
+ pixelRatio: container.retina.pixelRatio,
1147
+ fill: fillEnabled,
1148
+ stroke: strokeWidth > minStrokeWidth,
1149
+ transformData,
1150
+ position: { ...pos },
1151
+ drawPosition,
1152
+ drawScale,
1153
+ };
1154
+ for (const plugin of container.plugins) {
1155
+ plugin.drawParticleTransform?.(drawData);
1156
+ }
1157
+ const effect = particle.effect ? effectDrawers.get(particle.effect) : undefined, shape = particle.shape ? shapeDrawers.get(particle.shape) : undefined;
1158
+ drawBeforeEffect(effect, drawData);
1159
+ drawShapeBeforeDraw(shape, drawData);
1160
+ drawShape(shape, drawData);
1161
+ drawShapeAfterDraw(shape, drawData);
1162
+ drawAfterEffect(effect, drawData);
1163
+ context.resetTransform();
1164
+ }
1165
+ function drawAfterEffect(drawer, data) {
1166
+ if (!drawer?.drawAfter) {
1167
+ return;
1168
+ }
1169
+ const { particle } = data;
1170
+ if (!particle.effect) {
1171
+ return;
1172
+ }
1173
+ drawer.drawAfter(data);
1174
+ }
1175
+ function drawBeforeEffect(drawer, data) {
1176
+ if (!drawer?.drawBefore) {
1177
+ return;
1178
+ }
1179
+ const { particle } = data;
1180
+ if (!particle.effect) {
1181
+ return;
1182
+ }
1183
+ drawer.drawBefore(data);
1184
+ }
1185
+ function drawShape(drawer, data) {
1186
+ if (!drawer) {
1187
+ return;
1188
+ }
1189
+ const { context, fill, particle, stroke } = data;
1190
+ if (!particle.shape) {
1191
+ return;
1192
+ }
1193
+ context.beginPath();
1194
+ drawer.draw(data);
1195
+ if (particle.shapeClose) {
1196
+ context.closePath();
1197
+ }
1198
+ if (fill) {
1199
+ context.fill();
1200
+ }
1201
+ if (stroke) {
1202
+ context.stroke();
1203
+ }
1204
+ }
1205
+ function drawShapeAfterDraw(drawer, data) {
1206
+ if (!drawer?.afterDraw) {
1207
+ return;
1208
+ }
1209
+ const { particle } = data;
1210
+ if (!particle.shape) {
1211
+ return;
1212
+ }
1213
+ drawer.afterDraw(data);
1214
+ }
1215
+ function drawShapeBeforeDraw(drawer, data) {
1216
+ if (!drawer?.beforeDraw) {
1217
+ return;
1218
+ }
1219
+ const { particle } = data;
1220
+ if (!particle.shape) {
1221
+ return;
1222
+ }
1223
+ drawer.beforeDraw(data);
1224
+ }
1225
+ function drawParticlePlugin(context, plugin, particle, delta) {
1226
+ if (!plugin.drawParticle) {
1227
+ return;
1228
+ }
1229
+ plugin.drawParticle(context, particle, delta);
1230
+ }
1231
+
1232
+ const styleCache = new Map(), maxCacheSize = 1000, firstIndex = 0, rgbFixedPrecision = 2, hslFixedPrecision = 2;
1233
+ function getCachedStyle(key, generator) {
1234
+ let cached = styleCache.get(key);
1235
+ if (!cached) {
1236
+ cached = generator();
1237
+ if (styleCache.size >= maxCacheSize) {
1238
+ const keysToDelete = [...styleCache.keys()].slice(firstIndex, maxCacheSize * half);
1239
+ keysToDelete.forEach(k => styleCache.delete(k));
1240
+ }
1241
+ styleCache.set(key, cached);
1242
+ }
1243
+ return cached;
1244
+ }
1245
+ function stringToRgba(pluginManager, input) {
1246
+ if (!input) {
1247
+ return;
1248
+ }
1249
+ for (const manager of pluginManager.colorManagers.values()) {
1250
+ if (manager.accepts(input)) {
1251
+ return manager.parseString(input);
1252
+ }
1253
+ }
1254
+ return undefined;
1255
+ }
1256
+ function rangeColorToRgb(pluginManager, input, index, useIndex = true) {
1257
+ if (!input) {
1258
+ return;
1259
+ }
1260
+ const color = isString(input) ? { value: input } : input;
1261
+ if (isString(color.value)) {
1262
+ return colorToRgb(pluginManager, color.value, index, useIndex);
1263
+ }
1264
+ if (isArray(color.value)) {
1265
+ const value = itemFromArray(color.value, index, useIndex);
1266
+ if (!value) {
1267
+ return;
1268
+ }
1269
+ return rangeColorToRgb(pluginManager, {
1270
+ value,
1271
+ });
1272
+ }
1273
+ for (const manager of pluginManager.colorManagers.values()) {
1274
+ const res = manager.handleRangeColor(color);
1275
+ if (res) {
1276
+ return res;
1277
+ }
1278
+ }
1279
+ return undefined;
1280
+ }
1281
+ function colorToRgb(pluginManager, input, index, useIndex = true) {
1282
+ if (!input) {
1283
+ return;
1284
+ }
1285
+ const color = isString(input) ? { value: input } : input;
1286
+ if (isString(color.value)) {
1287
+ return color.value === randomColorValue ? getRandomRgbColor() : stringToRgb(pluginManager, color.value);
1288
+ }
1289
+ if (isArray(color.value)) {
1290
+ const value = itemFromArray(color.value, index, useIndex);
1291
+ if (!value) {
1292
+ return;
1293
+ }
1294
+ return colorToRgb(pluginManager, {
1295
+ value,
1296
+ });
1297
+ }
1298
+ for (const manager of pluginManager.colorManagers.values()) {
1299
+ const res = manager.handleColor(color);
1300
+ if (res) {
1301
+ return res;
1302
+ }
1303
+ }
1304
+ return undefined;
1305
+ }
1306
+ function rangeColorToHsl(pluginManager, color, index, useIndex = true) {
1307
+ const rgb = rangeColorToRgb(pluginManager, color, index, useIndex);
1308
+ return rgb ? rgbToHsl(rgb) : undefined;
1309
+ }
1310
+ function rgbToHsl(color) {
1311
+ const r1 = color.r / rgbMax, g1 = color.g / rgbMax, b1 = color.b / rgbMax, max = Math.max(r1, g1, b1), min = Math.min(r1, g1, b1), res = {
1312
+ h: hMin,
1313
+ l: (max + min) * half,
1314
+ s: sMin,
1315
+ };
1316
+ if (max !== min) {
1317
+ res.s = res.l < half ? (max - min) / (max + min) : (max - min) / (double - max - min);
1318
+ if (r1 === max) {
1319
+ res.h = (g1 - b1) / (max - min);
1320
+ }
1321
+ else if (g1 === max) {
1322
+ res.h = double + (b1 - r1) / (max - min);
1323
+ }
1324
+ else {
1325
+ res.h = double * double + (r1 - g1) / (max - min);
1326
+ }
1327
+ }
1328
+ res.l *= lMax;
1329
+ res.s *= sMax;
1330
+ res.h *= hPhase;
1331
+ if (res.h < hMin) {
1332
+ res.h += hMax;
1333
+ }
1334
+ if (res.h >= hMax) {
1335
+ res.h -= hMax;
1336
+ }
1337
+ return res;
1338
+ }
1339
+ function stringToRgb(pluginManager, input) {
1340
+ return stringToRgba(pluginManager, input);
1341
+ }
1342
+ function hslToRgb(hsl) {
1343
+ const h = ((hsl.h % hMax) + hMax) % hMax, s = Math.max(sMin, Math.min(sMax, hsl.s)), l = Math.max(lMin, Math.min(lMax, hsl.l)), hNormalized = h / hMax, sNormalized = s / sMax, lNormalized = l / lMax;
1344
+ if (s === sMin) {
1345
+ const grayscaleValue = Math.round(lNormalized * rgbMax);
1346
+ return { r: grayscaleValue, g: grayscaleValue, b: grayscaleValue };
1347
+ }
1348
+ const channel = (temp1, temp2, temp3) => {
1349
+ const temp3Min = 0, temp3Max = 1;
1350
+ if (temp3 < temp3Min) {
1351
+ temp3++;
1352
+ }
1353
+ if (temp3 > temp3Max) {
1354
+ temp3--;
1355
+ }
1356
+ if (temp3 * sextuple < temp3Max) {
1357
+ return temp1 + (temp2 - temp1) * sextuple * temp3;
1358
+ }
1359
+ if (temp3 * double < temp3Max) {
1360
+ return temp2;
1361
+ }
1362
+ if (temp3 * triple < temp3Max * double) {
1363
+ const temp3Offset = double / triple;
1364
+ return temp1 + (temp2 - temp1) * (temp3Offset - temp3) * sextuple;
1365
+ }
1366
+ return temp1;
1367
+ }, temp1 = lNormalized < half
1368
+ ? lNormalized * (sNormalizedOffset + sNormalized)
1369
+ : lNormalized + sNormalized - lNormalized * sNormalized, temp2 = double * lNormalized - temp1, phaseThird = phaseNumerator / triple, red = Math.min(rgbMax, rgbMax * channel(temp2, temp1, hNormalized + phaseThird)), green = Math.min(rgbMax, rgbMax * channel(temp2, temp1, hNormalized)), blue = Math.min(rgbMax, rgbMax * channel(temp2, temp1, hNormalized - phaseThird));
1370
+ return { r: Math.round(red), g: Math.round(green), b: Math.round(blue) };
1371
+ }
1372
+ function getRandomRgbColor(min) {
1373
+ const fixedMin = defaultRgbMin, fixedMax = rgbMax + identity, getRgbInRangeValue = () => Math.floor(getRandomInRange(fixedMin, fixedMax));
1374
+ return {
1375
+ b: getRgbInRangeValue(),
1376
+ g: getRgbInRangeValue(),
1377
+ r: getRgbInRangeValue(),
1378
+ };
1379
+ }
1380
+ function getStyleFromRgb(color, hdr, opacity) {
1381
+ const op = opacity ?? defaultOpacity, key = `rgb-${color.r.toFixed(rgbFixedPrecision)}-${color.g.toFixed(rgbFixedPrecision)}-${color.b.toFixed(rgbFixedPrecision)}-${hdr ? "hdr" : "sdr"}-${op.toString()}`;
1382
+ return getCachedStyle(key, () => (hdr ? getHdrStyleFromRgb(color, opacity) : getSdrStyleFromRgb(color, opacity)));
1383
+ }
1384
+ function getHdrStyleFromRgb(color, opacity) {
1385
+ return `color(display-p3 ${(color.r / rgbMax).toString()} ${(color.g / rgbMax).toString()} ${(color.b / rgbMax).toString()} / ${(opacity ?? defaultOpacity).toString()})`;
1386
+ }
1387
+ function getSdrStyleFromRgb(color, opacity) {
1388
+ return `rgba(${color.r.toString()}, ${color.g.toString()}, ${color.b.toString()}, ${(opacity ?? defaultOpacity).toString()})`;
1389
+ }
1390
+ function getStyleFromHsl(color, hdr, opacity) {
1391
+ const op = opacity ?? defaultOpacity, key = `hsl-${color.h.toFixed(hslFixedPrecision)}-${color.s.toFixed(hslFixedPrecision)}-${color.l.toFixed(hslFixedPrecision)}-${hdr ? "hdr" : "sdr"}-${op.toString()}`;
1392
+ return getCachedStyle(key, () => (hdr ? getHdrStyleFromHsl(color, opacity) : getSdrStyleFromHsl(color, opacity)));
1393
+ }
1394
+ function getHdrStyleFromHsl(color, opacity) {
1395
+ return getHdrStyleFromRgb(hslToRgb(color), opacity);
1396
+ }
1397
+ function getSdrStyleFromHsl(color, opacity) {
1398
+ return `hsla(${color.h.toString()}, ${color.s.toString()}%, ${color.l.toString()}%, ${(opacity ?? defaultOpacity).toString()})`;
1399
+ }
1400
+ function getHslFromAnimation(animation) {
1401
+ return animation === undefined
1402
+ ? undefined
1403
+ : {
1404
+ h: animation.h.value,
1405
+ s: animation.s.value,
1406
+ l: animation.l.value,
1407
+ };
1408
+ }
1409
+ function alterHsl(color, type, value) {
1410
+ return {
1411
+ h: color.h,
1412
+ s: color.s,
1413
+ l: color.l + (type === AlterType.darken ? -lFactor : lFactor) * value,
1414
+ };
1415
+ }
1416
+
1417
+ const fColorIndex = 0, sColorIndex = 1;
1418
+ function setTransformValue(factor, newFactor, key) {
1419
+ const newValue = newFactor[key];
1420
+ if (newValue !== undefined) {
1421
+ factor[key] = (factor[key] ?? defaultTransformValue) * newValue;
1422
+ }
1423
+ }
1424
+ class RenderManager {
1425
+ #canvasClearPlugins;
1426
+ #canvasManager;
1427
+ #canvasPaintPlugins;
1428
+ #clearDrawPlugins;
1429
+ #colorPlugins;
1430
+ #container;
1431
+ #context;
1432
+ #contextSettings;
1433
+ #drawParticlePlugins;
1434
+ #drawParticlesCleanupPlugins;
1435
+ #drawParticlesSetupPlugins;
1436
+ #drawPlugins;
1437
+ #drawSettingsCleanupPlugins;
1438
+ #drawSettingsSetupPlugins;
1439
+ #pluginManager;
1440
+ #postDrawUpdaters;
1441
+ #preDrawUpdaters;
1442
+ #reusableColorStyles = {};
1443
+ #reusablePluginColors = [undefined, undefined];
1444
+ #reusableTransform = {};
1445
+ constructor(pluginManager, container, canvasManager) {
1446
+ this.#pluginManager = pluginManager;
1447
+ this.#container = container;
1448
+ this.#canvasManager = canvasManager;
1449
+ this.#context = null;
1450
+ this.#preDrawUpdaters = [];
1451
+ this.#postDrawUpdaters = [];
1452
+ this.#colorPlugins = [];
1453
+ this.#canvasClearPlugins = [];
1454
+ this.#canvasPaintPlugins = [];
1455
+ this.#clearDrawPlugins = [];
1456
+ this.#drawParticlePlugins = [];
1457
+ this.#drawParticlesCleanupPlugins = [];
1458
+ this.#drawParticlesSetupPlugins = [];
1459
+ this.#drawPlugins = [];
1460
+ this.#drawSettingsSetupPlugins = [];
1461
+ this.#drawSettingsCleanupPlugins = [];
1462
+ }
1463
+ get settings() {
1464
+ return this.#contextSettings;
1465
+ }
1466
+ canvasClear() {
1467
+ if (!this.#container.actualOptions.clear) {
1468
+ return;
1469
+ }
1470
+ this.draw(ctx => {
1471
+ clear(ctx, this.#canvasManager.size);
1472
+ });
1473
+ }
1474
+ clear() {
1475
+ let pluginHandled = false;
1476
+ for (const plugin of this.#canvasClearPlugins) {
1477
+ pluginHandled = plugin.canvasClear?.() ?? false;
1478
+ if (pluginHandled) {
1479
+ break;
1480
+ }
1481
+ }
1482
+ if (pluginHandled) {
1483
+ return;
1484
+ }
1485
+ this.canvasClear();
1486
+ }
1487
+ destroy() {
1488
+ this.stop();
1489
+ this.#preDrawUpdaters = [];
1490
+ this.#postDrawUpdaters = [];
1491
+ this.#colorPlugins = [];
1492
+ this.#canvasClearPlugins = [];
1493
+ this.#canvasPaintPlugins = [];
1494
+ this.#clearDrawPlugins = [];
1495
+ this.#drawParticlePlugins = [];
1496
+ this.#drawParticlesCleanupPlugins = [];
1497
+ this.#drawParticlesSetupPlugins = [];
1498
+ this.#drawPlugins = [];
1499
+ this.#drawSettingsSetupPlugins = [];
1500
+ this.#drawSettingsCleanupPlugins = [];
1501
+ }
1502
+ draw(cb) {
1503
+ const ctx = this.#context;
1504
+ if (!ctx) {
1505
+ return;
1506
+ }
1507
+ return cb(ctx);
1508
+ }
1509
+ drawParticle(particle, delta) {
1510
+ if (particle.spawning || particle.destroyed) {
1511
+ return;
1512
+ }
1513
+ const radius = particle.getRadius();
1514
+ if (radius <= minimumSize) {
1515
+ return;
1516
+ }
1517
+ const pfColor = particle.getFillColor(), psColor = particle.getStrokeColor();
1518
+ let [fColor, sColor] = this.#getPluginParticleColors(particle);
1519
+ fColor ??= pfColor;
1520
+ sColor ??= psColor;
1521
+ if (!fColor && !sColor) {
1522
+ return;
1523
+ }
1524
+ const container = this.#container, zIndexOptions = particle.options.zIndex, zIndexFactor = zIndexFactorOffset - particle.zIndexFactor, { fillOpacity, opacity, strokeOpacity } = particle.getOpacity(), transform = this.#reusableTransform, colorStyles = this.#reusableColorStyles, fill = fColor ? getStyleFromHsl(fColor, container.hdr, fillOpacity * opacity) : undefined, stroke = sColor ? getStyleFromHsl(sColor, container.hdr, strokeOpacity * opacity) : fill;
1525
+ transform.a = transform.b = transform.c = transform.d = undefined;
1526
+ colorStyles.fill = fill;
1527
+ colorStyles.stroke = stroke;
1528
+ this.draw((context) => {
1529
+ for (const plugin of this.#drawParticlesSetupPlugins) {
1530
+ plugin.drawParticleSetup?.(context, particle, delta);
1531
+ }
1532
+ this.#applyPreDrawUpdaters(context, particle, radius, opacity, colorStyles, transform);
1533
+ drawParticle({
1534
+ container,
1535
+ context,
1536
+ particle,
1537
+ delta,
1538
+ colorStyles,
1539
+ radius: radius * zIndexFactor ** zIndexOptions.sizeRate,
1540
+ opacity: opacity,
1541
+ transform,
1542
+ });
1543
+ this.#applyPostDrawUpdaters(particle);
1544
+ for (const plugin of this.#drawParticlesCleanupPlugins) {
1545
+ plugin.drawParticleCleanup?.(context, particle, delta);
1546
+ }
1547
+ });
1548
+ }
1549
+ drawParticlePlugins(particle, delta) {
1550
+ this.draw(ctx => {
1551
+ for (const plugin of this.#drawParticlePlugins) {
1552
+ drawParticlePlugin(ctx, plugin, particle, delta);
1553
+ }
1554
+ });
1555
+ }
1556
+ drawParticles(delta) {
1557
+ const { particles } = this.#container;
1558
+ this.clear();
1559
+ particles.update(delta);
1560
+ this.draw(ctx => {
1561
+ for (const plugin of this.#drawSettingsSetupPlugins) {
1562
+ plugin.drawSettingsSetup?.(ctx, delta);
1563
+ }
1564
+ for (const plugin of this.#drawPlugins) {
1565
+ plugin.draw?.(ctx, delta);
1566
+ }
1567
+ particles.drawParticles(delta);
1568
+ for (const plugin of this.#clearDrawPlugins) {
1569
+ plugin.clearDraw?.(ctx, delta);
1570
+ }
1571
+ for (const plugin of this.#drawSettingsCleanupPlugins) {
1572
+ plugin.drawSettingsCleanup?.(ctx, delta);
1573
+ }
1574
+ });
1575
+ }
1576
+ init() {
1577
+ this.initUpdaters();
1578
+ this.initPlugins();
1579
+ this.paint();
1580
+ }
1581
+ initPlugins() {
1582
+ this.#colorPlugins = [];
1583
+ this.#canvasClearPlugins = [];
1584
+ this.#canvasPaintPlugins = [];
1585
+ this.#clearDrawPlugins = [];
1586
+ this.#drawParticlePlugins = [];
1587
+ this.#drawParticlesSetupPlugins = [];
1588
+ this.#drawParticlesCleanupPlugins = [];
1589
+ this.#drawPlugins = [];
1590
+ this.#drawSettingsSetupPlugins = [];
1591
+ this.#drawSettingsCleanupPlugins = [];
1592
+ for (const plugin of this.#container.plugins) {
1593
+ if (plugin.particleFillColor ?? plugin.particleStrokeColor) {
1594
+ this.#colorPlugins.push(plugin);
1595
+ }
1596
+ if (plugin.canvasClear) {
1597
+ this.#canvasClearPlugins.push(plugin);
1598
+ }
1599
+ if (plugin.canvasPaint) {
1600
+ this.#canvasPaintPlugins.push(plugin);
1601
+ }
1602
+ if (plugin.drawParticle) {
1603
+ this.#drawParticlePlugins.push(plugin);
1604
+ }
1605
+ if (plugin.drawParticleSetup) {
1606
+ this.#drawParticlesSetupPlugins.push(plugin);
1607
+ }
1608
+ if (plugin.drawParticleCleanup) {
1609
+ this.#drawParticlesCleanupPlugins.push(plugin);
1610
+ }
1611
+ if (plugin.draw) {
1612
+ this.#drawPlugins.push(plugin);
1613
+ }
1614
+ if (plugin.drawSettingsSetup) {
1615
+ this.#drawSettingsSetupPlugins.push(plugin);
1616
+ }
1617
+ if (plugin.drawSettingsCleanup) {
1618
+ this.#drawSettingsCleanupPlugins.push(plugin);
1619
+ }
1620
+ if (plugin.clearDraw) {
1621
+ this.#clearDrawPlugins.push(plugin);
1622
+ }
1623
+ }
1624
+ }
1625
+ initUpdaters() {
1626
+ this.#preDrawUpdaters = [];
1627
+ this.#postDrawUpdaters = [];
1628
+ for (const updater of this.#container.particleUpdaters) {
1629
+ if (updater.afterDraw) {
1630
+ this.#postDrawUpdaters.push(updater);
1631
+ }
1632
+ if (updater.getColorStyles ?? updater.getTransformValues ?? updater.beforeDraw) {
1633
+ this.#preDrawUpdaters.push(updater);
1634
+ }
1635
+ }
1636
+ }
1637
+ paint() {
1638
+ let handled = false;
1639
+ for (const plugin of this.#canvasPaintPlugins) {
1640
+ handled = plugin.canvasPaint?.() ?? false;
1641
+ if (handled) {
1642
+ break;
1643
+ }
1644
+ }
1645
+ if (handled) {
1646
+ return;
1647
+ }
1648
+ this.paintBase();
1649
+ }
1650
+ paintBase(baseColor) {
1651
+ this.draw(ctx => {
1652
+ paintBase(ctx, this.#canvasManager.size, baseColor);
1653
+ });
1654
+ }
1655
+ paintImage(image, opacity) {
1656
+ this.draw(ctx => {
1657
+ paintImage(ctx, this.#canvasManager.size, image, opacity);
1658
+ });
1659
+ }
1660
+ setContext(context) {
1661
+ this.#context = context;
1662
+ if (this.#context) {
1663
+ this.#context.globalCompositeOperation = defaultCompositeValue;
1664
+ }
1665
+ }
1666
+ setContextSettings(settings) {
1667
+ this.#contextSettings = settings;
1668
+ }
1669
+ stop() {
1670
+ this.draw(ctx => {
1671
+ clear(ctx, this.#canvasManager.size);
1672
+ });
1673
+ }
1674
+ #applyPostDrawUpdaters = particle => {
1675
+ for (const updater of this.#postDrawUpdaters) {
1676
+ updater.afterDraw?.(particle);
1677
+ }
1678
+ };
1679
+ #applyPreDrawUpdaters = (ctx, particle, radius, zOpacity, colorStyles, transform) => {
1680
+ for (const updater of this.#preDrawUpdaters) {
1681
+ if (updater.getColorStyles) {
1682
+ const { fill, stroke } = updater.getColorStyles(particle, ctx, radius, zOpacity);
1683
+ if (fill) {
1684
+ colorStyles.fill = fill;
1685
+ }
1686
+ if (stroke) {
1687
+ colorStyles.stroke = stroke;
1688
+ }
1689
+ }
1690
+ if (updater.getTransformValues) {
1691
+ const updaterTransform = updater.getTransformValues(particle);
1692
+ for (const key in updaterTransform) {
1693
+ setTransformValue(transform, updaterTransform, key);
1694
+ }
1695
+ }
1696
+ updater.beforeDraw?.(particle);
1697
+ }
1698
+ };
1699
+ #getPluginParticleColors = particle => {
1700
+ let fColor, sColor;
1701
+ for (const plugin of this.#colorPlugins) {
1702
+ if (!fColor && plugin.particleFillColor) {
1703
+ fColor = rangeColorToHsl(this.#pluginManager, plugin.particleFillColor(particle));
1704
+ }
1705
+ if (!sColor && plugin.particleStrokeColor) {
1706
+ sColor = rangeColorToHsl(this.#pluginManager, plugin.particleStrokeColor(particle));
1707
+ }
1708
+ if (fColor && sColor) {
1709
+ break;
1710
+ }
1711
+ }
1712
+ this.#reusablePluginColors[fColorIndex] = fColor;
1713
+ this.#reusablePluginColors[sColorIndex] = sColor;
1714
+ return this.#reusablePluginColors;
1715
+ };
1716
+ }
1717
+
1718
+ const transferredCanvases = new WeakMap(), getTransferredCanvas = (canvas) => {
1719
+ const transferredCanvas = transferredCanvases.get(canvas);
1720
+ if (transferredCanvas) {
1721
+ return transferredCanvas;
1722
+ }
1723
+ if (typeof canvas.transferControlToOffscreen !== "function") {
1724
+ throw new TypeError("OffscreenCanvas is required but not supported by this browser");
1725
+ }
1726
+ try {
1727
+ const offscreenCanvas = canvas.transferControlToOffscreen();
1728
+ transferredCanvases.set(canvas, offscreenCanvas);
1729
+ return offscreenCanvas;
1730
+ }
1731
+ catch {
1732
+ throw new TypeError("OffscreenCanvas transfer failed");
1733
+ }
1734
+ }, isHtmlCanvasElement = (canvas) => {
1735
+ return typeof HTMLCanvasElement !== "undefined" && canvas instanceof HTMLCanvasElement;
1736
+ };
1737
+ function setStyle(canvas, style, important = false) {
1738
+ if (!style) {
1739
+ return;
1740
+ }
1741
+ const element = canvas, elementStyle = element.style, keys = new Set();
1742
+ for (let i = 0; i < elementStyle.length; i++) {
1743
+ const key = elementStyle.item(i);
1744
+ if (!key) {
1745
+ continue;
1746
+ }
1747
+ keys.add(key);
1748
+ }
1749
+ for (let i = 0; i < style.length; i++) {
1750
+ const key = style.item(i);
1751
+ if (!key) {
1752
+ continue;
1753
+ }
1754
+ keys.add(key);
1755
+ }
1756
+ for (const key of keys) {
1757
+ const value = style.getPropertyValue(key);
1758
+ if (value) {
1759
+ elementStyle.setProperty(key, value, important ? "important" : "");
1760
+ }
1761
+ else {
1762
+ elementStyle.removeProperty(key);
1763
+ }
1764
+ }
1765
+ }
1766
+ class CanvasManager {
1767
+ domElement;
1768
+ render;
1769
+ renderCanvas;
1770
+ size;
1771
+ zoom = defaultZoom;
1772
+ #container;
1773
+ #generated;
1774
+ #mutationObserver;
1775
+ #originalStyle;
1776
+ #pluginManager;
1777
+ #pointerEvents;
1778
+ #resizePlugins;
1779
+ #standardSize;
1780
+ #zoomCenter;
1781
+ constructor(pluginManager, container) {
1782
+ this.#pluginManager = pluginManager;
1783
+ this.#container = container;
1784
+ this.render = new RenderManager(pluginManager, container, this);
1785
+ this.#standardSize = {
1786
+ height: 0,
1787
+ width: 0,
1788
+ };
1789
+ const pxRatio = container.retina.pixelRatio, stdSize = this.#standardSize;
1790
+ this.size = {
1791
+ height: stdSize.height * pxRatio,
1792
+ width: stdSize.width * pxRatio,
1793
+ };
1794
+ this.#generated = false;
1795
+ this.#resizePlugins = [];
1796
+ this.#pointerEvents = "none";
1797
+ }
1798
+ get #fullScreen() {
1799
+ return this.#container.actualOptions.fullScreen.enable;
1800
+ }
1801
+ destroy() {
1802
+ this.stop();
1803
+ if (this.#generated) {
1804
+ const element = this.domElement;
1805
+ element?.remove();
1806
+ this.domElement = undefined;
1807
+ this.renderCanvas = undefined;
1808
+ }
1809
+ else {
1810
+ this.#resetOriginalStyle();
1811
+ }
1812
+ this.render.destroy();
1813
+ this.#resizePlugins = [];
1814
+ }
1815
+ getZoomCenter() {
1816
+ const pxRatio = this.#container.retina.pixelRatio, { width, height } = this.size;
1817
+ if (this.#zoomCenter) {
1818
+ return this.#zoomCenter;
1819
+ }
1820
+ return {
1821
+ x: (width * half) / pxRatio,
1822
+ y: (height * half) / pxRatio,
1823
+ };
1824
+ }
1825
+ init() {
1826
+ this.#safeMutationObserver(obs => {
1827
+ obs.disconnect();
1828
+ });
1829
+ this.#mutationObserver = safeMutationObserver(records => {
1830
+ for (const record of records) {
1831
+ if (record.type === "attributes" && record.attributeName === "style") {
1832
+ this.#repairStyle();
1833
+ }
1834
+ }
1835
+ });
1836
+ this.resize();
1837
+ this.#initStyle();
1838
+ this.initBackground();
1839
+ this.#safeMutationObserver(obs => {
1840
+ const element = this.domElement;
1841
+ if (!element || !(element instanceof Node)) {
1842
+ return;
1843
+ }
1844
+ obs.observe(element, { attributes: true });
1845
+ });
1846
+ this.initPlugins();
1847
+ this.render.init();
1848
+ }
1849
+ initBackground() {
1850
+ const container = this.#container, options = container.actualOptions, background = options.background, element = this.domElement;
1851
+ if (!element) {
1852
+ return;
1853
+ }
1854
+ const elementStyle = element.style, color = rangeColorToRgb(this.#pluginManager, background.color);
1855
+ if (color) {
1856
+ elementStyle.backgroundColor = getStyleFromRgb(color, container.hdr, background.opacity);
1857
+ }
1858
+ else {
1859
+ elementStyle.backgroundColor = "";
1860
+ }
1861
+ elementStyle.backgroundImage = background.image || "";
1862
+ elementStyle.backgroundPosition = background.position || "";
1863
+ elementStyle.backgroundRepeat = background.repeat || "";
1864
+ elementStyle.backgroundSize = background.size || "";
1865
+ }
1866
+ initPlugins() {
1867
+ this.#resizePlugins = [];
1868
+ for (const plugin of this.#container.plugins) {
1869
+ if (plugin.resize) {
1870
+ this.#resizePlugins.push(plugin);
1871
+ }
1872
+ }
1873
+ }
1874
+ loadCanvas(canvas) {
1875
+ if (this.#generated && this.domElement) {
1876
+ this.domElement.remove();
1877
+ }
1878
+ const container = this.#container, domCanvas = isHtmlCanvasElement(canvas) ? canvas : undefined;
1879
+ this.domElement = domCanvas;
1880
+ this.#generated = domCanvas ? domCanvas.dataset[generatedAttribute] === "true" : false;
1881
+ this.renderCanvas = domCanvas ? getTransferredCanvas(domCanvas) : canvas;
1882
+ const domElement = this.domElement;
1883
+ if (domElement) {
1884
+ domElement.ariaHidden = "true";
1885
+ this.#originalStyle = cloneStyle(domElement.style);
1886
+ }
1887
+ const standardSize = this.#standardSize, renderCanvas = this.renderCanvas;
1888
+ if (domElement) {
1889
+ standardSize.height = domElement.offsetHeight;
1890
+ standardSize.width = domElement.offsetWidth;
1891
+ }
1892
+ else {
1893
+ standardSize.height = renderCanvas.height;
1894
+ standardSize.width = renderCanvas.width;
1895
+ }
1896
+ const pxRatio = this.#container.retina.pixelRatio, retinaSize = this.size;
1897
+ renderCanvas.height = retinaSize.height = standardSize.height * pxRatio;
1898
+ renderCanvas.width = retinaSize.width = standardSize.width * pxRatio;
1899
+ const canSupportHdrQuery = safeMatchMedia("(color-gamut: p3)");
1900
+ this.render.setContextSettings({
1901
+ alpha: true,
1902
+ colorSpace: canSupportHdrQuery?.matches && container.hdr ? "display-p3" : "srgb",
1903
+ desynchronized: true,
1904
+ willReadFrequently: false,
1905
+ });
1906
+ this.render.setContext(renderCanvas.getContext("2d", this.render.settings));
1907
+ this.#safeMutationObserver(obs => {
1908
+ obs.disconnect();
1909
+ });
1910
+ container.retina.init();
1911
+ this.initBackground();
1912
+ this.#safeMutationObserver(obs => {
1913
+ const element = this.domElement;
1914
+ if (!element || !(element instanceof Node)) {
1915
+ return;
1916
+ }
1917
+ obs.observe(element, { attributes: true });
1918
+ });
1919
+ }
1920
+ resize() {
1921
+ const element = this.domElement;
1922
+ if (!element) {
1923
+ return false;
1924
+ }
1925
+ const container = this.#container, renderCanvas = this.renderCanvas;
1926
+ if (renderCanvas === undefined) {
1927
+ return false;
1928
+ }
1929
+ const currentSize = container.canvas.#standardSize, newSize = {
1930
+ width: element.offsetWidth,
1931
+ height: element.offsetHeight,
1932
+ }, pxRatio = container.retina.pixelRatio, retinaSize = {
1933
+ width: newSize.width * pxRatio,
1934
+ height: newSize.height * pxRatio,
1935
+ };
1936
+ if (newSize.height === currentSize.height &&
1937
+ newSize.width === currentSize.width &&
1938
+ retinaSize.height === renderCanvas.height &&
1939
+ retinaSize.width === renderCanvas.width) {
1940
+ return false;
1941
+ }
1942
+ const oldSize = { ...currentSize };
1943
+ currentSize.height = newSize.height;
1944
+ currentSize.width = newSize.width;
1945
+ const canvasSize = this.size;
1946
+ renderCanvas.width = canvasSize.width = retinaSize.width;
1947
+ renderCanvas.height = canvasSize.height = retinaSize.height;
1948
+ if (this.#container.started) {
1949
+ container.particles.setResizeFactor({
1950
+ width: currentSize.width / oldSize.width,
1951
+ height: currentSize.height / oldSize.height,
1952
+ });
1953
+ }
1954
+ return true;
1955
+ }
1956
+ setPointerEvents(type) {
1957
+ const element = this.domElement;
1958
+ if (!element) {
1959
+ return;
1960
+ }
1961
+ this.#pointerEvents = type;
1962
+ this.#repairStyle();
1963
+ }
1964
+ setZoom(zoomLevel, center) {
1965
+ this.zoom = zoomLevel;
1966
+ this.#zoomCenter = center;
1967
+ }
1968
+ stop() {
1969
+ this.#safeMutationObserver(obs => {
1970
+ obs.disconnect();
1971
+ });
1972
+ this.#mutationObserver = undefined;
1973
+ this.render.stop();
1974
+ }
1975
+ async windowResize() {
1976
+ if (!this.domElement || !this.resize()) {
1977
+ return;
1978
+ }
1979
+ const container = this.#container, needsRefresh = container.updateActualOptions();
1980
+ container.particles.setDensity();
1981
+ this.#applyResizePlugins();
1982
+ if (needsRefresh) {
1983
+ await container.refresh();
1984
+ }
1985
+ }
1986
+ #applyResizePlugins = () => {
1987
+ for (const plugin of this.#resizePlugins) {
1988
+ plugin.resize?.();
1989
+ }
1990
+ };
1991
+ #initStyle = () => {
1992
+ const element = this.domElement, options = this.#container.actualOptions;
1993
+ if (!element) {
1994
+ return;
1995
+ }
1996
+ if (this.#fullScreen) {
1997
+ this.#setFullScreenStyle();
1998
+ }
1999
+ else {
2000
+ this.#resetOriginalStyle();
2001
+ }
2002
+ for (const key in options.style) {
2003
+ if (!key || !(key in options.style)) {
2004
+ continue;
2005
+ }
2006
+ const value = options.style[key];
2007
+ if (!value) {
2008
+ continue;
2009
+ }
2010
+ element.style.setProperty(key, value, "important");
2011
+ }
2012
+ };
2013
+ #repairStyle = () => {
2014
+ const element = this.domElement;
2015
+ if (!element) {
2016
+ return;
2017
+ }
2018
+ this.#safeMutationObserver(observer => {
2019
+ observer.disconnect();
2020
+ });
2021
+ this.#initStyle();
2022
+ this.initBackground();
2023
+ const pointerEvents = this.#pointerEvents;
2024
+ element.style.pointerEvents = pointerEvents;
2025
+ element.style.setProperty("pointer-events", pointerEvents);
2026
+ this.#safeMutationObserver(observer => {
2027
+ if (!(element instanceof Node)) {
2028
+ return;
2029
+ }
2030
+ observer.observe(element, { attributes: true });
2031
+ });
2032
+ };
2033
+ #resetOriginalStyle = () => {
2034
+ const element = this.domElement, originalStyle = this.#originalStyle;
2035
+ if (!element || !originalStyle) {
2036
+ return;
2037
+ }
2038
+ setStyle(element, originalStyle, true);
2039
+ };
2040
+ #safeMutationObserver = callback => {
2041
+ if (!this.#mutationObserver) {
2042
+ return;
2043
+ }
2044
+ callback(this.#mutationObserver);
2045
+ };
2046
+ #setFullScreenStyle = () => {
2047
+ const element = this.domElement;
2048
+ if (!element) {
2049
+ return;
2050
+ }
2051
+ setStyle(element, getFullScreenStyle(this.#container.actualOptions.fullScreen.zIndex), true);
2052
+ };
2053
+ }
2054
+
2055
+ class EventListeners {
2056
+ #container;
2057
+ #handlers;
2058
+ #resizeObserver;
2059
+ #resizeTimeout;
2060
+ constructor(container) {
2061
+ this.#container = container;
2062
+ this.#handlers = {
2063
+ visibilityChange: () => {
2064
+ this.#handleVisibilityChange();
2065
+ },
2066
+ resize: () => {
2067
+ this.#handleWindowResize();
2068
+ },
2069
+ };
2070
+ }
2071
+ addListeners() {
2072
+ this.#manageListeners(true);
2073
+ }
2074
+ removeListeners() {
2075
+ this.#manageListeners(false);
2076
+ }
2077
+ #handleVisibilityChange = () => {
2078
+ const container = this.#container, options = container.actualOptions;
2079
+ if (!options.pauseOnBlur) {
2080
+ return;
2081
+ }
2082
+ if (safeDocument().hidden) {
2083
+ container.pageHidden = true;
2084
+ container.pause();
2085
+ }
2086
+ else {
2087
+ container.pageHidden = false;
2088
+ if (container.animationStatus) {
2089
+ container.play(true);
2090
+ }
2091
+ else {
2092
+ container.draw(true);
2093
+ }
2094
+ }
2095
+ };
2096
+ #handleWindowResize = () => {
2097
+ if (this.#resizeTimeout) {
2098
+ clearTimeout(this.#resizeTimeout);
2099
+ this.#resizeTimeout = undefined;
2100
+ }
2101
+ const handleResize = async () => {
2102
+ const canvas = this.#container.canvas;
2103
+ await canvas.windowResize();
2104
+ };
2105
+ this.#resizeTimeout = setTimeout(() => void handleResize(), this.#container.actualOptions.resize.delay * millisecondsToSeconds);
2106
+ };
2107
+ #manageListeners = add => {
2108
+ const handlers = this.#handlers;
2109
+ this.#manageResize(add);
2110
+ manageListener(document, visibilityChangeEvent, handlers.visibilityChange, add, false);
2111
+ };
2112
+ #manageResize = add => {
2113
+ const handlers = this.#handlers, container = this.#container, options = container.actualOptions;
2114
+ if (!options.resize.enable) {
2115
+ return;
2116
+ }
2117
+ if (typeof ResizeObserver === "undefined") {
2118
+ manageListener(globalThis, resizeEvent, handlers.resize, add);
2119
+ return;
2120
+ }
2121
+ const canvasEl = container.canvas.domElement;
2122
+ if (this.#resizeObserver && !add) {
2123
+ if (canvasEl) {
2124
+ this.#resizeObserver.unobserve(canvasEl);
2125
+ }
2126
+ this.#resizeObserver.disconnect();
2127
+ this.#resizeObserver = undefined;
2128
+ }
2129
+ else if (!this.#resizeObserver && add && canvasEl) {
2130
+ this.#resizeObserver = new ResizeObserver((entries) => {
2131
+ const entry = entries.find(e => e.target === canvasEl);
2132
+ if (!entry) {
2133
+ return;
2134
+ }
2135
+ this.#handleWindowResize();
2136
+ });
2137
+ this.#resizeObserver.observe(canvasEl);
2138
+ }
2139
+ };
2140
+ }
2141
+
2142
+ function loadEffectData(effect, effectOptions, id, reduceDuplicates) {
2143
+ const effectData = effectOptions.options[effect];
2144
+ return deepExtend({
2145
+ close: effectOptions.close,
2146
+ }, itemFromSingleOrMultiple(effectData, id, reduceDuplicates));
2147
+ }
2148
+ function loadShapeData(shape, shapeOptions, id, reduceDuplicates) {
2149
+ const shapeData = shapeOptions.options[shape];
2150
+ return deepExtend({
2151
+ close: shapeOptions.close,
2152
+ }, itemFromSingleOrMultiple(shapeData, id, reduceDuplicates));
2153
+ }
2154
+ function fixOutMode(data) {
2155
+ if (!isInArray(data.outMode, data.checkModes)) {
2156
+ return;
2157
+ }
2158
+ const diameter = data.radius * double;
2159
+ if (data.coord > data.maxCoord - diameter) {
2160
+ data.setCb(-data.radius);
2161
+ }
2162
+ else if (data.coord < diameter) {
2163
+ data.setCb(data.radius);
2164
+ }
2165
+ }
2166
+ class Particle {
2167
+ backColor;
2168
+ bubble;
2169
+ destroyed;
2170
+ direction;
2171
+ effect;
2172
+ effectClose;
2173
+ effectData;
2174
+ fillColor;
2175
+ fillEnabled;
2176
+ fillOpacity;
2177
+ group;
2178
+ id;
2179
+ ignoresResizeRatio;
2180
+ initialPosition;
2181
+ initialVelocity;
2182
+ isRotating;
2183
+ justWarped;
2184
+ lastPathTime;
2185
+ misplaced;
2186
+ moveCenter;
2187
+ offset;
2188
+ opacity;
2189
+ options;
2190
+ outType;
2191
+ pathRotation;
2192
+ position;
2193
+ randomIndexData;
2194
+ retina;
2195
+ roll;
2196
+ rotation;
2197
+ shape;
2198
+ shapeClose;
2199
+ shapeData;
2200
+ sides;
2201
+ size;
2202
+ slow;
2203
+ spawning;
2204
+ strokeColor;
2205
+ strokeOpacity;
2206
+ strokeWidth;
2207
+ unbreakable;
2208
+ velocity;
2209
+ zIndexFactor;
2210
+ #cachedOpacityData = {
2211
+ fillOpacity: defaultOpacity,
2212
+ opacity: defaultOpacity,
2213
+ strokeOpacity: defaultOpacity,
2214
+ };
2215
+ #cachedPosition = Vector3d.origin;
2216
+ #cachedRotateData = { sin: 0, cos: 0 };
2217
+ #cachedTransform = {
2218
+ a: 1,
2219
+ b: 0,
2220
+ c: 0,
2221
+ d: 1,
2222
+ };
2223
+ #container;
2224
+ #pluginManager;
2225
+ constructor(pluginManager, container) {
2226
+ this.#pluginManager = pluginManager;
2227
+ this.#container = container;
2228
+ }
2229
+ destroy(override) {
2230
+ if (this.unbreakable || this.destroyed) {
2231
+ return;
2232
+ }
2233
+ this.destroyed = true;
2234
+ this.bubble.inRange = false;
2235
+ this.slow.inRange = false;
2236
+ const container = this.#container, shapeDrawer = this.shape ? container.shapeDrawers.get(this.shape) : undefined;
2237
+ shapeDrawer?.particleDestroy?.(this);
2238
+ for (const plugin of container.particleDestroyedPlugins) {
2239
+ plugin.particleDestroyed?.(this, override);
2240
+ }
2241
+ for (const updater of container.particleUpdaters) {
2242
+ updater.particleDestroyed?.(this, override);
2243
+ }
2244
+ this.#container.dispatchEvent(EventType.particleDestroyed, {
2245
+ particle: this,
2246
+ });
2247
+ }
2248
+ draw(delta) {
2249
+ const container = this.#container, render = container.canvas.render;
2250
+ render.drawParticlePlugins(this, delta);
2251
+ render.drawParticle(this, delta);
2252
+ }
2253
+ getAngle() {
2254
+ return this.rotation + (this.pathRotation ? this.velocity.angle : defaultAngle);
2255
+ }
2256
+ getFillColor() {
2257
+ return this.#getRollColor(this.bubble.color ?? getHslFromAnimation(this.fillColor));
2258
+ }
2259
+ getMass() {
2260
+ return this.getRadius() ** squareExp * Math.PI * half;
2261
+ }
2262
+ getOpacity() {
2263
+ const zIndexOptions = this.options.zIndex, zIndexFactor = zIndexFactorOffset - this.zIndexFactor, zOpacityFactor = zIndexFactor ** zIndexOptions.opacityRate, opacity = this.bubble.opacity ?? getRangeValue(this.opacity?.value ?? defaultOpacity), fillOpacity = this.fillOpacity ?? defaultOpacity, strokeOpacity = this.strokeOpacity ?? defaultOpacity;
2264
+ this.#cachedOpacityData.fillOpacity = opacity * fillOpacity * zOpacityFactor;
2265
+ this.#cachedOpacityData.opacity = opacity * zOpacityFactor;
2266
+ this.#cachedOpacityData.strokeOpacity = opacity * strokeOpacity * zOpacityFactor;
2267
+ return this.#cachedOpacityData;
2268
+ }
2269
+ getPosition() {
2270
+ this.#cachedPosition.x = this.position.x + this.offset.x;
2271
+ this.#cachedPosition.y = this.position.y + this.offset.y;
2272
+ this.#cachedPosition.z = this.position.z;
2273
+ return this.#cachedPosition;
2274
+ }
2275
+ getRadius() {
2276
+ return this.bubble.radius ?? this.size.value;
2277
+ }
2278
+ getRotateData() {
2279
+ const angle = this.getAngle();
2280
+ this.#cachedRotateData.sin = Math.sin(angle);
2281
+ this.#cachedRotateData.cos = Math.cos(angle);
2282
+ return this.#cachedRotateData;
2283
+ }
2284
+ getStrokeColor() {
2285
+ return this.#getRollColor(this.bubble.color ?? getHslFromAnimation(this.strokeColor));
2286
+ }
2287
+ getTransformData(externalTransform) {
2288
+ const rotateData = this.getRotateData(), rotating = this.isRotating;
2289
+ this.#cachedTransform.a = rotateData.cos * (externalTransform.a ?? defaultTransform.a);
2290
+ this.#cachedTransform.b = rotating
2291
+ ? rotateData.sin * (externalTransform.b ?? identity)
2292
+ : (externalTransform.b ?? defaultTransform.b);
2293
+ this.#cachedTransform.c = rotating
2294
+ ? -rotateData.sin * (externalTransform.c ?? identity)
2295
+ : (externalTransform.c ?? defaultTransform.c);
2296
+ this.#cachedTransform.d = rotateData.cos * (externalTransform.d ?? defaultTransform.d);
2297
+ return this.#cachedTransform;
2298
+ }
2299
+ init(id, position, overrideOptions, group) {
2300
+ const container = this.#container;
2301
+ this.id = id;
2302
+ this.group = group;
2303
+ this.justWarped = false;
2304
+ this.effectClose = true;
2305
+ this.shapeClose = true;
2306
+ this.pathRotation = false;
2307
+ this.lastPathTime = 0;
2308
+ this.destroyed = false;
2309
+ this.unbreakable = false;
2310
+ this.isRotating = false;
2311
+ this.rotation = 0;
2312
+ this.misplaced = false;
2313
+ this.retina = {
2314
+ maxDistance: {},
2315
+ maxSpeed: 0,
2316
+ moveDrift: 0,
2317
+ moveSpeed: 0,
2318
+ sizeAnimationSpeed: 0,
2319
+ };
2320
+ this.size = {
2321
+ value: 1,
2322
+ max: 1,
2323
+ min: 1,
2324
+ enable: false,
2325
+ };
2326
+ this.outType = ParticleOutType.normal;
2327
+ this.ignoresResizeRatio = true;
2328
+ const mainOptions = container.actualOptions, particlesOptions = loadParticlesOptions(this.#pluginManager, container, mainOptions.particles), reduceDuplicates = particlesOptions.reduceDuplicates, effectType = particlesOptions.effect.type, shapeType = particlesOptions.shape.type;
2329
+ this.effect = itemFromSingleOrMultiple(effectType, this.id, reduceDuplicates);
2330
+ this.shape = itemFromSingleOrMultiple(shapeType, this.id, reduceDuplicates);
2331
+ const effectOptions = particlesOptions.effect, shapeOptions = particlesOptions.shape;
2332
+ if (overrideOptions) {
2333
+ if (overrideOptions.effect?.type && overrideOptions.effect.type !== this.effect) {
2334
+ const overrideEffectType = overrideOptions.effect.type, effect = itemFromSingleOrMultiple(overrideEffectType, this.id, reduceDuplicates);
2335
+ if (effect) {
2336
+ this.effect = effect;
2337
+ effectOptions.load(overrideOptions.effect);
2338
+ }
2339
+ }
2340
+ if (overrideOptions.shape?.type && overrideOptions.shape.type !== this.shape) {
2341
+ const overrideShapeType = overrideOptions.shape.type, shape = itemFromSingleOrMultiple(overrideShapeType, this.id, reduceDuplicates);
2342
+ if (shape) {
2343
+ this.shape = shape;
2344
+ shapeOptions.load(overrideOptions.shape);
2345
+ }
2346
+ }
2347
+ }
2348
+ if (this.effect === randomColorValue) {
2349
+ const availableEffects = [...this.#container.effectDrawers.keys()];
2350
+ this.effect = availableEffects[Math.floor(getRandom() * availableEffects.length)];
2351
+ }
2352
+ if (this.shape === randomColorValue) {
2353
+ const availableShapes = [...this.#container.shapeDrawers.keys()];
2354
+ this.shape = availableShapes[Math.floor(getRandom() * availableShapes.length)];
2355
+ }
2356
+ this.effectData = this.effect ? loadEffectData(this.effect, effectOptions, this.id, reduceDuplicates) : undefined;
2357
+ this.shapeData = this.shape ? loadShapeData(this.shape, shapeOptions, this.id, reduceDuplicates) : undefined;
2358
+ particlesOptions.load(overrideOptions);
2359
+ const effectData = this.effectData, shapeData = this.shapeData;
2360
+ if (effectData) {
2361
+ particlesOptions.load(effectData.particles);
2362
+ }
2363
+ if (shapeData) {
2364
+ particlesOptions.load(shapeData.particles);
2365
+ }
2366
+ this.effectClose = effectData?.close ?? particlesOptions.effect.close;
2367
+ this.shapeClose = shapeData?.close ?? particlesOptions.shape.close;
2368
+ this.options = particlesOptions;
2369
+ container.retina.initParticle(this);
2370
+ for (const updater of container.particleUpdaters) {
2371
+ updater.preInit?.(this);
2372
+ }
2373
+ this.bubble = {
2374
+ inRange: false,
2375
+ };
2376
+ this.slow = {
2377
+ inRange: false,
2378
+ factor: 1,
2379
+ };
2380
+ this.#initPosition(position);
2381
+ this.initialVelocity = this.#calculateVelocity();
2382
+ this.velocity = this.initialVelocity.copy();
2383
+ this.zIndexFactor = this.position.z / container.zLayers;
2384
+ this.sides = 24;
2385
+ let effectDrawer, shapeDrawer;
2386
+ if (this.effect) {
2387
+ effectDrawer = container.effectDrawers.get(this.effect);
2388
+ }
2389
+ if (effectDrawer?.loadEffect) {
2390
+ effectDrawer.loadEffect(this);
2391
+ }
2392
+ if (this.shape) {
2393
+ shapeDrawer = container.shapeDrawers.get(this.shape);
2394
+ }
2395
+ if (shapeDrawer?.loadShape) {
2396
+ shapeDrawer.loadShape(this);
2397
+ }
2398
+ const sideCountFunc = shapeDrawer?.getSidesCount;
2399
+ if (sideCountFunc) {
2400
+ this.sides = sideCountFunc(this);
2401
+ }
2402
+ this.spawning = false;
2403
+ for (const updater of container.particleUpdaters) {
2404
+ updater.init(this);
2405
+ }
2406
+ effectDrawer?.particleInit?.(container, this);
2407
+ shapeDrawer?.particleInit?.(container, this);
2408
+ for (const plugin of container.particleCreatedPlugins) {
2409
+ plugin.particleCreated?.(this);
2410
+ }
2411
+ }
2412
+ isInsideCanvas(direction) {
2413
+ return this.#getInsideCanvasResult({ direction }).inside;
2414
+ }
2415
+ isInsideCanvasForOutMode(outMode, direction) {
2416
+ return this.#getInsideCanvasResult({ direction, outMode }).inside;
2417
+ }
2418
+ isShowingBack() {
2419
+ if (!this.roll) {
2420
+ return false;
2421
+ }
2422
+ const angle = this.roll.angle;
2423
+ if (this.roll.horizontal && this.roll.vertical) {
2424
+ const normalizedAngle = angle % doublePI, adjustedAngle = normalizedAngle < defaultAngle ? normalizedAngle + doublePI : normalizedAngle;
2425
+ return adjustedAngle >= Math.PI * half && adjustedAngle < Math.PI * triple * half;
2426
+ }
2427
+ if (this.roll.horizontal) {
2428
+ const normalizedAngle = (angle + Math.PI * half) % (Math.PI * double), adjustedAngle = normalizedAngle < defaultAngle ? normalizedAngle + Math.PI * double : normalizedAngle;
2429
+ return adjustedAngle >= Math.PI && adjustedAngle < Math.PI * double;
2430
+ }
2431
+ if (this.roll.vertical) {
2432
+ const normalizedAngle = angle % (Math.PI * double), adjustedAngle = normalizedAngle < defaultAngle ? normalizedAngle + Math.PI * double : normalizedAngle;
2433
+ return adjustedAngle >= Math.PI && adjustedAngle < Math.PI * double;
2434
+ }
2435
+ return false;
2436
+ }
2437
+ isVisible() {
2438
+ return !this.destroyed && !this.spawning && this.isInsideCanvas();
2439
+ }
2440
+ reset() {
2441
+ for (const updater of this.#container.particleUpdaters) {
2442
+ updater.reset?.(this);
2443
+ }
2444
+ }
2445
+ #calcPosition = (position, zIndex) => {
2446
+ let tryCount = defaultRetryCount, posVec = position ? Vector3d.create(position.x, position.y, zIndex) : undefined;
2447
+ const container = this.#container, plugins = container.particlePositionPlugins, outModes = this.options.move.outModes, radius = this.getRadius(), canvasSize = container.canvas.size, abortController = new AbortController(), { signal } = abortController;
2448
+ while (!signal.aborted) {
2449
+ for (const plugin of plugins) {
2450
+ const pluginPos = plugin.particlePosition?.(posVec, this);
2451
+ if (pluginPos) {
2452
+ return Vector3d.create(pluginPos.x, pluginPos.y, zIndex);
2453
+ }
2454
+ }
2455
+ const exactPosition = calcExactPositionOrRandomFromSize({
2456
+ size: canvasSize,
2457
+ position: posVec,
2458
+ }), pos = Vector3d.create(exactPosition.x, exactPosition.y, zIndex);
2459
+ this.#fixHorizontal(pos, radius, outModes.left ?? outModes.default);
2460
+ this.#fixHorizontal(pos, radius, outModes.right ?? outModes.default);
2461
+ this.#fixVertical(pos, radius, outModes.top ?? outModes.default);
2462
+ this.#fixVertical(pos, radius, outModes.bottom ?? outModes.default);
2463
+ let isValidPosition = true;
2464
+ for (const plugin of container.particles.checkParticlePositionPlugins) {
2465
+ isValidPosition = plugin.checkParticlePosition?.(this, pos, tryCount) ?? true;
2466
+ if (!isValidPosition) {
2467
+ break;
2468
+ }
2469
+ }
2470
+ if (isValidPosition) {
2471
+ return pos;
2472
+ }
2473
+ tryCount += tryCountIncrement;
2474
+ posVec = undefined;
2475
+ }
2476
+ return posVec;
2477
+ };
2478
+ #calculateVelocity = () => {
2479
+ const moveOptions = this.options.move, baseVelocity = getParticleBaseVelocity(this.direction), res = baseVelocity.copy();
2480
+ if (moveOptions.direction === MoveDirection.inside || moveOptions.direction === MoveDirection.outside) {
2481
+ return res;
2482
+ }
2483
+ const rad = degToRad(getRangeValue(moveOptions.angle.value)), radOffset = degToRad(getRangeValue(moveOptions.angle.offset)), range = {
2484
+ left: radOffset - rad * half,
2485
+ right: radOffset + rad * half,
2486
+ };
2487
+ if (!moveOptions.straight) {
2488
+ res.angle += randomInRangeValue(setRangeValue(range.left, range.right));
2489
+ }
2490
+ if (moveOptions.random && typeof moveOptions.speed === "number") {
2491
+ res.length *= getRandom();
2492
+ }
2493
+ return res;
2494
+ };
2495
+ #fixHorizontal = (pos, radius, outMode) => {
2496
+ fixOutMode({
2497
+ outMode,
2498
+ checkModes: [OutMode.bounce],
2499
+ coord: pos.x,
2500
+ maxCoord: this.#container.canvas.size.width,
2501
+ setCb: (value) => (pos.x += value),
2502
+ radius,
2503
+ });
2504
+ };
2505
+ #fixVertical = (pos, radius, outMode) => {
2506
+ fixOutMode({
2507
+ outMode,
2508
+ checkModes: [OutMode.bounce],
2509
+ coord: pos.y,
2510
+ maxCoord: this.#container.canvas.size.height,
2511
+ setCb: (value) => (pos.y += value),
2512
+ radius,
2513
+ });
2514
+ };
2515
+ #getDefaultInsideCanvasResult = (direction, outMode) => {
2516
+ const radius = this.getRadius(), canvasSize = this.#container.canvas.size, position = this.position, isBounce = outMode === OutMode.bounce;
2517
+ if (direction === OutModeDirection.bottom) {
2518
+ return {
2519
+ inside: isBounce ? position.y + radius < canvasSize.height : position.y - radius < canvasSize.height,
2520
+ reason: "default",
2521
+ };
2522
+ }
2523
+ if (direction === OutModeDirection.left) {
2524
+ return {
2525
+ inside: isBounce ? position.x - radius > defaultAngle : position.x + radius > defaultAngle,
2526
+ reason: "default",
2527
+ };
2528
+ }
2529
+ if (direction === OutModeDirection.right) {
2530
+ return {
2531
+ inside: isBounce ? position.x + radius < canvasSize.width : position.x - radius < canvasSize.width,
2532
+ reason: "default",
2533
+ };
2534
+ }
2535
+ if (direction === OutModeDirection.top) {
2536
+ return {
2537
+ inside: isBounce ? position.y - radius > defaultAngle : position.y + radius > defaultAngle,
2538
+ reason: "default",
2539
+ };
2540
+ }
2541
+ return {
2542
+ inside: position.x >= -radius &&
2543
+ position.y >= -radius &&
2544
+ position.y <= canvasSize.height + radius &&
2545
+ position.x <= canvasSize.width + radius,
2546
+ reason: "default",
2547
+ };
2548
+ };
2549
+ #getInsideCanvasCallbackData = (direction, outMode) => {
2550
+ return {
2551
+ canvasSize: this.#container.canvas.size,
2552
+ direction,
2553
+ outMode,
2554
+ particle: this,
2555
+ radius: this.getRadius(),
2556
+ };
2557
+ };
2558
+ #getInsideCanvasResult = (data) => {
2559
+ const defaultResult = this.#getDefaultInsideCanvasResult(data.direction, data.outMode), container = this.#container, shapeDrawer = this.shape ? container.shapeDrawers.get(this.shape) : undefined, effectDrawer = this.effect ? container.effectDrawers.get(this.effect) : undefined, shapeCheck = shapeDrawer?.isInsideCanvas, effectCheck = effectDrawer?.isInsideCanvas;
2560
+ if (!shapeCheck && !effectCheck) {
2561
+ return defaultResult;
2562
+ }
2563
+ const callbackData = this.#getInsideCanvasCallbackData(data.direction, data.outMode), shapeResult = shapeCheck ? this.#normalizeInsideCanvasResult(shapeCheck(callbackData), "shape") : undefined, effectResult = effectCheck ? this.#normalizeInsideCanvasResult(effectCheck(callbackData), "effect") : undefined;
2564
+ if (shapeResult && effectResult) {
2565
+ const margin = Math.max(shapeResult.margin ?? defaultAngle, effectResult.margin ?? defaultAngle);
2566
+ return {
2567
+ inside: shapeResult.inside && effectResult.inside,
2568
+ margin: margin > defaultAngle ? margin : undefined,
2569
+ reason: "combined",
2570
+ };
2571
+ }
2572
+ return shapeResult ?? effectResult ?? defaultResult;
2573
+ };
2574
+ #getRollColor = color => {
2575
+ if (!color || !this.roll || (!this.backColor && !this.roll.alter)) {
2576
+ return color;
2577
+ }
2578
+ if (!this.isShowingBack()) {
2579
+ return color;
2580
+ }
2581
+ if (this.backColor) {
2582
+ return this.backColor;
2583
+ }
2584
+ if (this.roll.alter) {
2585
+ return alterHsl(color, this.roll.alter.type, this.roll.alter.value);
2586
+ }
2587
+ return color;
2588
+ };
2589
+ #initPosition = position => {
2590
+ const container = this.#container, zIndexValue = Math.floor(getRangeValue(this.options.zIndex.value)), initialPosition = this.#calcPosition(position, clamp(zIndexValue, minZ, container.zLayers));
2591
+ if (!initialPosition) {
2592
+ throw new Error("a valid position cannot be found for particle");
2593
+ }
2594
+ this.position = initialPosition;
2595
+ this.initialPosition = this.position.copy();
2596
+ const canvasSize = container.canvas.size;
2597
+ this.moveCenter = {
2598
+ ...getPosition(this.options.move.center, canvasSize),
2599
+ radius: this.options.move.center.radius,
2600
+ mode: this.options.move.center.mode,
2601
+ };
2602
+ this.direction = getParticleDirectionAngle(this.options.move.direction, this.position, this.moveCenter);
2603
+ switch (this.options.move.direction) {
2604
+ case MoveDirection.inside:
2605
+ this.outType = ParticleOutType.inside;
2606
+ break;
2607
+ case MoveDirection.outside:
2608
+ this.outType = ParticleOutType.outside;
2609
+ break;
2610
+ }
2611
+ this.offset = Vector.origin;
2612
+ };
2613
+ #normalizeInsideCanvasResult = (result, reason) => {
2614
+ if (typeof result === "boolean") {
2615
+ return {
2616
+ inside: result,
2617
+ reason,
2618
+ };
2619
+ }
2620
+ return {
2621
+ inside: result.inside,
2622
+ margin: result.margin,
2623
+ reason: result.reason ?? reason,
2624
+ };
2625
+ };
2626
+ }
2627
+
2628
+ class SpatialHashGrid {
2629
+ #cellSize;
2630
+ #cells = new Map();
2631
+ #circlePool = [];
2632
+ #circlePoolIdx;
2633
+ #pendingCellSize;
2634
+ #rectanglePool = [];
2635
+ #rectanglePoolIdx;
2636
+ constructor(cellSize) {
2637
+ this.#cellSize = cellSize;
2638
+ this.#circlePoolIdx = 0;
2639
+ this.#rectanglePoolIdx = 0;
2640
+ }
2641
+ clear() {
2642
+ this.#cells.clear();
2643
+ const pendingCellSize = this.#pendingCellSize;
2644
+ if (pendingCellSize) {
2645
+ this.#cellSize = pendingCellSize;
2646
+ }
2647
+ this.#pendingCellSize = undefined;
2648
+ }
2649
+ insert(particle) {
2650
+ const { x, y } = particle.getPosition(), key = this.#cellKeyFromCoords(x, y);
2651
+ if (!this.#cells.has(key)) {
2652
+ this.#cells.set(key, []);
2653
+ }
2654
+ this.#cells.get(key)?.push(particle);
2655
+ }
2656
+ query(range, check, out = []) {
2657
+ const bounds = this.#getRangeBounds(range);
2658
+ if (!bounds) {
2659
+ return out;
2660
+ }
2661
+ const minCellX = Math.floor(bounds.minX / this.#cellSize), maxCellX = Math.floor(bounds.maxX / this.#cellSize), minCellY = Math.floor(bounds.minY / this.#cellSize), maxCellY = Math.floor(bounds.maxY / this.#cellSize);
2662
+ for (let cx = minCellX; cx <= maxCellX; cx++) {
2663
+ for (let cy = minCellY; cy <= maxCellY; cy++) {
2664
+ const key = `${cx}_${cy}`, cellParticles = this.#cells.get(key);
2665
+ if (!cellParticles) {
2666
+ continue;
2667
+ }
2668
+ for (const p of cellParticles) {
2669
+ if (check && !check(p)) {
2670
+ continue;
2671
+ }
2672
+ if (range.contains(p.getPosition())) {
2673
+ out.push(p);
2674
+ }
2675
+ }
2676
+ }
2677
+ }
2678
+ return out;
2679
+ }
2680
+ queryCircle(position, radius, check, out = []) {
2681
+ const circle = this.#acquireCircle(position.x, position.y, radius), result = this.query(circle, check, out);
2682
+ this.#releaseShapes();
2683
+ return result;
2684
+ }
2685
+ queryRectangle(position, size, check, out = []) {
2686
+ const rect = this.#acquireRectangle(position.x, position.y, size.width, size.height), result = this.query(rect, check, out);
2687
+ this.#releaseShapes();
2688
+ return result;
2689
+ }
2690
+ setCellSize(cellSize) {
2691
+ this.#pendingCellSize = cellSize;
2692
+ }
2693
+ #acquireCircle(x, y, r) {
2694
+ return (this.#circlePool[this.#circlePoolIdx++] ??= new Circle(x, y, r)).reset(x, y, r);
2695
+ }
2696
+ #acquireRectangle(x, y, w, h) {
2697
+ return (this.#rectanglePool[this.#rectanglePoolIdx++] ??= new Rectangle(x, y, w, h)).reset(x, y, w, h);
2698
+ }
2699
+ #cellKeyFromCoords(x, y) {
2700
+ const cellX = Math.floor(x / this.#cellSize), cellY = Math.floor(y / this.#cellSize);
2701
+ return `${cellX}_${cellY}`;
2702
+ }
2703
+ #getRangeBounds(range) {
2704
+ if (range instanceof Circle) {
2705
+ const r = range.radius, { x, y } = range.position;
2706
+ return {
2707
+ minX: x - r,
2708
+ maxX: x + r,
2709
+ minY: y - r,
2710
+ maxY: y + r,
2711
+ };
2712
+ }
2713
+ if (range instanceof Rectangle) {
2714
+ const { x, y } = range.position, { width, height } = range.size;
2715
+ return {
2716
+ minX: x,
2717
+ maxX: x + width,
2718
+ minY: y,
2719
+ maxY: y + height,
2720
+ };
2721
+ }
2722
+ return null;
2723
+ }
2724
+ #releaseShapes() {
2725
+ this.#circlePoolIdx = 0;
2726
+ this.#rectanglePoolIdx = 0;
2727
+ }
2728
+ }
2729
+
2730
+ class ParticlesManager {
2731
+ checkParticlePositionPlugins;
2732
+ grid;
2733
+ #array;
2734
+ #container;
2735
+ #groupLimits;
2736
+ #limit;
2737
+ #nextId;
2738
+ #particleBuckets;
2739
+ #particleResetPlugins;
2740
+ #particleUpdatePlugins;
2741
+ #pluginManager;
2742
+ #pool;
2743
+ #postParticleUpdatePlugins;
2744
+ #postUpdatePlugins;
2745
+ #resizeFactor;
2746
+ #updatePlugins;
2747
+ #zBuckets;
2748
+ constructor(pluginManager, container) {
2749
+ this.#pluginManager = pluginManager;
2750
+ this.#container = container;
2751
+ this.#nextId = 0;
2752
+ this.#array = [];
2753
+ this.#pool = [];
2754
+ this.#limit = 0;
2755
+ this.#groupLimits = new Map();
2756
+ this.#particleBuckets = new Map();
2757
+ this.#zBuckets = this.#createBuckets(this.#container.zLayers);
2758
+ this.grid = new SpatialHashGrid(spatialHashGridCellSize);
2759
+ this.checkParticlePositionPlugins = [];
2760
+ this.#particleResetPlugins = [];
2761
+ this.#particleUpdatePlugins = [];
2762
+ this.#postUpdatePlugins = [];
2763
+ this.#postParticleUpdatePlugins = [];
2764
+ this.#updatePlugins = [];
2765
+ }
2766
+ get count() {
2767
+ return this.#array.length;
2768
+ }
2769
+ addParticle(position, overrideOptions, group, initializer) {
2770
+ const limitMode = this.#container.actualOptions.particles.number.limit.mode, limit = group === undefined ? this.#limit : (this.#groupLimits.get(group) ?? this.#limit), currentCount = this.count;
2771
+ if (limit > minLimit) {
2772
+ switch (limitMode) {
2773
+ case LimitMode.delete: {
2774
+ const countToRemove = currentCount + countOffset - limit;
2775
+ if (countToRemove > minCount) {
2776
+ this.removeQuantity(countToRemove);
2777
+ }
2778
+ break;
2779
+ }
2780
+ case LimitMode.wait:
2781
+ if (currentCount >= limit) {
2782
+ return;
2783
+ }
2784
+ break;
2785
+ }
2786
+ }
2787
+ try {
2788
+ const particle = this.#pool.pop() ?? new Particle(this.#pluginManager, this.#container);
2789
+ particle.init(this.#nextId, position, overrideOptions, group);
2790
+ let canAdd = true;
2791
+ if (initializer) {
2792
+ canAdd = initializer(particle);
2793
+ }
2794
+ if (!canAdd) {
2795
+ this.#pool.push(particle);
2796
+ return;
2797
+ }
2798
+ this.#array.push(particle);
2799
+ this.#insertParticleIntoBucket(particle);
2800
+ this.#nextId++;
2801
+ this.#container.dispatchEvent(EventType.particleAdded, {
2802
+ particle,
2803
+ });
2804
+ return particle;
2805
+ }
2806
+ catch (e) {
2807
+ getLogger().warning(`error adding particle: ${e}`);
2808
+ }
2809
+ return undefined;
2810
+ }
2811
+ clear() {
2812
+ this.#array = [];
2813
+ this.#particleBuckets.clear();
2814
+ this.#resetBuckets(this.#container.zLayers);
2815
+ }
2816
+ destroy() {
2817
+ this.#array = [];
2818
+ this.#pool.length = 0;
2819
+ this.#particleBuckets.clear();
2820
+ this.#zBuckets = [];
2821
+ this.checkParticlePositionPlugins = [];
2822
+ this.#particleResetPlugins = [];
2823
+ this.#particleUpdatePlugins = [];
2824
+ this.#postUpdatePlugins = [];
2825
+ this.#postParticleUpdatePlugins = [];
2826
+ this.#updatePlugins = [];
2827
+ }
2828
+ drawParticles(delta) {
2829
+ for (let i = this.#zBuckets.length - one; i >= minIndex; i--) {
2830
+ const bucket = this.#zBuckets[i];
2831
+ if (!bucket) {
2832
+ continue;
2833
+ }
2834
+ for (const particle of bucket) {
2835
+ particle.draw(delta);
2836
+ }
2837
+ }
2838
+ }
2839
+ filter(condition) {
2840
+ return this.#array.filter(condition);
2841
+ }
2842
+ find(condition) {
2843
+ return this.#array.find(condition);
2844
+ }
2845
+ get(index) {
2846
+ return this.#array[index];
2847
+ }
2848
+ async init() {
2849
+ const container = this.#container, options = container.actualOptions;
2850
+ this.checkParticlePositionPlugins = [];
2851
+ this.#updatePlugins = [];
2852
+ this.#particleUpdatePlugins = [];
2853
+ this.#postUpdatePlugins = [];
2854
+ this.#particleResetPlugins = [];
2855
+ this.#postParticleUpdatePlugins = [];
2856
+ this.#particleBuckets.clear();
2857
+ this.#resetBuckets(container.zLayers);
2858
+ this.grid = new SpatialHashGrid(spatialHashGridCellSize * container.retina.pixelRatio);
2859
+ for (const plugin of container.plugins) {
2860
+ if (plugin.redrawInit) {
2861
+ await plugin.redrawInit();
2862
+ }
2863
+ if (plugin.checkParticlePosition) {
2864
+ this.checkParticlePositionPlugins.push(plugin);
2865
+ }
2866
+ if (plugin.update) {
2867
+ this.#updatePlugins.push(plugin);
2868
+ }
2869
+ if (plugin.particleUpdate) {
2870
+ this.#particleUpdatePlugins.push(plugin);
2871
+ }
2872
+ if (plugin.postUpdate) {
2873
+ this.#postUpdatePlugins.push(plugin);
2874
+ }
2875
+ if (plugin.particleReset) {
2876
+ this.#particleResetPlugins.push(plugin);
2877
+ }
2878
+ if (plugin.postParticleUpdate) {
2879
+ this.#postParticleUpdatePlugins.push(plugin);
2880
+ }
2881
+ }
2882
+ await this.#container.initDrawersAndUpdaters();
2883
+ for (const drawer of this.#container.effectDrawers.values()) {
2884
+ await drawer.init?.(container);
2885
+ }
2886
+ for (const drawer of this.#container.shapeDrawers.values()) {
2887
+ await drawer.init?.(container);
2888
+ }
2889
+ let handled = false;
2890
+ for (const plugin of container.plugins) {
2891
+ handled = plugin.particlesInitialization?.() ?? handled;
2892
+ if (handled) {
2893
+ break;
2894
+ }
2895
+ }
2896
+ if (!handled) {
2897
+ const particlesOptions = options.particles, groups = particlesOptions.groups;
2898
+ for (const group in groups) {
2899
+ const groupOptions = groups[group];
2900
+ if (!groupOptions) {
2901
+ continue;
2902
+ }
2903
+ for (let i = this.count, j = 0; j < groupOptions.number.value && i < particlesOptions.number.value; i++, j++) {
2904
+ this.addParticle(undefined, groupOptions, group);
2905
+ }
2906
+ }
2907
+ for (let i = this.count; i < particlesOptions.number.value; i++) {
2908
+ this.addParticle();
2909
+ }
2910
+ }
2911
+ }
2912
+ push(nb, position, overrideOptions, group) {
2913
+ for (let i = 0; i < nb; i++) {
2914
+ this.addParticle(position, overrideOptions, group);
2915
+ }
2916
+ }
2917
+ async redraw() {
2918
+ this.clear();
2919
+ await this.init();
2920
+ this.#container.canvas.render.drawParticles({ value: 0, factor: 0 });
2921
+ }
2922
+ remove(particle, group, override) {
2923
+ this.removeAt(this.#array.indexOf(particle), undefined, group, override);
2924
+ }
2925
+ removeAt(index, quantity = defaultRemoveQuantity, group, override) {
2926
+ if (index < minIndex || index > this.count) {
2927
+ return;
2928
+ }
2929
+ let deleted = 0;
2930
+ for (let i = index; deleted < quantity && i < this.count; i++) {
2931
+ if (this.#removeParticle(i, group, override)) {
2932
+ i--;
2933
+ deleted++;
2934
+ }
2935
+ }
2936
+ }
2937
+ removeQuantity(quantity, group) {
2938
+ this.removeAt(minIndex, quantity, group);
2939
+ }
2940
+ setDensity() {
2941
+ const options = this.#container.actualOptions, groups = options.particles.groups;
2942
+ let pluginsCount = 0;
2943
+ for (const plugin of this.#container.plugins) {
2944
+ if (plugin.particlesDensityCount) {
2945
+ pluginsCount += plugin.particlesDensityCount();
2946
+ }
2947
+ }
2948
+ for (const group in groups) {
2949
+ const groupData = groups[group];
2950
+ if (!groupData) {
2951
+ continue;
2952
+ }
2953
+ const groupDataOptions = loadParticlesOptions(this.#pluginManager, this.#container, groupData);
2954
+ this.#applyDensity(groupDataOptions, pluginsCount, group);
2955
+ }
2956
+ this.#applyDensity(options.particles, pluginsCount);
2957
+ }
2958
+ setResizeFactor(factor) {
2959
+ this.#resizeFactor = factor;
2960
+ }
2961
+ update(delta) {
2962
+ this.grid.clear();
2963
+ for (const plugin of this.#updatePlugins) {
2964
+ plugin.update?.(delta);
2965
+ }
2966
+ const particlesToDelete = this.#updateParticlesPhase1(delta);
2967
+ for (const plugin of this.#postUpdatePlugins) {
2968
+ plugin.postUpdate?.(delta);
2969
+ }
2970
+ this.#updateParticlesPhase2(delta, particlesToDelete);
2971
+ if (particlesToDelete.size) {
2972
+ for (const particle of particlesToDelete) {
2973
+ this.remove(particle);
2974
+ }
2975
+ }
2976
+ this.#resizeFactor = undefined;
2977
+ }
2978
+ #addToPool = (...particles) => {
2979
+ this.#pool.push(...particles);
2980
+ };
2981
+ #applyDensity = (options, pluginsCount, group, groupOptions) => {
2982
+ const numberOptions = options.number;
2983
+ if (!numberOptions.density.enable) {
2984
+ if (group === undefined) {
2985
+ this.#limit = numberOptions.limit.value;
2986
+ }
2987
+ else if (groupOptions?.number.limit.value ?? numberOptions.limit.value) {
2988
+ this.#groupLimits.set(group, groupOptions?.number.limit.value ?? numberOptions.limit.value);
2989
+ }
2990
+ return;
2991
+ }
2992
+ const densityFactor = this.#initDensityFactor(numberOptions.density), optParticlesNumber = numberOptions.value, optParticlesLimit = numberOptions.limit.value > minLimit ? numberOptions.limit.value : optParticlesNumber, particlesNumber = Math.min(optParticlesNumber, optParticlesLimit) * densityFactor + pluginsCount, particlesCount = Math.min(this.count, this.filter(t => t.group === group).length);
2993
+ if (group === undefined) {
2994
+ this.#limit = numberOptions.limit.value * densityFactor;
2995
+ }
2996
+ else {
2997
+ this.#groupLimits.set(group, numberOptions.limit.value * densityFactor);
2998
+ }
2999
+ if (particlesCount < particlesNumber) {
3000
+ this.push(Math.abs(particlesNumber - particlesCount), undefined, options, group);
3001
+ }
3002
+ else if (particlesCount > particlesNumber) {
3003
+ this.removeQuantity(particlesCount - particlesNumber, group);
3004
+ }
3005
+ };
3006
+ #createBuckets = (zLayers) => {
3007
+ const bucketCount = Math.max(Math.floor(zLayers), one);
3008
+ return Array.from({ length: bucketCount }, () => []);
3009
+ };
3010
+ #getBucketIndex = (zIndex) => {
3011
+ const maxBucketIndex = this.#zBuckets.length - one;
3012
+ if (maxBucketIndex <= minIndex) {
3013
+ return minIndex;
3014
+ }
3015
+ return Math.min(Math.max(Math.floor(zIndex), minIndex), maxBucketIndex);
3016
+ };
3017
+ #getParticleInsertIndex = (bucket, particleId) => {
3018
+ let start = minIndex, end = bucket.length;
3019
+ while (start < end) {
3020
+ const middle = Math.floor((start + end) / double), middleParticle = bucket[middle];
3021
+ if (!middleParticle) {
3022
+ end = middle;
3023
+ continue;
3024
+ }
3025
+ if (middleParticle.id < particleId) {
3026
+ start = middle + one;
3027
+ }
3028
+ else {
3029
+ end = middle;
3030
+ }
3031
+ }
3032
+ return start;
3033
+ };
3034
+ #initDensityFactor = densityOptions => {
3035
+ const container = this.#container;
3036
+ if (!densityOptions.enable) {
3037
+ return defaultDensityFactor;
3038
+ }
3039
+ const canvasSize = container.canvas.size, pxRatio = container.retina.pixelRatio;
3040
+ if (!canvasSize.width || !canvasSize.height) {
3041
+ return defaultDensityFactor;
3042
+ }
3043
+ return ((canvasSize.width * canvasSize.height) / (densityOptions.height * densityOptions.width * pxRatio ** squareExp));
3044
+ };
3045
+ #insertParticleIntoBucket = (particle) => {
3046
+ const bucketIndex = this.#getBucketIndex(particle.position.z), bucket = this.#zBuckets[bucketIndex];
3047
+ if (!bucket) {
3048
+ return;
3049
+ }
3050
+ bucket.splice(this.#getParticleInsertIndex(bucket, particle.id), empty, particle);
3051
+ this.#particleBuckets.set(particle.id, bucketIndex);
3052
+ };
3053
+ #removeParticle = (index, group, override) => {
3054
+ const particle = this.#array[index];
3055
+ if (!particle) {
3056
+ return false;
3057
+ }
3058
+ if (particle.group !== group) {
3059
+ return false;
3060
+ }
3061
+ this.#array.splice(index, deleteCount);
3062
+ this.#removeParticleFromBucket(particle);
3063
+ particle.destroy(override);
3064
+ this.#container.dispatchEvent(EventType.particleRemoved, {
3065
+ particle,
3066
+ });
3067
+ this.#addToPool(particle);
3068
+ return true;
3069
+ };
3070
+ #removeParticleFromBucket = (particle) => {
3071
+ const bucketIndex = this.#particleBuckets.get(particle.id) ?? this.#getBucketIndex(particle.position.z), bucket = this.#zBuckets[bucketIndex];
3072
+ if (!bucket) {
3073
+ this.#particleBuckets.delete(particle.id);
3074
+ return;
3075
+ }
3076
+ const particleIndex = this.#getParticleInsertIndex(bucket, particle.id);
3077
+ if (bucket[particleIndex]?.id !== particle.id) {
3078
+ this.#particleBuckets.delete(particle.id);
3079
+ return;
3080
+ }
3081
+ bucket.splice(particleIndex, deleteCount);
3082
+ this.#particleBuckets.delete(particle.id);
3083
+ };
3084
+ #resetBuckets = (zLayers) => {
3085
+ const bucketCount = Math.max(Math.floor(zLayers), one);
3086
+ if (this.#zBuckets.length !== bucketCount) {
3087
+ this.#zBuckets = this.#createBuckets(bucketCount);
3088
+ return;
3089
+ }
3090
+ for (const bucket of this.#zBuckets) {
3091
+ bucket.length = minIndex;
3092
+ }
3093
+ };
3094
+ #updateParticleBucket = (particle) => {
3095
+ const newBucketIndex = this.#getBucketIndex(particle.position.z), currentBucketIndex = this.#particleBuckets.get(particle.id);
3096
+ if (currentBucketIndex === undefined) {
3097
+ this.#insertParticleIntoBucket(particle);
3098
+ return;
3099
+ }
3100
+ if (currentBucketIndex === newBucketIndex) {
3101
+ return;
3102
+ }
3103
+ const currentBucket = this.#zBuckets[currentBucketIndex];
3104
+ if (currentBucket) {
3105
+ const particleIndex = this.#getParticleInsertIndex(currentBucket, particle.id);
3106
+ if (currentBucket[particleIndex]?.id === particle.id) {
3107
+ currentBucket.splice(particleIndex, deleteCount);
3108
+ }
3109
+ }
3110
+ const newBucket = this.#zBuckets[newBucketIndex];
3111
+ if (!newBucket) {
3112
+ this.#particleBuckets.set(particle.id, newBucketIndex);
3113
+ return;
3114
+ }
3115
+ newBucket.splice(this.#getParticleInsertIndex(newBucket, particle.id), empty, particle);
3116
+ this.#particleBuckets.set(particle.id, newBucketIndex);
3117
+ };
3118
+ #updateParticlesPhase1 = (delta) => {
3119
+ const particlesToDelete = new Set(), resizeFactor = this.#resizeFactor;
3120
+ for (const particle of this.#array) {
3121
+ if (resizeFactor && !particle.ignoresResizeRatio) {
3122
+ particle.position.x *= resizeFactor.width;
3123
+ particle.position.y *= resizeFactor.height;
3124
+ particle.initialPosition.x *= resizeFactor.width;
3125
+ particle.initialPosition.y *= resizeFactor.height;
3126
+ }
3127
+ particle.ignoresResizeRatio = false;
3128
+ for (const plugin of this.#particleResetPlugins) {
3129
+ plugin.particleReset?.(particle);
3130
+ }
3131
+ for (const plugin of this.#particleUpdatePlugins) {
3132
+ if (particle.destroyed) {
3133
+ break;
3134
+ }
3135
+ plugin.particleUpdate?.(particle, delta);
3136
+ }
3137
+ if (particle.destroyed) {
3138
+ particlesToDelete.add(particle);
3139
+ continue;
3140
+ }
3141
+ this.grid.insert(particle);
3142
+ }
3143
+ return particlesToDelete;
3144
+ };
3145
+ #updateParticlesPhase2 = (delta, particlesToDelete) => {
3146
+ for (const particle of this.#array) {
3147
+ if (particle.destroyed) {
3148
+ particlesToDelete.add(particle);
3149
+ continue;
3150
+ }
3151
+ for (const updater of this.#container.particleUpdaters) {
3152
+ updater.update(particle, delta);
3153
+ }
3154
+ if (!particle.spawning) {
3155
+ for (const plugin of this.#postParticleUpdatePlugins) {
3156
+ plugin.postParticleUpdate?.(particle, delta);
3157
+ }
3158
+ }
3159
+ this.#updateParticleBucket(particle);
3160
+ }
3161
+ };
3162
+ }
3163
+
3164
+ class Retina {
3165
+ pixelRatio;
3166
+ reduceFactor;
3167
+ #container;
3168
+ constructor(container) {
3169
+ this.#container = container;
3170
+ this.pixelRatio = defaultRatio;
3171
+ this.reduceFactor = defaultReduceFactor;
3172
+ }
3173
+ init() {
3174
+ const container = this.#container, options = container.actualOptions;
3175
+ this.pixelRatio = options.detectRetina ? devicePixelRatio : defaultRatio;
3176
+ this.reduceFactor = defaultReduceFactor;
3177
+ const ratio = this.pixelRatio, canvas = container.canvas, element = canvas.domElement;
3178
+ if (element) {
3179
+ canvas.size.width = element.offsetWidth * ratio;
3180
+ canvas.size.height = element.offsetHeight * ratio;
3181
+ }
3182
+ }
3183
+ initParticle(particle) {
3184
+ const options = particle.options, ratio = this.pixelRatio, moveOptions = options.move, moveDistance = moveOptions.distance, props = particle.retina;
3185
+ props.maxSpeed = getRangeValue(moveOptions.gravity.maxSpeed) * ratio;
3186
+ props.moveDrift = getRangeValue(moveOptions.drift) * ratio;
3187
+ props.moveSpeed = getRangeValue(moveOptions.speed) * ratio;
3188
+ const maxDistance = props.maxDistance;
3189
+ maxDistance.horizontal = moveDistance.horizontal === undefined ? undefined : moveDistance.horizontal * ratio;
3190
+ maxDistance.vertical = moveDistance.vertical === undefined ? undefined : moveDistance.vertical * ratio;
3191
+ }
3192
+ }
3193
+
3194
+ function guardCheck(container) {
3195
+ return !container.destroyed;
3196
+ }
3197
+ function updateDelta(delta, value, fpsLimit = defaultFps, smooth = false) {
3198
+ delta.value = value;
3199
+ delta.factor = smooth ? defaultFps / fpsLimit : (defaultFps * value) / millisecondsToSeconds;
3200
+ }
3201
+ function loadContainerOptions(pluginManager, container, ...sourceOptionsArr) {
3202
+ const options = new Options(pluginManager, container);
3203
+ loadOptions(options, ...sourceOptionsArr);
3204
+ return options;
3205
+ }
3206
+ class Container {
3207
+ actualOptions;
3208
+ canvas;
3209
+ destroyed;
3210
+ effectDrawers;
3211
+ fpsLimit;
3212
+ hdr;
3213
+ id;
3214
+ pageHidden;
3215
+ particleCreatedPlugins;
3216
+ particleDestroyedPlugins;
3217
+ particlePositionPlugins;
3218
+ particleUpdaters;
3219
+ particles;
3220
+ plugins;
3221
+ retina;
3222
+ shapeDrawers;
3223
+ started;
3224
+ zLayers;
3225
+ #delay;
3226
+ #delayTimeout;
3227
+ #delta = { value: 0, factor: 0 };
3228
+ #dispatchCallback;
3229
+ #drawAnimationFrame;
3230
+ #duration;
3231
+ #eventListeners;
3232
+ #firstStart;
3233
+ #initialSourceOptions;
3234
+ #lastFrameTime;
3235
+ #lifeTime;
3236
+ #onDestroy;
3237
+ #options;
3238
+ #paused;
3239
+ #pluginManager;
3240
+ #smooth;
3241
+ #sourceOptions;
3242
+ constructor(params) {
3243
+ const { dispatchCallback, pluginManager, id, onDestroy, sourceOptions } = params;
3244
+ this.#pluginManager = pluginManager;
3245
+ this.#dispatchCallback = dispatchCallback;
3246
+ this.#onDestroy = onDestroy;
3247
+ this.id = Symbol(id);
3248
+ this.fpsLimit = 120;
3249
+ this.hdr = false;
3250
+ this.#smooth = false;
3251
+ this.#delay = 0;
3252
+ this.#duration = 0;
3253
+ this.#lifeTime = 0;
3254
+ this.#firstStart = true;
3255
+ this.started = false;
3256
+ this.destroyed = false;
3257
+ this.#paused = true;
3258
+ this.#lastFrameTime = 0;
3259
+ this.zLayers = 100;
3260
+ this.pageHidden = false;
3261
+ this.#sourceOptions = sourceOptions;
3262
+ this.#initialSourceOptions = sourceOptions;
3263
+ this.effectDrawers = new Map();
3264
+ this.shapeDrawers = new Map();
3265
+ this.particleUpdaters = [];
3266
+ this.retina = new Retina(this);
3267
+ this.canvas = new CanvasManager(this.#pluginManager, this);
3268
+ this.particles = new ParticlesManager(this.#pluginManager, this);
3269
+ this.plugins = [];
3270
+ this.particleDestroyedPlugins = [];
3271
+ this.particleCreatedPlugins = [];
3272
+ this.particlePositionPlugins = [];
3273
+ this.#options = loadContainerOptions(this.#pluginManager, this);
3274
+ this.actualOptions = loadContainerOptions(this.#pluginManager, this);
3275
+ this.#eventListeners = new EventListeners(this);
3276
+ this.dispatchEvent(EventType.containerBuilt);
3277
+ }
3278
+ get animationStatus() {
3279
+ return !this.#paused && !this.pageHidden && guardCheck(this);
3280
+ }
3281
+ get options() {
3282
+ return this.#options;
3283
+ }
3284
+ get sourceOptions() {
3285
+ return this.#sourceOptions;
3286
+ }
3287
+ addLifeTime(value) {
3288
+ this.#lifeTime += value;
3289
+ }
3290
+ alive() {
3291
+ return !this.#duration || this.#lifeTime <= this.#duration;
3292
+ }
3293
+ destroy(remove = true) {
3294
+ if (!guardCheck(this)) {
3295
+ return;
3296
+ }
3297
+ this.stop();
3298
+ this.particles.destroy();
3299
+ this.canvas.destroy();
3300
+ for (const [, effectDrawer] of this.effectDrawers) {
3301
+ effectDrawer.destroy?.(this);
3302
+ }
3303
+ for (const [, shapeDrawer] of this.shapeDrawers) {
3304
+ shapeDrawer.destroy?.(this);
3305
+ }
3306
+ for (const plugin of this.plugins) {
3307
+ plugin.destroy?.();
3308
+ }
3309
+ this.effectDrawers = new Map();
3310
+ this.shapeDrawers = new Map();
3311
+ this.particleUpdaters = [];
3312
+ this.plugins.length = 0;
3313
+ this.#pluginManager.clearPlugins(this);
3314
+ this.destroyed = true;
3315
+ this.#onDestroy(remove);
3316
+ this.dispatchEvent(EventType.containerDestroyed);
3317
+ }
3318
+ dispatchEvent(type, data) {
3319
+ this.#dispatchCallback(type, {
3320
+ container: this,
3321
+ data,
3322
+ });
3323
+ }
3324
+ draw(force) {
3325
+ if (!guardCheck(this)) {
3326
+ return;
3327
+ }
3328
+ let refreshTime = force;
3329
+ this.#drawAnimationFrame = animate((timestamp) => {
3330
+ if (refreshTime) {
3331
+ this.#lastFrameTime = undefined;
3332
+ refreshTime = false;
3333
+ }
3334
+ this.#nextFrame(timestamp);
3335
+ });
3336
+ }
3337
+ async export(type, options = {}) {
3338
+ for (const plugin of this.plugins) {
3339
+ if (!plugin.export) {
3340
+ continue;
3341
+ }
3342
+ const res = await plugin.export(type, options);
3343
+ if (!res.supported) {
3344
+ continue;
3345
+ }
3346
+ return res.blob;
3347
+ }
3348
+ getLogger().error(`Export plugin with type ${type} not found`);
3349
+ return undefined;
3350
+ }
3351
+ async init() {
3352
+ if (!guardCheck(this)) {
3353
+ return;
3354
+ }
3355
+ const allContainerPlugins = new Map();
3356
+ for (const plugin of this.#pluginManager.plugins) {
3357
+ const containerPlugin = await plugin.getPlugin(this);
3358
+ if (containerPlugin.preInit) {
3359
+ await containerPlugin.preInit();
3360
+ }
3361
+ allContainerPlugins.set(plugin, containerPlugin);
3362
+ }
3363
+ await this.initDrawersAndUpdaters();
3364
+ this.#options = loadContainerOptions(this.#pluginManager, this, this.#initialSourceOptions, this.sourceOptions);
3365
+ this.actualOptions = loadContainerOptions(this.#pluginManager, this, this.#options);
3366
+ this.plugins.length = 0;
3367
+ this.particleDestroyedPlugins.length = 0;
3368
+ this.particleCreatedPlugins.length = 0;
3369
+ this.particlePositionPlugins.length = 0;
3370
+ for (const [plugin, containerPlugin] of allContainerPlugins) {
3371
+ if (plugin.needsPlugin(this.actualOptions)) {
3372
+ this.plugins.push(containerPlugin);
3373
+ if (containerPlugin.particleCreated) {
3374
+ this.particleCreatedPlugins.push(containerPlugin);
3375
+ }
3376
+ if (containerPlugin.particleDestroyed) {
3377
+ this.particleDestroyedPlugins.push(containerPlugin);
3378
+ }
3379
+ if (containerPlugin.particlePosition) {
3380
+ this.particlePositionPlugins.push(containerPlugin);
3381
+ }
3382
+ }
3383
+ }
3384
+ this.retina.init();
3385
+ this.canvas.init();
3386
+ this.updateActualOptions();
3387
+ this.canvas.initBackground();
3388
+ this.canvas.resize();
3389
+ const { delay, duration, fpsLimit, hdr, smooth, zLayers } = this.actualOptions;
3390
+ this.hdr = hdr;
3391
+ this.zLayers = zLayers;
3392
+ this.#duration = getRangeValue(duration) * millisecondsToSeconds;
3393
+ this.#delay = getRangeValue(delay) * millisecondsToSeconds;
3394
+ this.#lifeTime = 0;
3395
+ this.fpsLimit = fpsLimit > minFpsLimit ? fpsLimit : defaultFpsLimit;
3396
+ this.#smooth = smooth;
3397
+ for (const plugin of this.plugins) {
3398
+ await plugin.init?.();
3399
+ }
3400
+ await this.particles.init();
3401
+ this.dispatchEvent(EventType.containerInit);
3402
+ this.particles.setDensity();
3403
+ for (const plugin of this.plugins) {
3404
+ plugin.particlesSetup?.();
3405
+ }
3406
+ this.dispatchEvent(EventType.particlesSetup);
3407
+ }
3408
+ async initDrawersAndUpdaters() {
3409
+ const pluginManager = this.#pluginManager;
3410
+ this.effectDrawers = await pluginManager.getEffectDrawers(this, true);
3411
+ this.shapeDrawers = await pluginManager.getShapeDrawers(this, true);
3412
+ this.particleUpdaters = await pluginManager.getUpdaters(this, true);
3413
+ }
3414
+ pause() {
3415
+ if (!guardCheck(this)) {
3416
+ return;
3417
+ }
3418
+ if (this.#drawAnimationFrame !== undefined) {
3419
+ cancelAnimation(this.#drawAnimationFrame);
3420
+ this.#drawAnimationFrame = undefined;
3421
+ }
3422
+ if (this.#paused) {
3423
+ return;
3424
+ }
3425
+ for (const plugin of this.plugins) {
3426
+ plugin.pause?.();
3427
+ }
3428
+ if (!this.pageHidden) {
3429
+ this.#paused = true;
3430
+ }
3431
+ this.dispatchEvent(EventType.containerPaused);
3432
+ }
3433
+ play(force) {
3434
+ if (!guardCheck(this)) {
3435
+ return;
3436
+ }
3437
+ const needsUpdate = this.#paused || force;
3438
+ if (this.#firstStart && !this.actualOptions.autoPlay) {
3439
+ this.#firstStart = false;
3440
+ return;
3441
+ }
3442
+ if (this.#paused) {
3443
+ this.#paused = false;
3444
+ }
3445
+ if (needsUpdate) {
3446
+ for (const plugin of this.plugins) {
3447
+ if (plugin.play) {
3448
+ plugin.play();
3449
+ }
3450
+ }
3451
+ }
3452
+ this.dispatchEvent(EventType.containerPlay);
3453
+ this.draw(needsUpdate ?? false);
3454
+ }
3455
+ async refresh() {
3456
+ if (!guardCheck(this)) {
3457
+ return;
3458
+ }
3459
+ this.stop();
3460
+ return this.start();
3461
+ }
3462
+ async reset(sourceOptions) {
3463
+ if (!guardCheck(this)) {
3464
+ return;
3465
+ }
3466
+ this.#initialSourceOptions = sourceOptions;
3467
+ this.#sourceOptions = sourceOptions;
3468
+ this.#options = loadContainerOptions(this.#pluginManager, this, this.#initialSourceOptions, this.sourceOptions);
3469
+ this.actualOptions = loadContainerOptions(this.#pluginManager, this, this.#options);
3470
+ return this.refresh();
3471
+ }
3472
+ async start() {
3473
+ if (!guardCheck(this) || this.started) {
3474
+ return;
3475
+ }
3476
+ await this.init();
3477
+ this.started = true;
3478
+ await new Promise(resolve => {
3479
+ const start = async () => {
3480
+ this.#eventListeners.addListeners();
3481
+ for (const plugin of this.plugins) {
3482
+ await plugin.start?.();
3483
+ }
3484
+ this.dispatchEvent(EventType.containerStarted);
3485
+ this.play();
3486
+ resolve();
3487
+ };
3488
+ this.#delayTimeout = setTimeout(() => void start(), this.#delay);
3489
+ });
3490
+ }
3491
+ stop() {
3492
+ if (!guardCheck(this) || !this.started) {
3493
+ return;
3494
+ }
3495
+ if (this.#delayTimeout) {
3496
+ clearTimeout(this.#delayTimeout);
3497
+ this.#delayTimeout = undefined;
3498
+ }
3499
+ this.#firstStart = true;
3500
+ this.started = false;
3501
+ this.#eventListeners.removeListeners();
3502
+ this.pause();
3503
+ this.particles.clear();
3504
+ this.canvas.stop();
3505
+ for (const plugin of this.plugins) {
3506
+ plugin.stop?.();
3507
+ }
3508
+ this.particleCreatedPlugins.length = 0;
3509
+ this.particleDestroyedPlugins.length = 0;
3510
+ this.particlePositionPlugins.length = 0;
3511
+ this.#sourceOptions = this.#options;
3512
+ this.dispatchEvent(EventType.containerStopped);
3513
+ }
3514
+ updateActualOptions() {
3515
+ let refresh = false;
3516
+ for (const plugin of this.plugins) {
3517
+ if (plugin.updateActualOptions) {
3518
+ refresh = plugin.updateActualOptions() || refresh;
3519
+ }
3520
+ }
3521
+ return refresh;
3522
+ }
3523
+ #nextFrame = (timestamp) => {
3524
+ try {
3525
+ if (!this.#smooth &&
3526
+ this.#lastFrameTime !== undefined &&
3527
+ timestamp < this.#lastFrameTime + millisecondsToSeconds / this.fpsLimit) {
3528
+ this.draw(false);
3529
+ return;
3530
+ }
3531
+ this.#lastFrameTime ??= timestamp;
3532
+ updateDelta(this.#delta, timestamp - this.#lastFrameTime, this.fpsLimit, this.#smooth);
3533
+ this.addLifeTime(this.#delta.value);
3534
+ this.#lastFrameTime = timestamp;
3535
+ if (this.#delta.value > millisecondsToSeconds) {
3536
+ this.draw(false);
3537
+ return;
3538
+ }
3539
+ this.canvas.render.drawParticles(this.#delta);
3540
+ if (!this.alive()) {
3541
+ this.destroy();
3542
+ return;
3543
+ }
3544
+ if (this.animationStatus) {
3545
+ this.draw(false);
3546
+ }
3547
+ }
3548
+ catch (e) {
3549
+ getLogger().error("error in animation loop", e);
3550
+ }
3551
+ };
3552
+ }
3553
+
3554
+ export { Container };