custom-pixi-particles 8.0.0 → 8.0.1

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 (163) hide show
  1. package/dist/index.d.ts +15 -0
  2. package/dist/index.js +45 -0
  3. package/dist/index.js.map +1 -0
  4. package/dist/lib/Model.d.ts +32 -0
  5. package/dist/lib/Model.js +44 -0
  6. package/dist/lib/Model.js.map +1 -0
  7. package/dist/lib/Particle.d.ts +234 -0
  8. package/dist/lib/Particle.js +201 -0
  9. package/dist/lib/Particle.js.map +1 -0
  10. package/dist/lib/ParticlePool.d.ts +37 -0
  11. package/dist/lib/ParticlePool.js +56 -0
  12. package/dist/lib/ParticlePool.js.map +1 -0
  13. package/dist/lib/behaviour/AngularVelocityBehaviour.d.ts +61 -0
  14. package/dist/lib/behaviour/AngularVelocityBehaviour.js +118 -0
  15. package/dist/lib/behaviour/AngularVelocityBehaviour.js.map +1 -0
  16. package/dist/lib/behaviour/AttractionRepulsionBehaviour.d.ts +42 -0
  17. package/dist/lib/behaviour/AttractionRepulsionBehaviour.js +78 -0
  18. package/dist/lib/behaviour/AttractionRepulsionBehaviour.js.map +1 -0
  19. package/dist/lib/behaviour/Behaviour.d.ts +33 -0
  20. package/dist/lib/behaviour/Behaviour.js +45 -0
  21. package/dist/lib/behaviour/Behaviour.js.map +1 -0
  22. package/dist/lib/behaviour/BehaviourNames.d.ts +22 -0
  23. package/dist/lib/behaviour/BehaviourNames.js +23 -0
  24. package/dist/lib/behaviour/BehaviourNames.js.map +1 -0
  25. package/dist/lib/behaviour/CollisionBehaviour.d.ts +86 -0
  26. package/dist/lib/behaviour/CollisionBehaviour.js +140 -0
  27. package/dist/lib/behaviour/CollisionBehaviour.js.map +1 -0
  28. package/dist/lib/behaviour/ColorBehaviour.d.ts +67 -0
  29. package/dist/lib/behaviour/ColorBehaviour.js +193 -0
  30. package/dist/lib/behaviour/ColorBehaviour.js.map +1 -0
  31. package/dist/lib/behaviour/EmitDirectionBehaviour.d.ts +52 -0
  32. package/dist/lib/behaviour/EmitDirectionBehaviour.js +105 -0
  33. package/dist/lib/behaviour/EmitDirectionBehaviour.js.map +1 -0
  34. package/dist/lib/behaviour/EmitterBehaviours.d.ts +69 -0
  35. package/dist/lib/behaviour/EmitterBehaviours.js +116 -0
  36. package/dist/lib/behaviour/EmitterBehaviours.js.map +1 -0
  37. package/dist/lib/behaviour/ForceFieldsBehaviour.d.ts +53 -0
  38. package/dist/lib/behaviour/ForceFieldsBehaviour.js +80 -0
  39. package/dist/lib/behaviour/ForceFieldsBehaviour.js.map +1 -0
  40. package/dist/lib/behaviour/GroupingBehaviour.d.ts +57 -0
  41. package/dist/lib/behaviour/GroupingBehaviour.js +143 -0
  42. package/dist/lib/behaviour/GroupingBehaviour.js.map +1 -0
  43. package/dist/lib/behaviour/LifeBehaviour.d.ts +47 -0
  44. package/dist/lib/behaviour/LifeBehaviour.js +65 -0
  45. package/dist/lib/behaviour/LifeBehaviour.js.map +1 -0
  46. package/dist/lib/behaviour/LightEffectBehaviour.d.ts +59 -0
  47. package/dist/lib/behaviour/LightEffectBehaviour.js +94 -0
  48. package/dist/lib/behaviour/LightEffectBehaviour.js.map +1 -0
  49. package/dist/lib/behaviour/NoiseBasedMotionBehaviour.d.ts +35 -0
  50. package/dist/lib/behaviour/NoiseBasedMotionBehaviour.js +93 -0
  51. package/dist/lib/behaviour/NoiseBasedMotionBehaviour.js.map +1 -0
  52. package/dist/lib/behaviour/PositionBehaviour.d.ts +164 -0
  53. package/dist/lib/behaviour/PositionBehaviour.js +484 -0
  54. package/dist/lib/behaviour/PositionBehaviour.js.map +1 -0
  55. package/dist/lib/behaviour/RotationBehaviour.d.ts +59 -0
  56. package/dist/lib/behaviour/RotationBehaviour.js +100 -0
  57. package/dist/lib/behaviour/RotationBehaviour.js.map +1 -0
  58. package/dist/lib/behaviour/SizeBehaviour.d.ts +61 -0
  59. package/dist/lib/behaviour/SizeBehaviour.js +156 -0
  60. package/dist/lib/behaviour/SizeBehaviour.js.map +1 -0
  61. package/dist/lib/behaviour/SoundReactiveBehaviour.d.ts +58 -0
  62. package/dist/lib/behaviour/SoundReactiveBehaviour.js +132 -0
  63. package/dist/lib/behaviour/SoundReactiveBehaviour.js.map +1 -0
  64. package/dist/lib/behaviour/SpawnBehaviour.d.ts +89 -0
  65. package/dist/lib/behaviour/SpawnBehaviour.js +705 -0
  66. package/dist/lib/behaviour/SpawnBehaviour.js.map +1 -0
  67. package/dist/lib/behaviour/StretchBehaviour.d.ts +30 -0
  68. package/dist/lib/behaviour/StretchBehaviour.js +59 -0
  69. package/dist/lib/behaviour/StretchBehaviour.js.map +1 -0
  70. package/dist/lib/behaviour/TemperatureBehaviour.d.ts +47 -0
  71. package/dist/lib/behaviour/TemperatureBehaviour.js +64 -0
  72. package/dist/lib/behaviour/TemperatureBehaviour.js.map +1 -0
  73. package/dist/lib/behaviour/TimelineBehaviour.d.ts +73 -0
  74. package/dist/lib/behaviour/TimelineBehaviour.js +100 -0
  75. package/dist/lib/behaviour/TimelineBehaviour.js.map +1 -0
  76. package/dist/lib/behaviour/TurbulenceBehaviour.d.ts +73 -0
  77. package/dist/lib/behaviour/TurbulenceBehaviour.js +133 -0
  78. package/dist/lib/behaviour/TurbulenceBehaviour.js.map +1 -0
  79. package/dist/lib/behaviour/index.d.ts +23 -0
  80. package/dist/lib/behaviour/index.js +24 -0
  81. package/dist/lib/behaviour/index.js.map +1 -0
  82. package/dist/lib/customPixiParticlesSettingsInterface.d.ts +19 -0
  83. package/dist/lib/customPixiParticlesSettingsInterface.js +2 -0
  84. package/dist/lib/customPixiParticlesSettingsInterface.js.map +1 -0
  85. package/dist/lib/emission/AbstractEmission.d.ts +30 -0
  86. package/dist/lib/emission/AbstractEmission.js +39 -0
  87. package/dist/lib/emission/AbstractEmission.js.map +1 -0
  88. package/dist/lib/emission/EmissionTypes.d.ts +6 -0
  89. package/dist/lib/emission/EmissionTypes.js +6 -0
  90. package/dist/lib/emission/EmissionTypes.js.map +1 -0
  91. package/dist/lib/emission/RandomEmission.d.ts +44 -0
  92. package/dist/lib/emission/RandomEmission.js +66 -0
  93. package/dist/lib/emission/RandomEmission.js.map +1 -0
  94. package/dist/lib/emission/StandardEmission.d.ts +62 -0
  95. package/dist/lib/emission/StandardEmission.js +87 -0
  96. package/dist/lib/emission/StandardEmission.js.map +1 -0
  97. package/dist/lib/emission/UniformEmission.d.ts +56 -0
  98. package/dist/lib/emission/UniformEmission.js +83 -0
  99. package/dist/lib/emission/UniformEmission.js.map +1 -0
  100. package/dist/lib/emission/index.d.ts +6 -0
  101. package/dist/lib/emission/index.js +7 -0
  102. package/dist/lib/emission/index.js.map +1 -0
  103. package/dist/lib/emitter/Duration.d.ts +27 -0
  104. package/dist/lib/emitter/Duration.js +41 -0
  105. package/dist/lib/emitter/Duration.js.map +1 -0
  106. package/dist/lib/emitter/Emitter.d.ts +116 -0
  107. package/dist/lib/emitter/Emitter.js +240 -0
  108. package/dist/lib/emitter/Emitter.js.map +1 -0
  109. package/dist/lib/emitter/index.d.ts +3 -0
  110. package/dist/lib/emitter/index.js +4 -0
  111. package/dist/lib/emitter/index.js.map +1 -0
  112. package/dist/lib/index.d.ts +20 -0
  113. package/dist/lib/index.js +21 -0
  114. package/dist/lib/index.js.map +1 -0
  115. package/dist/lib/parser/BehaviourParser.d.ts +21 -0
  116. package/dist/lib/parser/BehaviourParser.js +72 -0
  117. package/dist/lib/parser/BehaviourParser.js.map +1 -0
  118. package/dist/lib/parser/CompatibilityHelper.d.ts +12 -0
  119. package/dist/lib/parser/CompatibilityHelper.js +19 -0
  120. package/dist/lib/parser/CompatibilityHelper.js.map +1 -0
  121. package/dist/lib/parser/EmitControllerParser.d.ts +27 -0
  122. package/dist/lib/parser/EmitControllerParser.js +39 -0
  123. package/dist/lib/parser/EmitControllerParser.js.map +1 -0
  124. package/dist/lib/parser/EmitterParser.d.ts +67 -0
  125. package/dist/lib/parser/EmitterParser.js +168 -0
  126. package/dist/lib/parser/EmitterParser.js.map +1 -0
  127. package/dist/lib/parser/index.d.ts +5 -0
  128. package/dist/lib/parser/index.js +6 -0
  129. package/dist/lib/parser/index.js.map +1 -0
  130. package/dist/lib/pixi/Renderer.d.ts +125 -0
  131. package/dist/lib/pixi/Renderer.js +560 -0
  132. package/dist/lib/pixi/Renderer.js.map +1 -0
  133. package/dist/lib/pixi/TestRenderer.d.ts +125 -0
  134. package/dist/lib/pixi/TestRenderer.js +560 -0
  135. package/dist/lib/pixi/TestRenderer.js.map +1 -0
  136. package/dist/lib/util/Color.d.ts +133 -0
  137. package/dist/lib/util/Color.js +173 -0
  138. package/dist/lib/util/Color.js.map +1 -0
  139. package/dist/lib/util/List.d.ts +49 -0
  140. package/dist/lib/util/List.js +87 -0
  141. package/dist/lib/util/List.js.map +1 -0
  142. package/dist/lib/util/MinMax.d.ts +34 -0
  143. package/dist/lib/util/MinMax.js +46 -0
  144. package/dist/lib/util/MinMax.js.map +1 -0
  145. package/dist/lib/util/Point.d.ts +34 -0
  146. package/dist/lib/util/Point.js +46 -0
  147. package/dist/lib/util/Point.js.map +1 -0
  148. package/dist/lib/util/Random.d.ts +24 -0
  149. package/dist/lib/util/Random.js +30 -0
  150. package/dist/lib/util/Random.js.map +1 -0
  151. package/dist/lib/util/ThereBack.d.ts +14 -0
  152. package/dist/lib/util/ThereBack.js +23 -0
  153. package/dist/lib/util/ThereBack.js.map +1 -0
  154. package/dist/lib/util/index.d.ts +5 -0
  155. package/dist/lib/util/index.js +6 -0
  156. package/dist/lib/util/index.js.map +1 -0
  157. package/dist/lib/util/maths.d.ts +10 -0
  158. package/dist/lib/util/maths.js +13 -0
  159. package/dist/lib/util/maths.js.map +1 -0
  160. package/dist/lib/util/turbulencePool.d.ts +4 -0
  161. package/dist/lib/util/turbulencePool.js +7 -0
  162. package/dist/lib/util/turbulencePool.js.map +1 -0
  163. package/package.json +1 -1
@@ -0,0 +1,705 @@
1
+ import { Point } from '../util';
2
+ import { Behaviour, BehaviourNames } from './index';
3
+ let canvas = null;
4
+ let imageData = null;
5
+ let particleCount = 0;
6
+ let pixelPositions = [];
7
+ export default class SpawnBehaviour extends Behaviour {
8
+ constructor() {
9
+ super(...arguments);
10
+ this.enabled = true;
11
+ this.priority = 0;
12
+ this.overOne = false;
13
+ this.trailProgress = 0; // Progress along the trail (0-1)
14
+ this.trailingEnabled = false; // Enable trailing
15
+ this.spawnAlongTrail = false; // Spawn particles along the entire trail
16
+ this.trailSpeed = 1; // Speed of the trail
17
+ this.trailRepeat = true; // Loop the trail
18
+ this.trailStart = 0; // Start the trail at 20% of its path
19
+ this.currentProgress = 0;
20
+ this.customPoints = [
21
+ {
22
+ spawnType: 'Rectangle',
23
+ word: 'Hello',
24
+ fontSize: 50,
25
+ fontSpacing: 5,
26
+ particleDensity: 1,
27
+ fontMaxWidth: 1334,
28
+ fontMaxHeight: 750,
29
+ textAlign: 'center',
30
+ textBaseline: 'middle',
31
+ radius: 100,
32
+ radiusX: 100,
33
+ radiusY: 100,
34
+ starPoints: 5,
35
+ rows: 10,
36
+ columns: 10,
37
+ cellSize: 20,
38
+ center: { x: 0, y: 0, z: 0 },
39
+ apex: { x: 0, y: 0, z: 0 },
40
+ spread: 360,
41
+ baseRadius: 100,
42
+ coneDirection: 1,
43
+ height: 100,
44
+ coneAngle: 45,
45
+ position: { x: 0, y: 0 },
46
+ positionVariance: { x: 0, y: 0 },
47
+ perspective: 0, // Distance for perspective
48
+ maxZ: 0, // Maximum z distance for effects
49
+ frequency: { x: 3, y: 2 },
50
+ start: { x: 10, y: 10 },
51
+ end: { x: 20, y: 20 },
52
+ control1: { x: 0, y: 0 },
53
+ control2: { x: 0, y: 0 },
54
+ delta: 1,
55
+ pitch: 50, // Vertical distance between consecutive loops
56
+ turns: 5, // Number of turns in the helix
57
+ pathPoints: [],
58
+ },
59
+ ];
60
+ this.lastWordSettings = {};
61
+ /**
62
+ * Initialize particles for each custom point.
63
+ * @param {Particle} particle - The particle to initialize.
64
+ */
65
+ this.init = (particle) => {
66
+ if (this.customPoints.length === 0)
67
+ return;
68
+ // Choose a random custom point
69
+ const point = this.customPoints[Math.floor(Math.random() * this.customPoints.length)];
70
+ if (this.trailingEnabled) {
71
+ const { positions, probabilities } = this.spawnAlongTrail
72
+ ? this.calculateTrailRangePositions(point)
73
+ : { positions: [this.calculateTrailPosition(point)], probabilities: [1] };
74
+ // Use weighted random selection for position
75
+ const positionIndex = this.weightedRandomIndex(probabilities);
76
+ const position = positions[positionIndex];
77
+ particle.movement.x = this.calculate(point.position.x, point.positionVariance.x) + position.x;
78
+ particle.movement.y = this.calculate(point.position.y, point.positionVariance.y) + position.y;
79
+ particle.z = Math.random() * point.maxZ;
80
+ }
81
+ else {
82
+ // Normal spawning logic
83
+ this.spawnParticleAtPoint(particle, point);
84
+ }
85
+ if (point.perspective && point.maxZ) {
86
+ // Apply perspective scaling based on z
87
+ const scale = point.perspective / (point.perspective + particle.z);
88
+ particle.movement.x *= scale;
89
+ particle.movement.y *= scale;
90
+ // Adjust particle opacity based on z
91
+ particle.superColorAlphaEnd = 1 - particle.z / point.maxZ;
92
+ particle.size.x = 1 - particle.z / point.maxZ;
93
+ particle.size.y = 1 - particle.z / point.maxZ;
94
+ }
95
+ };
96
+ this.apply = () => {
97
+ // do nothing
98
+ };
99
+ /**
100
+ * Calculate trail positions along the range from trailStart to currentProgress.
101
+ * @param {Object} point - The custom point configuration.
102
+ * @returns {Point[]} List of positions along the trail.
103
+ */
104
+ this.calculateTrailRangePositions = (point) => {
105
+ const positions = [];
106
+ const segments = 20; // Increase segments for finer granularity
107
+ const trailStart = this.trailStart || 0;
108
+ const weights = []; // Store probabilities for positions
109
+ const startProgress = Math.max(trailStart, 0);
110
+ const endProgress = Math.min(this.currentProgress, 1);
111
+ for (let i = startProgress; i <= endProgress; i += (endProgress - startProgress) / segments) {
112
+ const position = this.calculateTrailPosition(point, i);
113
+ positions.push(position);
114
+ // Assign a weight inversely proportional to distance from trail center
115
+ const weightFactor = 4; // Higher values give steeper drops
116
+ const distanceToTrail = Math.abs(i - this.currentProgress);
117
+ weights.push(Math.exp(-distanceToTrail * weightFactor));
118
+ }
119
+ // Normalize weights to create probabilities
120
+ const totalWeight = weights.reduce((sum, w) => sum + w, 0);
121
+ const probabilities = weights.map((w) => w / totalWeight);
122
+ return { positions, probabilities };
123
+ };
124
+ /**
125
+ * Spawn particle at the specified point configuration.
126
+ * @param {Particle} particle - The particle to be initialized.
127
+ * @param {Object} point - The custom point configuration.
128
+ */
129
+ this.spawnParticleAtPoint = (particle, point) => {
130
+ // Assign particle z-coordinate within the max range
131
+ // particle.z = Math.random() * point.maxZ
132
+ if (point.spawnType === 'Word' &&
133
+ (this.lastWordSettings.word !== point.word ||
134
+ point.fontSize !== this.lastWordSettings.fontSize ||
135
+ point.fontSpacing !== this.lastWordSettings.fontSpacing ||
136
+ point.particleDensity !== this.lastWordSettings.particleDensity ||
137
+ point.textAlign !== this.lastWordSettings.textAlign ||
138
+ point.textBaseline !== this.lastWordSettings.textBaseline ||
139
+ point.fontMaxWidth !== this.lastWordSettings.fontMaxWidth ||
140
+ point.fontMaxHeight !== this.lastWordSettings.fontMaxHeight)) {
141
+ this.lastWordSettings = {
142
+ word: point.word,
143
+ fontSize: point.fontSize,
144
+ fontSpacing: point.fontSpacing,
145
+ particleDensity: point.particleDensity,
146
+ textAlign: point.textAlign,
147
+ textBaseline: point.textBaseline,
148
+ fontMaxWidth: point.fontMaxWidth,
149
+ fontMaxHeight: point.fontMaxHeight,
150
+ };
151
+ this.calculateCtx(point);
152
+ particle.reset(); // Reset the particle's position and movement
153
+ }
154
+ // Assign a random z-coordinate within a range
155
+ particle.z = Math.random() * point.maxZ;
156
+ if (point.spawnType === 'Rectangle') {
157
+ particle.movement.x = this.calculate(point.position.x, point.positionVariance.x);
158
+ particle.movement.y = this.calculate(point.position.y, point.positionVariance.y);
159
+ }
160
+ else if (point.spawnType === 'Ring') {
161
+ const angle = Math.random() * Math.PI * 2;
162
+ particle.movement.x = this.calculate(point.position.x, point.positionVariance.x) + Math.cos(angle) * point.radius;
163
+ particle.movement.y = this.calculate(point.position.y, point.positionVariance.y) + Math.sin(angle) * point.radius;
164
+ }
165
+ else if (point.spawnType === 'Star') {
166
+ const points = point.starPoints; // Configurable number of star points
167
+ const angle = (Math.PI * 2 * particle.uid) / points;
168
+ const radius = particle.uid % 2 === 0 ? point.radius : point.radius / 2;
169
+ particle.movement.x = this.calculate(point.position.x, point.positionVariance.x) + Math.cos(angle) * radius;
170
+ particle.movement.y = this.calculate(point.position.y, point.positionVariance.y) + Math.sin(angle) * radius;
171
+ }
172
+ else if (point.spawnType === 'FrameRectangle') {
173
+ const w = point.radiusX;
174
+ const h = point.radiusY;
175
+ if (Math.random() < w / (w + h)) {
176
+ particle.movement.x = Math.random() * w + particle.movement.x;
177
+ particle.movement.y = Math.random() < 0.5 ? particle.movement.y : particle.movement.y + h - 1;
178
+ }
179
+ else {
180
+ particle.movement.y = Math.random() * h + particle.movement.y;
181
+ particle.movement.x = Math.random() < 0.5 ? particle.movement.x : particle.movement.x + w - 1;
182
+ }
183
+ particle.movement.x += this.calculate(point.position.x, point.positionVariance.x);
184
+ particle.movement.y += this.calculate(point.position.y, point.positionVariance.y);
185
+ }
186
+ else if (point.spawnType === 'Frame') {
187
+ const w = point.radius;
188
+ const h = point.radius;
189
+ if (Math.random() < w / (w + h)) {
190
+ particle.movement.x = Math.random() * w + particle.movement.x;
191
+ particle.movement.y = Math.random() < 0.5 ? particle.movement.y : particle.movement.y + h - 1;
192
+ }
193
+ else {
194
+ particle.movement.y = Math.random() * h + particle.movement.y;
195
+ particle.movement.x = Math.random() < 0.5 ? particle.movement.x : particle.movement.x + w - 1;
196
+ }
197
+ particle.movement.x += this.calculate(point.position.x, point.positionVariance.x);
198
+ particle.movement.y += this.calculate(point.position.y, point.positionVariance.y);
199
+ }
200
+ else if (point.spawnType === 'Sphere') {
201
+ const phi = Math.random() * Math.PI * 2; // Random azimuthal angle
202
+ const theta = Math.random() * (point.spread / 180) * Math.PI; // Random polar angle
203
+ particle.movement.x =
204
+ this.calculate(point.position.x, point.positionVariance.x) +
205
+ point.center.x +
206
+ point.radius * Math.sin(theta) * Math.cos(phi);
207
+ particle.movement.y =
208
+ this.calculate(point.position.y, point.positionVariance.y) +
209
+ point.center.y +
210
+ point.radius * Math.sin(theta) * Math.sin(phi);
211
+ particle.z = point.center.z + point.radius * Math.cos(theta);
212
+ }
213
+ else if (point.spawnType === 'Cone') {
214
+ const angle = (Math.random() * point.coneAngle - point.coneAngle / 2) * (Math.PI / 180);
215
+ const distance = Math.random() * point.baseRadius; // Random distance from apex
216
+ const localX = Math.cos(angle) * distance; // Local x position within cone
217
+ const localY = Math.sin(angle) * distance; // Local y position within cone
218
+ // Convert coneDirection to radians
219
+ const coneDirectionRad = (point.coneDirection || 0) * (Math.PI / 180);
220
+ // Apply rotation to align the cone to the specified direction
221
+ particle.movement.x =
222
+ this.calculate(point.position.x, point.positionVariance.x) +
223
+ point.apex.x +
224
+ Math.cos(coneDirectionRad) * localX -
225
+ Math.sin(coneDirectionRad) * localY;
226
+ particle.movement.y =
227
+ this.calculate(point.position.y, point.positionVariance.y) +
228
+ point.apex.y +
229
+ Math.sin(coneDirectionRad) * localX +
230
+ Math.cos(coneDirectionRad) * localY;
231
+ particle.z = point.apex.z + Math.random() * point.height;
232
+ }
233
+ else if (point.spawnType === 'Grid') {
234
+ const row = Math.floor(Math.random() * point.rows);
235
+ const column = Math.floor(Math.random() * point.columns);
236
+ particle.movement.x = this.calculate(point.position.x, point.positionVariance.x) + column * point.cellSize;
237
+ particle.movement.y = this.calculate(point.position.y, point.positionVariance.y) + row * point.cellSize;
238
+ }
239
+ else if (point.spawnType === 'Word') {
240
+ if (particleCount > 0 && pixelPositions.length > 0) {
241
+ const selectedPixel = pixelPositions[Math.floor(Math.random() * particleCount)];
242
+ particle.movement.x = point.position.x + selectedPixel.x - canvas.width / 2;
243
+ particle.movement.y = point.position.y + selectedPixel.y - canvas.height / 2;
244
+ // Add a bit of random jitter to make the word more dynamic
245
+ particle.movement.x += Math.random() * point.positionVariance.x - point.positionVariance.x / 2;
246
+ particle.movement.y += Math.random() * point.positionVariance.y - point.positionVariance.y / 2;
247
+ }
248
+ }
249
+ else if (point.spawnType === 'Lissajous') {
250
+ const a = point.frequency.x; // Frequency in x-axis
251
+ const b = point.frequency.y; // Frequency in y-axis
252
+ const delta = point.delta || Math.PI / 2; // Phase difference
253
+ const t = particle.uid * 0.1;
254
+ particle.movement.x =
255
+ this.calculate(point.position.x, point.positionVariance.x) + Math.sin(a * t + delta) * point.radius;
256
+ particle.movement.y = this.calculate(point.position.y, point.positionVariance.y) + Math.sin(b * t) * point.radius;
257
+ }
258
+ else if (point.spawnType === 'Bezier') {
259
+ const t = Math.random(); // Progress along the curve
260
+ const cx1 = point.control1.x;
261
+ const cy1 = point.control1.y;
262
+ const cx2 = point.control2.x;
263
+ const cy2 = point.control2.y;
264
+ const x = this.calculate(point.position.x, point.positionVariance.x) +
265
+ Math.pow((1 - t), 3) * point.start.x +
266
+ 3 * Math.pow((1 - t), 2) * t * cx1 +
267
+ 3 * (1 - t) * Math.pow(t, 2) * cx2 +
268
+ Math.pow(t, 3) * point.end.x;
269
+ const y = this.calculate(point.position.x, point.positionVariance.x) +
270
+ Math.pow((1 - t), 3) * point.start.y +
271
+ 3 * Math.pow((1 - t), 2) * t * cy1 +
272
+ 3 * (1 - t) * Math.pow(t, 2) * cy2 +
273
+ Math.pow(t, 3) * point.end.y;
274
+ particle.movement.x = x;
275
+ particle.movement.y = y;
276
+ }
277
+ else if (point.spawnType === 'Heart') {
278
+ // Heart shape formula: (x^2 + y^2 - 1)^3 - x^2 * y^3 = 0
279
+ const t = Math.random() * 2 * Math.PI; // Parametric angle
280
+ const scale = point.radius || 100; // Scale based on radius
281
+ const x = scale * 16 * Math.pow(Math.sin(t), 3); // Parametric x-coordinate
282
+ const y = -scale * (13 * Math.cos(t) - 5 * Math.cos(2 * t) - 2 * Math.cos(3 * t) - Math.cos(4 * t));
283
+ particle.movement.x = this.calculate(point.position.x, point.positionVariance.x) + x;
284
+ particle.movement.y = this.calculate(point.position.y, point.positionVariance.y) + y;
285
+ }
286
+ else if (point.spawnType === 'Helix') {
287
+ const turns = point.turns || 5; // Default number of turns
288
+ const pitch = point.pitch || 50; // Default pitch
289
+ const radius = point.radius || 100; // Default radius
290
+ const height = point.height || turns * pitch; // Default height if not specified
291
+ const t = Math.random() * turns * Math.PI * 2; // Angle along the helix
292
+ const z = Math.random() * height; // Random height within the helix
293
+ const x = radius * Math.cos(t); // X-coordinate based on angle
294
+ const y = radius * Math.sin(t); // Y-coordinate based on angle
295
+ const adjustedZ = z % pitch; // Adjust z for particles within a pitch interval
296
+ particle.movement.x = this.calculate(point.position.x, point.positionVariance.x) + x;
297
+ particle.movement.y = this.calculate(point.position.y, point.positionVariance.y) + y;
298
+ particle.z = point.position.z + adjustedZ; // Assign Z-coordinate to the particle
299
+ }
300
+ else if (point.spawnType === 'Spring') {
301
+ const turns = point.turns || 5; // Number of loops in the spring
302
+ const pitch = point.pitch || 50; // Distance between consecutive loops
303
+ const radius = point.radius || 100; // Radius of the spring
304
+ // Generate random position along the spring
305
+ const t = Math.random() * turns * Math.PI * 2; // Angle in radians along the spring
306
+ const z = (t / (Math.PI * 2)) * pitch; // Z position increases linearly with the angle
307
+ // Compute 3D positions
308
+ const x = Math.cos(t) * radius; // X-coordinate on the circle
309
+ const y = Math.sin(t) * radius; // Y-coordinate on the circle
310
+ // Apply perspective scaling if enabled
311
+ if (point.perspective > 0 && point.maxZ > 0) {
312
+ const scale = point.perspective / (point.perspective + z); // Perspective scaling
313
+ // Apply perspective scaling to coordinates
314
+ particle.movement.x = (this.calculate(point.position.x, point.positionVariance.x) + x) * scale;
315
+ particle.movement.y = (this.calculate(point.position.y, point.positionVariance.y) + y) * scale;
316
+ // Adjust particle size and alpha based on depth
317
+ particle.size.x = scale;
318
+ particle.size.y = scale;
319
+ particle.superColorAlphaEnd = 1 - z / point.maxZ;
320
+ }
321
+ else {
322
+ // No perspective: flat 2D visualization
323
+ particle.movement.x = this.calculate(point.position.x, point.positionVariance.x) + x;
324
+ particle.movement.y = this.calculate(point.position.y, point.positionVariance.y) + y;
325
+ }
326
+ // Assign Z-coordinate for reference
327
+ particle.z = z;
328
+ }
329
+ else if (point.spawnType === 'Path') {
330
+ const pathPoints = point.pathPoints || []; // List of path points [{ x, y, z }]
331
+ if (pathPoints.length < 2)
332
+ return; // Ensure at least two points for a path
333
+ // Choose a random segment along the path
334
+ const segmentIndex = Math.floor(Math.random() * (pathPoints.length - 1));
335
+ const start = pathPoints[segmentIndex];
336
+ const end = pathPoints[segmentIndex + 1];
337
+ // Interpolate between the start and end points
338
+ const t = Math.random(); // Random position along the segment (0 to 1)
339
+ const x = start.x + t * (end.x - start.x);
340
+ const y = start.y + t * (end.y - start.y);
341
+ const z = start.z + t * (end.z - start.z || 0); // Support z if defined
342
+ // Apply perspective scaling if enabled
343
+ if (point.perspective > 0 && point.maxZ > 0) {
344
+ const scale = point.perspective / (point.perspective + z);
345
+ particle.movement.x = (this.calculate(point.position.x, point.positionVariance.x) + x) * scale;
346
+ particle.movement.y = (this.calculate(point.position.y, point.positionVariance.y) + y) * scale;
347
+ // Adjust particle size and alpha based on depth
348
+ particle.size.x = scale;
349
+ particle.size.y = scale;
350
+ particle.superColorAlphaEnd = 1 - z / point.maxZ;
351
+ }
352
+ else {
353
+ // Flat 2D path
354
+ particle.movement.x = this.calculate(point.position.x, point.positionVariance.x) + x;
355
+ particle.movement.y = this.calculate(point.position.y, point.positionVariance.y) + y;
356
+ }
357
+ particle.z = z; // Assign Z-coordinate
358
+ }
359
+ else if (point.spawnType === 'Oval') {
360
+ const angle = Math.random() * Math.PI * 2; // Random angle around the ellipse
361
+ const radiusX = point.radiusX || 100; // Default radiusX
362
+ const radiusY = point.radiusY || 50; // Default radiusY
363
+ particle.movement.x = this.calculate(point.position.x, point.positionVariance.x) + Math.cos(angle) * radiusX;
364
+ particle.movement.y = this.calculate(point.position.y, point.positionVariance.y) + Math.sin(angle) * radiusY;
365
+ }
366
+ };
367
+ /**
368
+ * Calculate canvas context for rendering text-based particles.
369
+ * @param {Object} point - The custom point configuration.
370
+ */
371
+ this.calculateCtx = (point) => {
372
+ const text = point.word; // The word to render
373
+ const fontSize = point.fontSize; // Font size for rendering
374
+ if (!canvas) {
375
+ canvas = document.createElement('canvas');
376
+ canvas.width = point.fontMaxWidth;
377
+ canvas.height = point.fontMaxHeight;
378
+ }
379
+ const ctx = canvas.getContext('2d');
380
+ ctx.clearRect(0, 0, canvas.width, canvas.height); // Clear the canvas
381
+ ctx.font = `${fontSize}px Arial`;
382
+ ctx.textAlign = point.textAlign;
383
+ ctx.textBaseline = point.textBaseline;
384
+ ctx.fillText(text, canvas.width / 2, canvas.height / 2);
385
+ // Get pixel data
386
+ imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
387
+ const { data, width, height } = imageData;
388
+ const spacing = point.fontSpacing; // Spacing between pixels
389
+ // Find all non-transparent pixels
390
+ pixelPositions = [];
391
+ for (let y = 0; y < height; y += spacing) {
392
+ for (let x = 0; x < width; x += spacing) {
393
+ const index = (y * width + x) * 4;
394
+ if (data[index + 3] > 128) {
395
+ // Alpha channel > 128 means this pixel is part of the text
396
+ pixelPositions.push(new Point(x, y));
397
+ }
398
+ }
399
+ }
400
+ pixelPositions = pixelPositions.sort(() => Math.random() - 0.5);
401
+ // Use particleDensity to limit the number of particles
402
+ particleCount = Math.floor(pixelPositions.length * point.particleDensity);
403
+ };
404
+ this.calculateTrailPosition = (point, overrideProgress) => {
405
+ const progress = overrideProgress !== undefined ? overrideProgress : this.currentProgress;
406
+ switch (point.spawnType) {
407
+ case 'Rectangle': {
408
+ const totalSegments = 4; // Rectangle has four edges
409
+ const segmentLength = (2 * point.radiusX + 2 * point.radiusY) * progress;
410
+ const perimeter = 2 * point.radiusX + 2 * point.radiusY;
411
+ const localPosition = segmentLength % perimeter;
412
+ let x = point.position.x;
413
+ let y = point.position.y;
414
+ if (localPosition <= point.radiusX) {
415
+ // Top edge
416
+ x += localPosition - point.radiusX;
417
+ y -= point.radiusY;
418
+ }
419
+ else if (localPosition <= point.radiusX + point.radiusY) {
420
+ // Right edge
421
+ x += point.radiusX;
422
+ y += localPosition - point.radiusX - point.radiusY;
423
+ }
424
+ else if (localPosition <= 2 * point.radiusX + point.radiusY) {
425
+ // Bottom edge
426
+ x += point.radiusX - (localPosition - point.radiusX - point.radiusY);
427
+ y += point.radiusY;
428
+ }
429
+ else {
430
+ // Left edge
431
+ x -= point.radiusX;
432
+ y += point.radiusY - (localPosition - 2 * point.radiusX - point.radiusY);
433
+ }
434
+ return { x, y, z: 0 };
435
+ }
436
+ case 'Ring': {
437
+ const angle = progress * Math.PI * 2; // Progress is proportional to angle
438
+ const x = point.position.x + Math.cos(angle) * point.radius;
439
+ const y = point.position.y + Math.sin(angle) * point.radius;
440
+ return { x, y, z: 0 };
441
+ }
442
+ case 'Star': {
443
+ const totalPoints = point.starPoints * 2; // Include outer and inner points
444
+ const pointIndex = Math.floor(progress * totalPoints);
445
+ const localProgress = (progress * totalPoints) % 1;
446
+ const angle = (Math.PI * 2 * pointIndex) / totalPoints;
447
+ const nextAngle = (Math.PI * 2 * (pointIndex + 1)) / totalPoints;
448
+ const radius = pointIndex % 2 === 0 ? point.radius : point.radius / 2;
449
+ const nextRadius = (pointIndex + 1) % 2 === 0 ? point.radius : point.radius / 2;
450
+ const x = point.position.x +
451
+ Math.cos(angle) * radius * (1 - localProgress) +
452
+ Math.cos(nextAngle) * nextRadius * localProgress;
453
+ const y = point.position.y +
454
+ Math.sin(angle) * radius * (1 - localProgress) +
455
+ Math.sin(nextAngle) * nextRadius * localProgress;
456
+ return { x, y, z: 0 };
457
+ }
458
+ case 'Path': {
459
+ const pathPoints = point.pathPoints || [];
460
+ if (pathPoints.length < 2)
461
+ return { x: 0, y: 0, z: 0 };
462
+ const totalSegments = pathPoints.length - 1;
463
+ const segmentIndex = Math.floor(progress * totalSegments);
464
+ const localProgress = (progress * totalSegments) % 1;
465
+ const start = pathPoints[segmentIndex];
466
+ const end = pathPoints[segmentIndex + 1];
467
+ const x = start.x + localProgress * (end.x - start.x);
468
+ const y = start.y + localProgress * (end.y - start.y);
469
+ const z = start.z + localProgress * (end.z - start.z || 0);
470
+ return { x, y, z };
471
+ }
472
+ case 'Lissajous': {
473
+ const a = point.frequency.x;
474
+ const b = point.frequency.y;
475
+ const delta = point.delta || Math.PI / 2;
476
+ const t = progress * Math.PI * 2;
477
+ const x = point.position.x + Math.sin(a * t + delta) * point.radius;
478
+ const y = point.position.y + Math.sin(b * t) * point.radius;
479
+ return { x, y, z: 0 };
480
+ }
481
+ case 'Helix': {
482
+ const turns = point.turns || 5;
483
+ const pitch = point.pitch || 50;
484
+ const angle = progress * turns * Math.PI * 2;
485
+ const x = point.position.x + Math.cos(angle) * point.radius;
486
+ const y = point.position.y + Math.sin(angle) * point.radius;
487
+ const z = point.position.z + progress * turns * pitch;
488
+ return { x, y, z };
489
+ }
490
+ case 'Heart': {
491
+ const t = progress * Math.PI * 2; // Map progress to parametric angle
492
+ const scale = point.radius || 100; // Scale based on radius
493
+ // Parametric heart shape equations
494
+ const x = scale * 16 * Math.pow(Math.sin(t), 3);
495
+ const y = -scale * (13 * Math.cos(t) - 5 * Math.cos(2 * t) - 2 * Math.cos(3 * t) - Math.cos(4 * t));
496
+ // Apply position offset and variance
497
+ return {
498
+ x: point.position.x + x,
499
+ y: point.position.y + y,
500
+ z: 0,
501
+ };
502
+ }
503
+ case 'Bezier': {
504
+ const t = progress; // Use progress directly as the parameter for the curve
505
+ const { start, end, control1, control2 } = point;
506
+ // Calculate position along the Bezier curve
507
+ const x = Math.pow((1 - t), 3) * start.x +
508
+ 3 * Math.pow((1 - t), 2) * t * control1.x +
509
+ 3 * (1 - t) * Math.pow(t, 2) * control2.x +
510
+ Math.pow(t, 3) * end.x;
511
+ const y = Math.pow((1 - t), 3) * start.y +
512
+ 3 * Math.pow((1 - t), 2) * t * control1.y +
513
+ 3 * (1 - t) * Math.pow(t, 2) * control2.y +
514
+ Math.pow(t, 3) * end.y;
515
+ return { x, y, z: 0 }; // Bezier is 2D, so z is 0
516
+ }
517
+ case 'Frame': {
518
+ const perimeter = 2 * (point.radiusX + point.radiusY); // Total perimeter of the frame
519
+ const segmentLength = perimeter * progress; // Length covered by progress
520
+ const localPosition = segmentLength % perimeter;
521
+ let x = point.position.x;
522
+ let y = point.position.y;
523
+ if (localPosition <= point.radiusX) {
524
+ // Top edge
525
+ x += localPosition;
526
+ y -= point.radiusY;
527
+ }
528
+ else if (localPosition <= point.radiusX + point.radiusY) {
529
+ // Right edge
530
+ x += point.radiusX;
531
+ y += localPosition - point.radiusX;
532
+ }
533
+ else if (localPosition <= 2 * point.radiusX + point.radiusY) {
534
+ // Bottom edge
535
+ x += point.radiusX - (localPosition - point.radiusX - point.radiusY);
536
+ y += point.radiusY;
537
+ }
538
+ else {
539
+ // Left edge
540
+ x -= localPosition - 2 * point.radiusX - point.radiusY;
541
+ y -= point.radiusY;
542
+ }
543
+ return { x, y, z: 0 }; // Frame is 2D, so z is 0
544
+ }
545
+ case 'FrameRectangle': {
546
+ const perimeter = 2 * (point.radiusX + point.radiusY); // Total perimeter of the rectangular frame
547
+ const segmentLength = perimeter * progress; // Length covered by progress
548
+ const localPosition = segmentLength % perimeter;
549
+ let x = point.position.x;
550
+ let y = point.position.y;
551
+ if (localPosition <= point.radiusX) {
552
+ // Top edge
553
+ x += localPosition;
554
+ y -= point.radiusY / 2;
555
+ }
556
+ else if (localPosition <= point.radiusX + point.radiusY) {
557
+ // Right edge
558
+ x += point.radiusX / 2;
559
+ y += localPosition - point.radiusX - point.radiusY / 2;
560
+ }
561
+ else if (localPosition <= 2 * point.radiusX + point.radiusY) {
562
+ // Bottom edge
563
+ x += point.radiusX / 2 - (localPosition - point.radiusX - point.radiusY);
564
+ y += point.radiusY / 2;
565
+ }
566
+ else {
567
+ // Left edge
568
+ x -= localPosition - 2 * point.radiusX - point.radiusY;
569
+ y -= point.radiusY / 2;
570
+ }
571
+ return { x, y, z: 0 }; // FrameRectangle is 2D, so z is 0
572
+ }
573
+ case 'Oval': {
574
+ const angle = progress * Math.PI * 2; // Progress determines angle
575
+ const radiusX = point.radiusX || 100;
576
+ const radiusY = point.radiusY || 50;
577
+ const x = point.position.x + Math.cos(angle) * radiusX;
578
+ const y = point.position.y + Math.sin(angle) * radiusY;
579
+ return { x, y, z: 0 };
580
+ }
581
+ case 'Word': {
582
+ if (!pixelPositions || pixelPositions.length === 0) {
583
+ return { x: point.position.x, y: point.position.y, z: 0 }; // Fallback if no pixel data is available
584
+ }
585
+ // Determine the number of pixels to reveal based on trailProgress
586
+ const maxPixelIndex = Math.floor(progress * pixelPositions.length);
587
+ // Slice the pixels to include only those within the current progress
588
+ const revealedPixels = pixelPositions.slice(0, maxPixelIndex);
589
+ // If no pixels are revealed yet, return the starting position
590
+ if (revealedPixels.length === 0) {
591
+ return { x: point.position.x - canvas.width / 2, y: point.position.y - canvas.height / 2, z: 0 };
592
+ }
593
+ // Choose the first pixel in the revealed portion for a left-to-right effect
594
+ const selectedPixel = revealedPixels[revealedPixels.length - 1] || { x: 0, y: 0 };
595
+ // Map the pixel position to the particle's position
596
+ const x = point.position.x + selectedPixel.x - canvas.width / 2;
597
+ const y = point.position.y + selectedPixel.y - canvas.height / 2;
598
+ return { x, y, z: 0 };
599
+ }
600
+ default:
601
+ return { x: 0, y: 0, z: 0 };
602
+ }
603
+ };
604
+ /**
605
+ * Update trail progress once per frame.
606
+ * @param {number} deltaTime - Time since the last update
607
+ */
608
+ this.updateTrailProgress = (deltaTime) => {
609
+ if (!this.trailingEnabled)
610
+ return;
611
+ const trailStart = this.trailStart || 0;
612
+ // Increment trail progress
613
+ this.trailProgress += this.trailSpeed * deltaTime;
614
+ if (trailStart > 0) {
615
+ const remainingDistance = 1 - trailStart;
616
+ if (!this.overOne) {
617
+ if (this.trailProgress > remainingDistance) {
618
+ if (this.trailRepeat) {
619
+ this.overOne = true;
620
+ this.trailProgress = 0;
621
+ }
622
+ else {
623
+ this.trailProgress = remainingDistance;
624
+ }
625
+ }
626
+ this.currentProgress = trailStart + (this.trailProgress / remainingDistance) * remainingDistance;
627
+ }
628
+ else {
629
+ if (this.trailProgress > 1) {
630
+ if (this.trailRepeat) {
631
+ this.trailProgress %= 1;
632
+ }
633
+ else {
634
+ this.trailingEnabled = false;
635
+ this.trailProgress = 1;
636
+ }
637
+ }
638
+ this.currentProgress = this.trailProgress;
639
+ }
640
+ }
641
+ else {
642
+ if (this.trailProgress > 1) {
643
+ if (this.trailRepeat) {
644
+ this.trailProgress %= 1;
645
+ }
646
+ else {
647
+ this.trailingEnabled = false;
648
+ this.trailProgress = 1;
649
+ }
650
+ }
651
+ this.currentProgress = this.trailProgress;
652
+ }
653
+ };
654
+ // Utility function for weighted random selection
655
+ this.weightedRandomIndex = (probabilities) => {
656
+ const cumulative = probabilities.reduce((acc, prob, i) => {
657
+ acc.push(prob + (acc[i - 1] || 0));
658
+ return acc;
659
+ }, []);
660
+ const randomValue = Math.random();
661
+ return cumulative.findIndex((c) => randomValue <= c);
662
+ };
663
+ /**
664
+ * Update method to be called once per frame.
665
+ * @param {number} deltaTime - Time since the last frame
666
+ */
667
+ this.update = (deltaTime) => {
668
+ this.updateTrailProgress(deltaTime); // Update trail once per frame
669
+ };
670
+ /**
671
+ * Adds a random variance to the given value
672
+ * @param {number} value - The value to calculate
673
+ * @param {number} variance - The random variance to add
674
+ * @returns {number} The calculated value
675
+ */
676
+ this.calculate = (value, variance) => {
677
+ return value + this.varianceFrom(variance);
678
+ };
679
+ }
680
+ /**
681
+ * Gets the name of the behaviour
682
+ * @return {string} The name of the behaviour
683
+ */
684
+ getName() {
685
+ return BehaviourNames.SPAWN_BEHAVIOUR;
686
+ }
687
+ /**
688
+ * Retrieves the properties of the custom points.
689
+ * @returns {Object[]} The array of custom points and their properties.
690
+ */
691
+ getProps() {
692
+ return {
693
+ enabled: this.enabled,
694
+ priority: this.priority,
695
+ trailingEnabled: this.trailingEnabled,
696
+ spawnAlongTrail: this.spawnAlongTrail,
697
+ trailSpeed: this.trailSpeed,
698
+ trailRepeat: this.trailRepeat,
699
+ trailStart: this.trailStart,
700
+ customPoints: this.customPoints,
701
+ name: this.getName(),
702
+ };
703
+ }
704
+ }
705
+ //# sourceMappingURL=SpawnBehaviour.js.map