@basmilius/sparkle 2.1.0 → 2.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (108) hide show
  1. package/dist/index.d.mts +317 -459
  2. package/dist/index.d.mts.map +1 -1
  3. package/dist/index.mjs +1258 -949
  4. package/dist/index.mjs.map +1 -1
  5. package/package.json +6 -2
  6. package/src/aurora/index.ts +9 -3
  7. package/src/aurora/layer.ts +57 -29
  8. package/src/balloons/index.ts +9 -3
  9. package/src/balloons/layer.ts +50 -19
  10. package/src/bubbles/index.ts +9 -3
  11. package/src/bubbles/layer.ts +30 -17
  12. package/src/canvas.ts +92 -2
  13. package/src/color.ts +11 -2
  14. package/src/confetti/index.ts +15 -3
  15. package/src/confetti/layer.ts +8 -5
  16. package/src/confetti/particle.ts +12 -11
  17. package/src/confetti/shapes.ts +84 -97
  18. package/src/donuts/consts.ts +2 -2
  19. package/src/donuts/index.ts +9 -3
  20. package/src/donuts/layer.ts +43 -12
  21. package/src/effect.ts +107 -0
  22. package/src/fade.ts +87 -0
  23. package/src/fireflies/index.ts +9 -3
  24. package/src/fireflies/layer.ts +26 -9
  25. package/src/fireflies/particle.ts +2 -2
  26. package/src/firepit/index.ts +9 -3
  27. package/src/firepit/layer.ts +26 -7
  28. package/src/fireworks/create-explosion.ts +237 -0
  29. package/src/fireworks/explosion.ts +1 -1
  30. package/src/fireworks/index.ts +15 -3
  31. package/src/fireworks/layer.ts +55 -304
  32. package/src/fireworks/spark.ts +2 -2
  33. package/src/fireworks/types.ts +2 -2
  34. package/src/glitter/index.ts +9 -4
  35. package/src/glitter/layer.ts +15 -7
  36. package/src/glitter/types.ts +10 -0
  37. package/src/index.ts +3 -4
  38. package/src/lanterns/index.ts +9 -4
  39. package/src/lanterns/layer.ts +22 -10
  40. package/src/lanterns/types.ts +8 -0
  41. package/src/layer.ts +13 -11
  42. package/src/leaves/index.ts +9 -4
  43. package/src/leaves/layer.ts +21 -14
  44. package/src/leaves/types.ts +9 -0
  45. package/src/lightning/index.ts +9 -4
  46. package/src/lightning/layer.ts +4 -4
  47. package/src/lightning/system.ts +3 -3
  48. package/src/lightning/types.ts +10 -2
  49. package/src/matrix/index.ts +9 -4
  50. package/src/matrix/layer.ts +15 -7
  51. package/src/matrix/types.ts +9 -0
  52. package/src/orbits/index.ts +9 -4
  53. package/src/orbits/layer.ts +51 -21
  54. package/src/orbits/types.ts +12 -1
  55. package/src/particles/index.ts +9 -3
  56. package/src/particles/layer.ts +55 -12
  57. package/src/petals/index.ts +9 -3
  58. package/src/petals/layer.ts +29 -13
  59. package/src/plasma/index.ts +9 -3
  60. package/src/plasma/layer.ts +21 -6
  61. package/src/rain/index.ts +9 -3
  62. package/src/rain/layer.ts +30 -8
  63. package/src/sandstorm/index.ts +9 -3
  64. package/src/sandstorm/layer.ts +26 -9
  65. package/src/scene.ts +204 -0
  66. package/src/shooting-stars/system.ts +26 -24
  67. package/src/shooting-stars/types.ts +2 -1
  68. package/src/simulation-canvas.ts +45 -6
  69. package/src/snow/index.ts +9 -3
  70. package/src/snow/layer.ts +24 -11
  71. package/src/sparklers/index.ts +13 -3
  72. package/src/sparklers/layer.ts +61 -15
  73. package/src/stars/index.ts +9 -3
  74. package/src/stars/layer.ts +28 -22
  75. package/src/streamers/index.ts +9 -3
  76. package/src/streamers/layer.ts +18 -6
  77. package/src/streamers/types.ts +1 -1
  78. package/src/waves/index.ts +9 -3
  79. package/src/waves/layer.ts +42 -45
  80. package/src/waves/types.ts +1 -0
  81. package/src/wormhole/index.ts +9 -3
  82. package/src/wormhole/layer.ts +22 -6
  83. package/src/aurora/simulation.ts +0 -19
  84. package/src/balloons/simulation.ts +0 -19
  85. package/src/bubbles/simulation.ts +0 -20
  86. package/src/confetti/simulation.ts +0 -27
  87. package/src/donuts/simulation.ts +0 -25
  88. package/src/fireflies/simulation.ts +0 -18
  89. package/src/firepit/simulation.ts +0 -17
  90. package/src/fireworks/simulation.ts +0 -18
  91. package/src/glitter/simulation.ts +0 -19
  92. package/src/lanterns/simulation.ts +0 -17
  93. package/src/layered.ts +0 -185
  94. package/src/leaves/simulation.ts +0 -18
  95. package/src/lightning/simulation.ts +0 -17
  96. package/src/matrix/simulation.ts +0 -18
  97. package/src/orbits/simulation.ts +0 -19
  98. package/src/particles/simulation.ts +0 -26
  99. package/src/petals/simulation.ts +0 -18
  100. package/src/plasma/simulation.ts +0 -17
  101. package/src/rain/simulation.ts +0 -21
  102. package/src/sandstorm/simulation.ts +0 -18
  103. package/src/snow/simulation.ts +0 -17
  104. package/src/sparklers/simulation.ts +0 -30
  105. package/src/stars/simulation.ts +0 -22
  106. package/src/streamers/simulation.ts +0 -16
  107. package/src/waves/simulation.ts +0 -18
  108. package/src/wormhole/simulation.ts +0 -17
package/src/layer.ts CHANGED
@@ -7,18 +7,20 @@ export type EdgeFade = {
7
7
  readonly right?: EdgeFadeSide;
8
8
  };
9
9
 
10
- export abstract class SimulationLayer {
11
- fade: EdgeFade | null = null;
10
+ /**
11
+ * Internal interface implemented by all Effect subclasses. Used by SimulationCanvas
12
+ * and Scene to drive rendering without depending on the generic Effect<TConfig> type.
13
+ */
14
+ export interface SimulationLayer {
15
+ fade: EdgeFade | null;
12
16
 
13
- abstract tick(dt: number, width: number, height: number): void;
14
- abstract draw(ctx: CanvasRenderingContext2D, width: number, height: number): void;
17
+ tick(dt: number, width: number, height: number): void;
15
18
 
16
- onResize(_width: number, _height: number): void {}
17
- onMount(_canvas: HTMLCanvasElement): void {}
18
- onUnmount(_canvas: HTMLCanvasElement): void {}
19
+ draw(ctx: CanvasRenderingContext2D, width: number, height: number): void;
19
20
 
20
- withFade(fade: EdgeFade): this {
21
- this.fade = fade;
22
- return this;
23
- }
21
+ onResize(width: number, height: number): void;
22
+
23
+ onMount(canvas: HTMLCanvasElement): void;
24
+
25
+ onUnmount(canvas: HTMLCanvasElement): void;
24
26
  }
@@ -1,4 +1,9 @@
1
- export { LeafLayer } from './layer';
2
- export { LeafSimulation } from './simulation';
3
- export type { LeafSimulationConfig } from './simulation';
4
- export type { Leaf } from './types';
1
+ import { Leaves } from './layer';
2
+ import type { LeavesConfig } from './types';
3
+ import type { Effect } from '../effect';
4
+
5
+ export function createLeaves(config?: LeavesConfig): Effect<LeavesConfig> {
6
+ return new Leaves(config);
7
+ }
8
+
9
+ export type { LeavesConfig, Leaf } from './types';
@@ -1,13 +1,12 @@
1
- import { SimulationLayer } from '../layer';
1
+ import { Effect } from '../effect';
2
2
  import { LEAF_COLORS, MULBERRY } from './consts';
3
- import type { LeafSimulationConfig } from './simulation';
4
- import type { Leaf } from './types';
3
+ import type { Leaf, LeavesConfig } from './types';
5
4
 
6
- export class LeafLayer extends SimulationLayer {
5
+ export class Leaves extends Effect<LeavesConfig> {
7
6
  readonly #scale: number;
8
7
  readonly #size: number;
9
- readonly #speed: number;
10
- readonly #wind: number;
8
+ #speed: number;
9
+ #wind: number;
11
10
  readonly #colors: string[];
12
11
  #maxCount: number;
13
12
  #time: number = 0;
@@ -15,7 +14,7 @@ export class LeafLayer extends SimulationLayer {
15
14
  #sprites: HTMLCanvasElement[] = [];
16
15
  #height: number = 540;
17
16
 
18
- constructor(config: LeafSimulationConfig = {}) {
17
+ constructor(config: LeavesConfig = {}) {
19
18
  super();
20
19
 
21
20
  this.#scale = config.scale ?? 1;
@@ -40,6 +39,15 @@ export class LeafLayer extends SimulationLayer {
40
39
  this.#height = height;
41
40
  }
42
41
 
42
+ configure(config: Partial<LeavesConfig>): void {
43
+ if (config.speed !== undefined) {
44
+ this.#speed = config.speed;
45
+ }
46
+ if (config.wind !== undefined) {
47
+ this.#wind = config.wind;
48
+ }
49
+ }
50
+
43
51
  tick(dt: number, _width: number, height: number): void {
44
52
  this.#height = height;
45
53
  const speedFactor = (height / 540) / this.#speed;
@@ -47,8 +55,8 @@ export class LeafLayer extends SimulationLayer {
47
55
  this.#time += 0.015 * dt;
48
56
 
49
57
  const globalWind = Math.sin(this.#time * 0.5) * 0.4
50
- + Math.sin(this.#time * 1.3 + 2) * 0.2
51
- + Math.sin(this.#time * 3.1) * 0.1;
58
+ + Math.sin(this.#time * 1.3 + 2) * 0.2
59
+ + Math.sin(this.#time * 3.1) * 0.1;
52
60
 
53
61
  for (let index = 0; index < this.#leaves.length; index++) {
54
62
  const leaf = this.#leaves[index];
@@ -87,11 +95,10 @@ export class LeafLayer extends SimulationLayer {
87
95
  const py = leaf.y * height;
88
96
  const displaySize = leaf.size * leaf.depth;
89
97
  const scaleX = Math.cos(leaf.flipAngle);
98
+ const cos = Math.cos(leaf.rotation);
99
+ const sin = Math.sin(leaf.rotation);
90
100
 
91
- ctx.save();
92
- ctx.translate(px, py);
93
- ctx.rotate(leaf.rotation);
94
- ctx.scale(scaleX, 1);
101
+ ctx.setTransform(cos * scaleX, sin * scaleX, -sin, cos, px, py);
95
102
  ctx.globalAlpha = 0.3 + leaf.depth * 0.7;
96
103
  ctx.drawImage(
97
104
  this.#sprites[leaf.colorIndex % this.#sprites.length],
@@ -100,9 +107,9 @@ export class LeafLayer extends SimulationLayer {
100
107
  displaySize,
101
108
  displaySize
102
109
  );
103
- ctx.restore();
104
110
  }
105
111
 
112
+ ctx.resetTransform();
106
113
  ctx.globalAlpha = 1;
107
114
  }
108
115
 
@@ -1,3 +1,12 @@
1
+ export interface LeavesConfig {
2
+ readonly count?: number;
3
+ readonly colors?: string[];
4
+ readonly size?: number;
5
+ readonly speed?: number;
6
+ readonly wind?: number;
7
+ readonly scale?: number;
8
+ }
9
+
1
10
  export type Leaf = {
2
11
  x: number;
3
12
  y: number;
@@ -1,6 +1,11 @@
1
- export { LightningLayer } from './layer';
2
- export { LightningSimulation } from './simulation';
3
- export type { LightningSimulationConfig } from './simulation';
1
+ import { Lightning } from './layer';
2
+ import type { LightningConfig } from './types';
3
+ import type { Effect } from '../effect';
4
+
5
+ export function createLightning(config?: LightningConfig): Effect<LightningConfig> {
6
+ return new Lightning(config);
7
+ }
8
+
4
9
  export { LightningSystem } from './system';
5
10
  export type { LightningSystemConfig } from './system';
6
- export type { LightningBolt, LightningBranch } from './types';
11
+ export type { LightningConfig, LightningBolt, LightningBranch } from './types';
@@ -1,14 +1,14 @@
1
1
  import { hexToRGB } from '@basmilius/utils';
2
- import { SimulationLayer } from '../layer';
2
+ import { Effect } from '../effect';
3
3
  import { MULBERRY } from './consts';
4
- import type { LightningSimulationConfig } from './simulation';
4
+ import type { LightningConfig } from './types';
5
5
  import { LightningSystem } from './system';
6
6
 
7
- export class LightningLayer extends SimulationLayer {
7
+ export class Lightning extends Effect<LightningConfig> {
8
8
  readonly #system: LightningSystem;
9
9
  readonly #enableFlash: boolean;
10
10
 
11
- constructor(config: LightningSimulationConfig = {}) {
11
+ constructor(config: LightningConfig = {}) {
12
12
  super();
13
13
 
14
14
  this.#enableFlash = config.flash ?? true;
@@ -101,7 +101,7 @@ export class LightningSystem {
101
101
  ctx.globalCompositeOperation = 'source-over';
102
102
  }
103
103
 
104
- #drawSegments(ctx: CanvasRenderingContext2D, segments: {x: number; y: number}[], alpha: number, outerWidth: number, innerWidth: number, width: number, height: number): void {
104
+ #drawSegments(ctx: CanvasRenderingContext2D, segments: { x: number; y: number }[], alpha: number, outerWidth: number, innerWidth: number, width: number, height: number): void {
105
105
  if (segments.length < 2) {
106
106
  return;
107
107
  }
@@ -133,7 +133,7 @@ export class LightningSystem {
133
133
 
134
134
  #createBolt(): LightningBolt {
135
135
  const startX = 0.1 + this.#rng() * 0.8;
136
- const segments: {x: number; y: number}[] = [{x: startX, y: 0}];
136
+ const segments: { x: number; y: number }[] = [{x: startX, y: 0}];
137
137
  const branches: LightningBranch[] = [];
138
138
 
139
139
  let currentX = startX;
@@ -165,7 +165,7 @@ export class LightningSystem {
165
165
  }
166
166
 
167
167
  #createBranch(startX: number, startY: number): LightningBranch {
168
- const segments: {x: number; y: number}[] = [{x: startX, y: startY}];
168
+ const segments: { x: number; y: number }[] = [{x: startX, y: startY}];
169
169
  const direction = this.#rng() > 0.5 ? 1 : -1;
170
170
  const branchSteps = 3 + Math.floor(this.#rng() * 5);
171
171
 
@@ -1,10 +1,18 @@
1
+ export interface LightningConfig {
2
+ readonly frequency?: number;
3
+ readonly color?: string;
4
+ readonly branches?: boolean;
5
+ readonly flash?: boolean;
6
+ readonly scale?: number;
7
+ }
8
+
1
9
  export type LightningBranch = {
2
- segments: {x: number; y: number}[];
10
+ segments: { x: number; y: number }[];
3
11
  alpha: number;
4
12
  };
5
13
 
6
14
  export type LightningBolt = {
7
- segments: {x: number; y: number}[];
15
+ segments: { x: number; y: number }[];
8
16
  branches: LightningBranch[];
9
17
  alpha: number;
10
18
  lifetime: number;
@@ -1,4 +1,9 @@
1
- export { MatrixLayer } from './layer';
2
- export { MatrixSimulation } from './simulation';
3
- export type { MatrixSimulationConfig } from './simulation';
4
- export type { MatrixColumn } from './types';
1
+ import { Matrix } from './layer';
2
+ import type { MatrixConfig } from './types';
3
+ import type { Effect } from '../effect';
4
+
5
+ export function createMatrix(config?: MatrixConfig): Effect<MatrixConfig> {
6
+ return new Matrix(config);
7
+ }
8
+
9
+ export type { MatrixConfig, MatrixColumn } from './types';
@@ -1,14 +1,13 @@
1
1
  import { hexToRGB } from '@basmilius/utils';
2
- import { SimulationLayer } from '../layer';
2
+ import { Effect } from '../effect';
3
3
  import { MATRIX_CHARS, MULBERRY } from './consts';
4
- import type { MatrixSimulationConfig } from './simulation';
5
- import type { MatrixColumn } from './types';
4
+ import type { MatrixColumn, MatrixConfig } from './types';
6
5
 
7
- export class MatrixLayer extends SimulationLayer {
6
+ export class Matrix extends Effect<MatrixConfig> {
8
7
  readonly #scale: number;
9
- readonly #speed: number;
8
+ #speed: number;
10
9
  readonly #fontSize: number;
11
- readonly #trailLength: number;
10
+ #trailLength: number;
12
11
  readonly #colorRGB: [number, number, number];
13
12
  #maxColumns: number;
14
13
  #columns: MatrixColumn[] = [];
@@ -17,7 +16,7 @@ export class MatrixLayer extends SimulationLayer {
17
16
  #height: number = 540;
18
17
  #initialized: boolean = false;
19
18
 
20
- constructor(config: MatrixSimulationConfig = {}) {
19
+ constructor(config: MatrixConfig = {}) {
21
20
  super();
22
21
 
23
22
  this.#scale = config.scale ?? 1;
@@ -53,6 +52,15 @@ export class MatrixLayer extends SimulationLayer {
53
52
  }
54
53
  }
55
54
 
55
+ configure(config: Partial<MatrixConfig>): void {
56
+ if (config.speed !== undefined) {
57
+ this.#speed = config.speed;
58
+ }
59
+ if (config.trailLength !== undefined) {
60
+ this.#trailLength = config.trailLength;
61
+ }
62
+ }
63
+
56
64
  tick(dt: number, width: number, height: number): void {
57
65
  this.#width = width;
58
66
  this.#height = height;
@@ -1,3 +1,12 @@
1
+ export interface MatrixConfig {
2
+ readonly columns?: number;
3
+ readonly speed?: number;
4
+ readonly color?: string;
5
+ readonly fontSize?: number;
6
+ readonly trailLength?: number;
7
+ readonly scale?: number;
8
+ }
9
+
1
10
  export type MatrixColumn = {
2
11
  x: number;
3
12
  y: number;
@@ -1,4 +1,9 @@
1
- export { OrbitLayer } from './layer';
2
- export { OrbitSimulation } from './simulation';
3
- export type { OrbitSimulationConfig } from './simulation';
4
- export type { OrbitalCenter, Orbiter } from './types';
1
+ import { Orbits } from './layer';
2
+ import type { OrbitsConfig } from './types';
3
+ import type { Effect } from '../effect';
4
+
5
+ export function createOrbits(config?: OrbitsConfig): Effect<OrbitsConfig> {
6
+ return new Orbits(config);
7
+ }
8
+
9
+ export type { OrbitsConfig, OrbitalCenter, Orbiter } from './types';
@@ -1,23 +1,22 @@
1
1
  import { hexToRGB } from '@basmilius/utils';
2
- import { SimulationLayer } from '../layer';
2
+ import { Effect } from '../effect';
3
3
  import { MULBERRY, ORBIT_COLORS } from './consts';
4
- import type { OrbitSimulationConfig } from './simulation';
5
- import type { OrbitalCenter, Orbiter } from './types';
4
+ import type { OrbitalCenter, Orbiter, OrbitsConfig } from './types';
6
5
 
7
- export class OrbitLayer extends SimulationLayer {
6
+ export class Orbits extends Effect<OrbitsConfig> {
8
7
  readonly #centerCount: number;
9
8
  readonly #orbitersPerCenter: number;
10
- readonly #speed: number;
9
+ #speed: number;
11
10
  readonly #colors: string[];
12
- readonly #trailLength: number;
13
- readonly #showCenters: boolean;
14
- readonly #scale: number;
11
+ #trailLength: number;
12
+ #showCenters: boolean;
13
+ #scale: number;
15
14
  #centers: OrbitalCenter[] = [];
16
15
  #orbiters: Orbiter[] = [];
17
16
  #time: number = 0;
18
17
  #initialized: boolean = false;
19
18
 
20
- constructor(config: OrbitSimulationConfig = {}) {
19
+ constructor(config: OrbitsConfig = {}) {
21
20
  super();
22
21
 
23
22
  this.#centerCount = config.centers ?? 3;
@@ -54,6 +53,21 @@ export class OrbitLayer extends SimulationLayer {
54
53
  }
55
54
  }
56
55
 
56
+ configure(config: Partial<OrbitsConfig>): void {
57
+ if (config.speed !== undefined) {
58
+ this.#speed = config.speed;
59
+ }
60
+ if (config.trailLength !== undefined) {
61
+ this.#trailLength = config.trailLength;
62
+ }
63
+ if (config.showCenters !== undefined) {
64
+ this.#showCenters = config.showCenters;
65
+ }
66
+ if (config.scale !== undefined) {
67
+ this.#scale = config.scale;
68
+ }
69
+ }
70
+
57
71
  tick(dt: number, width: number, height: number): void {
58
72
  this.#time += 0.01 * dt * this.#speed;
59
73
 
@@ -77,10 +91,17 @@ export class OrbitLayer extends SimulationLayer {
77
91
  const px = cx + rotatedX;
78
92
  const py = cy + rotatedY;
79
93
 
80
- orbiter.trail.push({x: px, y: py});
81
-
82
- if (orbiter.trail.length > this.#trailLength) {
83
- orbiter.trail.shift();
94
+ const trail = orbiter.trail;
95
+ const maxLen = this.#trailLength;
96
+
97
+ if (trail.length < maxLen) {
98
+ trail.push({x: px, y: py});
99
+ orbiter.trailHead = trail.length - 1;
100
+ } else {
101
+ const next = (orbiter.trailHead + 1) % maxLen;
102
+ trail[next].x = px;
103
+ trail[next].y = py;
104
+ orbiter.trailHead = next;
84
105
  }
85
106
  }
86
107
  }
@@ -114,10 +135,15 @@ export class OrbitLayer extends SimulationLayer {
114
135
 
115
136
  for (const orbiter of this.#orbiters) {
116
137
  const [cr, cg, cb] = hexToRGB(orbiter.color);
138
+ const trail = orbiter.trail;
139
+ const trailLen = trail.length;
117
140
 
118
- if (orbiter.trail.length > 1) {
119
- for (let ti = 0; ti < orbiter.trail.length - 1; ti++) {
120
- const progress = (ti + 1) / orbiter.trail.length;
141
+ if (trailLen > 1) {
142
+ const isFull = trailLen === this.#trailLength;
143
+ const oldest = isFull ? (orbiter.trailHead + 1) % trailLen : 0;
144
+
145
+ for (let ti = 0; ti < trailLen - 1; ti++) {
146
+ const progress = (ti + 1) / trailLen;
121
147
  const trailAlpha = progress * 0.5;
122
148
  const trailWidth = orbiter.size * progress * this.#scale;
123
149
 
@@ -125,18 +151,21 @@ export class OrbitLayer extends SimulationLayer {
125
151
  continue;
126
152
  }
127
153
 
154
+ const idx0 = (oldest + ti) % trailLen;
155
+ const idx1 = (oldest + ti + 1) % trailLen;
156
+
128
157
  ctx.globalAlpha = trailAlpha;
129
158
  ctx.strokeStyle = `rgb(${cr}, ${cg}, ${cb})`;
130
159
  ctx.lineWidth = trailWidth;
131
160
  ctx.beginPath();
132
- ctx.moveTo(orbiter.trail[ti].x, orbiter.trail[ti].y);
133
- ctx.lineTo(orbiter.trail[ti + 1].x, orbiter.trail[ti + 1].y);
161
+ ctx.moveTo(trail[idx0].x, trail[idx0].y);
162
+ ctx.lineTo(trail[idx1].x, trail[idx1].y);
134
163
  ctx.stroke();
135
164
  }
136
165
  }
137
166
 
138
- if (orbiter.trail.length > 0) {
139
- const head = orbiter.trail[orbiter.trail.length - 1];
167
+ if (trailLen > 0) {
168
+ const head = trail[orbiter.trailHead];
140
169
  const headSize = orbiter.size * this.#scale;
141
170
 
142
171
  const glow = ctx.createRadialGradient(
@@ -177,7 +206,8 @@ export class OrbitLayer extends SimulationLayer {
177
206
  tilt: MULBERRY.next() * Math.PI,
178
207
  size: 1.5 + MULBERRY.next() * 2.5,
179
208
  color: this.#colors[Math.floor(MULBERRY.next() * this.#colors.length)],
180
- trail: []
209
+ trail: [],
210
+ trailHead: 0
181
211
  };
182
212
  }
183
213
  }
@@ -1,3 +1,13 @@
1
+ export interface OrbitsConfig {
2
+ readonly centers?: number;
3
+ readonly orbitersPerCenter?: number;
4
+ readonly speed?: number;
5
+ readonly colors?: string[];
6
+ readonly trailLength?: number;
7
+ readonly showCenters?: boolean;
8
+ readonly scale?: number;
9
+ }
10
+
1
11
  export type OrbitalCenter = {
2
12
  x: number;
3
13
  y: number;
@@ -12,5 +22,6 @@ export type Orbiter = {
12
22
  tilt: number;
13
23
  size: number;
14
24
  color: string;
15
- trail: {x: number; y: number}[];
25
+ trail: { x: number; y: number }[];
26
+ trailHead: number;
16
27
  };
@@ -1,4 +1,10 @@
1
- export { ParticleLayer } from './layer';
2
- export { ParticleSimulation } from './simulation';
3
- export type { ParticleSimulationConfig } from './simulation';
1
+ import { Particles } from './layer';
2
+ import type { ParticlesConfig } from './layer';
3
+ import type { Effect } from '../effect';
4
+
5
+ export function createParticles(config?: ParticlesConfig): Effect<ParticlesConfig> {
6
+ return new Particles(config);
7
+ }
8
+
9
+ export type { ParticlesConfig };
4
10
  export type { NetworkParticle, ParticleMouseMode } from './types';
@@ -1,18 +1,34 @@
1
1
  import { hexToRGB } from '@basmilius/utils';
2
- import { SimulationLayer } from '../layer';
2
+ import { Effect } from '../effect';
3
3
  import { MULBERRY } from './consts';
4
- import type { ParticleSimulationConfig } from './simulation';
5
4
  import type { NetworkParticle, ParticleMouseMode } from './types';
6
5
 
7
- export class ParticleLayer extends SimulationLayer {
8
- readonly #scale: number;
9
- readonly #connectionDistance: number;
10
- readonly #lineWidth: number;
11
- readonly #mouseMode: ParticleMouseMode;
12
- readonly #mouseRadius: number;
13
- readonly #mouseStrength: number;
14
- readonly #particleForces: boolean;
15
- readonly #glow: boolean;
6
+ export interface ParticlesConfig {
7
+ readonly count?: number;
8
+ readonly color?: string;
9
+ readonly lineColor?: string;
10
+ readonly size?: [number, number];
11
+ readonly speed?: [number, number];
12
+ readonly connectionDistance?: number;
13
+ readonly lineWidth?: number;
14
+ readonly mouseMode?: ParticleMouseMode;
15
+ readonly mouseRadius?: number;
16
+ readonly mouseStrength?: number;
17
+ readonly particleForces?: boolean;
18
+ readonly glow?: boolean;
19
+ readonly background?: string | null;
20
+ readonly scale?: number;
21
+ }
22
+
23
+ export class Particles extends Effect<ParticlesConfig> {
24
+ #scale: number;
25
+ #connectionDistance: number;
26
+ #lineWidth: number;
27
+ #mouseMode: ParticleMouseMode;
28
+ #mouseRadius: number;
29
+ #mouseStrength: number;
30
+ #particleForces: boolean;
31
+ #glow: boolean;
16
32
  readonly #background: string | null;
17
33
  readonly #colorRGB: [number, number, number];
18
34
  readonly #lineColorRGB: [number, number, number];
@@ -31,7 +47,7 @@ export class ParticleLayer extends SimulationLayer {
31
47
  #height: number = 540;
32
48
  #initialized: boolean = false;
33
49
 
34
- constructor(config: ParticleSimulationConfig = {}) {
50
+ constructor(config: ParticlesConfig = {}) {
35
51
  super();
36
52
 
37
53
  this.#scale = config.scale ?? 1;
@@ -60,6 +76,33 @@ export class ParticleLayer extends SimulationLayer {
60
76
  this.#onMouseLeaveBound = this.#onMouseLeave.bind(this);
61
77
  }
62
78
 
79
+ configure(config: Partial<ParticlesConfig>): void {
80
+ if (config.scale !== undefined) {
81
+ this.#scale = config.scale;
82
+ }
83
+ if (config.connectionDistance !== undefined) {
84
+ this.#connectionDistance = config.connectionDistance * this.#scale;
85
+ }
86
+ if (config.lineWidth !== undefined) {
87
+ this.#lineWidth = config.lineWidth;
88
+ }
89
+ if (config.mouseMode !== undefined) {
90
+ this.#mouseMode = config.mouseMode;
91
+ }
92
+ if (config.mouseRadius !== undefined) {
93
+ this.#mouseRadius = config.mouseRadius * this.#scale;
94
+ }
95
+ if (config.mouseStrength !== undefined) {
96
+ this.#mouseStrength = config.mouseStrength;
97
+ }
98
+ if (config.particleForces !== undefined) {
99
+ this.#particleForces = config.particleForces;
100
+ }
101
+ if (config.glow !== undefined) {
102
+ this.#glow = config.glow;
103
+ }
104
+ }
105
+
63
106
  onResize(width: number, height: number): void {
64
107
  this.#width = width;
65
108
  this.#height = height;
@@ -1,4 +1,10 @@
1
- export { PetalLayer } from './layer';
2
- export { PetalSimulation } from './simulation';
3
- export type { PetalSimulationConfig } from './simulation';
1
+ import { Petals } from './layer';
2
+ import type { PetalsConfig } from './layer';
3
+ import type { Effect } from '../effect';
4
+
5
+ export function createPetals(config?: PetalsConfig): Effect<PetalsConfig> {
6
+ return new Petals(config);
7
+ }
8
+
9
+ export type { PetalsConfig };
4
10
  export type { Petal } from './types';