@tsparticles/plugin-emitters 3.0.0-beta.3 → 3.0.0-beta.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 (80) hide show
  1. package/browser/EmitterInstance.js +133 -80
  2. package/browser/EmitterShapeBase.js +12 -0
  3. package/browser/Emitters.js +7 -6
  4. package/browser/Options/Classes/Emitter.js +3 -4
  5. package/browser/Options/Classes/EmitterShape.js +21 -0
  6. package/browser/Options/Classes/EmitterShapeReplace.js +17 -0
  7. package/browser/Options/Interfaces/IEmitterShape.js +1 -0
  8. package/browser/Options/Interfaces/IEmitterShapeReplace.js +1 -0
  9. package/browser/ShapeManager.js +8 -8
  10. package/browser/index.js +7 -8
  11. package/cjs/EmitterInstance.js +133 -80
  12. package/cjs/EmitterShapeBase.js +16 -0
  13. package/cjs/Emitters.js +7 -6
  14. package/cjs/IRandomPositionData.js +2 -0
  15. package/cjs/Options/Classes/Emitter.js +3 -4
  16. package/cjs/Options/Classes/EmitterShape.js +25 -0
  17. package/cjs/Options/Classes/EmitterShapeReplace.js +21 -0
  18. package/cjs/Options/Interfaces/IEmitterShape.js +2 -0
  19. package/cjs/Options/Interfaces/IEmitterShapeReplace.js +2 -0
  20. package/cjs/ShapeManager.js +8 -8
  21. package/cjs/index.js +7 -8
  22. package/esm/EmitterInstance.js +133 -80
  23. package/esm/EmitterShapeBase.js +12 -0
  24. package/esm/Emitters.js +7 -6
  25. package/esm/IEmitterShapeGenerator.js +1 -0
  26. package/esm/IRandomPositionData.js +1 -0
  27. package/esm/Options/Classes/Emitter.js +3 -4
  28. package/esm/Options/Classes/EmitterShape.js +21 -0
  29. package/esm/Options/Classes/EmitterShapeReplace.js +17 -0
  30. package/esm/Options/Interfaces/IEmitterShape.js +1 -0
  31. package/esm/Options/Interfaces/IEmitterShapeReplace.js +1 -0
  32. package/esm/ShapeManager.js +8 -8
  33. package/esm/index.js +7 -8
  34. package/package.json +3 -3
  35. package/report.html +4 -22
  36. package/tsparticles.plugin.emitters.js +208 -176
  37. package/tsparticles.plugin.emitters.min.js +1 -1
  38. package/tsparticles.plugin.emitters.min.js.LICENSE.txt +1 -1
  39. package/types/EmitterContainer.d.ts +1 -1
  40. package/types/EmitterInstance.d.ts +11 -9
  41. package/types/EmitterShapeBase.d.ts +13 -0
  42. package/types/Emitters.d.ts +2 -2
  43. package/types/EmittersEngine.d.ts +2 -2
  44. package/types/IEmitterShape.d.ts +4 -1
  45. package/types/IEmitterShapeGenerator.d.ts +5 -0
  46. package/types/IRandomPositionData.d.ts +6 -0
  47. package/types/Options/Classes/Emitter.d.ts +2 -2
  48. package/types/Options/Classes/EmitterShape.d.ts +10 -0
  49. package/types/Options/Classes/EmitterShapeReplace.d.ts +8 -0
  50. package/types/Options/Interfaces/IEmitter.d.ts +2 -2
  51. package/types/Options/Interfaces/IEmitterShape.d.ts +6 -0
  52. package/types/Options/Interfaces/IEmitterShapeReplace.d.ts +4 -0
  53. package/types/ShapeManager.d.ts +4 -4
  54. package/types/index.d.ts +4 -1
  55. package/umd/EmitterInstance.js +133 -80
  56. package/umd/EmitterShapeBase.js +26 -0
  57. package/umd/Emitters.js +7 -6
  58. package/umd/IRandomPositionData.js +12 -0
  59. package/umd/Options/Classes/Emitter.js +4 -5
  60. package/umd/Options/Classes/EmitterShape.js +35 -0
  61. package/umd/Options/Classes/EmitterShapeReplace.js +31 -0
  62. package/umd/Options/Interfaces/IEmitterShape.js +12 -0
  63. package/umd/Options/Interfaces/IEmitterShapeReplace.js +12 -0
  64. package/umd/ShapeManager.js +8 -8
  65. package/umd/index.js +8 -9
  66. package/browser/Shapes/Circle/CircleShape.js +0 -24
  67. package/browser/Shapes/Square/SquareShape.js +0 -40
  68. package/cjs/Shapes/Circle/CircleShape.js +0 -28
  69. package/cjs/Shapes/Square/SquareShape.js +0 -44
  70. package/esm/Shapes/Circle/CircleShape.js +0 -24
  71. package/esm/Shapes/Square/SquareShape.js +0 -40
  72. package/types/Enums/EmitterShapeType.d.ts +0 -6
  73. package/types/Shapes/Circle/CircleShape.d.ts +0 -5
  74. package/types/Shapes/Square/SquareShape.d.ts +0 -5
  75. package/umd/Shapes/Circle/CircleShape.js +0 -38
  76. package/umd/Shapes/Square/SquareShape.js +0 -54
  77. /package/browser/{Enums/EmitterShapeType.js → IEmitterShapeGenerator.js} +0 -0
  78. /package/{esm/Enums/EmitterShapeType.js → browser/IRandomPositionData.js} +0 -0
  79. /package/cjs/{Enums/EmitterShapeType.js → IEmitterShapeGenerator.js} +0 -0
  80. /package/umd/{Enums/EmitterShapeType.js → IEmitterShapeGenerator.js} +0 -0
@@ -1,17 +1,25 @@
1
1
  import { Vector, calcPositionOrRandomFromSizeRanged, deepExtend, getRangeValue, getSize, isPointInside, itemFromSingleOrMultiple, randomInRange, rangeColorToHsl, } from "@tsparticles/engine";
2
2
  import { Emitter } from "./Options/Classes/Emitter.js";
3
3
  import { EmitterSize } from "./Options/Classes/EmitterSize.js";
4
+ function setParticlesOptionsColor(particlesOptions, color) {
5
+ if (particlesOptions.color) {
6
+ particlesOptions.color.value = color;
7
+ }
8
+ else {
9
+ particlesOptions.color = {
10
+ value: color,
11
+ };
12
+ }
13
+ }
4
14
  export class EmitterInstance {
5
15
  constructor(engine, emitters, container, options, position) {
6
16
  this.emitters = emitters;
7
17
  this.container = container;
8
- this._calcPosition = () => {
9
- return calcPositionOrRandomFromSizeRanged({
10
- size: this.container.canvas.size,
11
- position: this.options.position,
12
- });
13
- };
14
18
  this._destroy = () => {
19
+ this._mutationObserver?.disconnect();
20
+ this._mutationObserver = undefined;
21
+ this._resizeObserver?.disconnect();
22
+ this._resizeObserver = undefined;
15
23
  this.emitters.removeEmitter(this);
16
24
  this._engine.dispatchEvent("emitterDestroyed", {
17
25
  container: this.container,
@@ -20,40 +28,6 @@ export class EmitterInstance {
20
28
  },
21
29
  });
22
30
  };
23
- this._emit = () => {
24
- if (this._paused) {
25
- return;
26
- }
27
- const quantity = getRangeValue(this.options.rate.quantity);
28
- this._emitParticles(quantity);
29
- };
30
- this._emitParticles = (quantity) => {
31
- const position = this.getPosition(), size = this.getSize(), singleParticlesOptions = itemFromSingleOrMultiple(this._particlesOptions);
32
- for (let i = 0; i < quantity; i++) {
33
- const particlesOptions = deepExtend({}, singleParticlesOptions);
34
- if (this.spawnColor) {
35
- const hslAnimation = this.options.spawnColor?.animation;
36
- if (hslAnimation) {
37
- this.spawnColor.h = this._setColorAnimation(hslAnimation.h, this.spawnColor.h, 360);
38
- this.spawnColor.s = this._setColorAnimation(hslAnimation.s, this.spawnColor.s, 100);
39
- this.spawnColor.l = this._setColorAnimation(hslAnimation.l, this.spawnColor.l, 100);
40
- }
41
- if (!particlesOptions.color) {
42
- particlesOptions.color = {
43
- value: this.spawnColor,
44
- };
45
- }
46
- else {
47
- particlesOptions.color.value = this.spawnColor;
48
- }
49
- }
50
- if (!position) {
51
- return;
52
- }
53
- const pPosition = this._shape?.randomPosition(position, size, this.fill) ?? position;
54
- this.container.particles.addParticle(pPosition, particlesOptions);
55
- }
56
- };
57
31
  this._prepareToDie = () => {
58
32
  if (this._paused) {
59
33
  return;
@@ -89,7 +63,6 @@ export class EmitterInstance {
89
63
  this._spawnDelay = (getRangeValue(this.options.life.delay ?? 0) * 1000) / this.container.retina.reduceFactor;
90
64
  this.position = this._initialPosition ?? this._calcPosition();
91
65
  this.name = this.options.name;
92
- this._shape = this._engine.emitterShapeManager?.getShape(this.options.shape);
93
66
  this.fill = this.options.fill;
94
67
  this._firstSpawn = !this.options.life.wait;
95
68
  this._startParticlesAdded = false;
@@ -102,19 +75,30 @@ export class EmitterInstance {
102
75
  }
103
76
  this._paused = !this.options.autoPlay;
104
77
  this._particlesOptions = particlesOptions;
105
- this.size =
106
- this.options.size ??
107
- (() => {
108
- const size = new EmitterSize();
109
- size.load({
110
- height: 0,
111
- mode: "percent",
112
- width: 0,
113
- });
114
- return size;
115
- })();
78
+ this._size = this._calcSize();
79
+ this.size = getSize(this._size, this.container.canvas.size);
116
80
  this._lifeCount = this.options.life.count ?? -1;
117
81
  this._immortal = this._lifeCount <= 0;
82
+ if (this.options.domId) {
83
+ const element = document.getElementById(this.options.domId);
84
+ if (element) {
85
+ this._mutationObserver = new MutationObserver(() => {
86
+ this.resize();
87
+ });
88
+ this._resizeObserver = new ResizeObserver(() => {
89
+ this.resize();
90
+ });
91
+ this._mutationObserver.observe(element, {
92
+ attributes: true,
93
+ attributeFilter: ["style", "width", "height"],
94
+ });
95
+ this._resizeObserver.observe(element);
96
+ }
97
+ }
98
+ const shapeOptions = this.options.shape, shapeGenerator = this._engine.emitterShapeManager?.getShapeGenerator(shapeOptions.type);
99
+ if (shapeGenerator) {
100
+ this._shape = shapeGenerator.generate(this.position, this.size, this.fill, shapeOptions.options);
101
+ }
118
102
  this._engine.dispatchEvent("emitterCreated", {
119
103
  container,
120
104
  data: {
@@ -131,32 +115,8 @@ export class EmitterInstance {
131
115
  this._paused = false;
132
116
  this.play();
133
117
  }
134
- getPosition() {
135
- if (this.options.domId) {
136
- const container = this.container, element = document.getElementById(this.options.domId);
137
- if (element) {
138
- const elRect = element.getBoundingClientRect();
139
- return {
140
- x: (elRect.x + elRect.width / 2) * container.retina.pixelRatio,
141
- y: (elRect.y + elRect.height / 2) * container.retina.pixelRatio,
142
- };
143
- }
144
- }
145
- return this.position;
146
- }
147
- getSize() {
148
- const container = this.container;
149
- if (this.options.domId) {
150
- const element = document.getElementById(this.options.domId);
151
- if (element) {
152
- const elRect = element.getBoundingClientRect();
153
- return {
154
- width: elRect.width * container.retina.pixelRatio,
155
- height: elRect.height * container.retina.pixelRatio,
156
- };
157
- }
158
- }
159
- return getSize(this.size, container.canvas.size);
118
+ async init() {
119
+ await this._shape?.init();
160
120
  }
161
121
  pause() {
162
122
  if (this._paused) {
@@ -187,8 +147,11 @@ export class EmitterInstance {
187
147
  initialPosition && isPointInside(initialPosition, this.container.canvas.size, Vector.origin)
188
148
  ? initialPosition
189
149
  : this._calcPosition();
150
+ this._size = this._calcSize();
151
+ this.size = getSize(this._size, this.container.canvas.size);
152
+ this._shape?.resize(this.position, this.size);
190
153
  }
191
- update(delta) {
154
+ async update(delta) {
192
155
  if (this._paused) {
193
156
  return;
194
157
  }
@@ -199,7 +162,7 @@ export class EmitterInstance {
199
162
  }
200
163
  if (!this._startParticlesAdded) {
201
164
  this._startParticlesAdded = true;
202
- this._emitParticles(this.options.startCount);
165
+ await this._emitParticles(this.options.startCount);
203
166
  }
204
167
  if (this._duration !== undefined) {
205
168
  this._currentDuration += delta.value;
@@ -242,4 +205,94 @@ export class EmitterInstance {
242
205
  }
243
206
  }
244
207
  }
208
+ _calcPosition() {
209
+ if (this.options.domId) {
210
+ const container = this.container, element = document.getElementById(this.options.domId);
211
+ if (element) {
212
+ const elRect = element.getBoundingClientRect();
213
+ return {
214
+ x: (elRect.x + elRect.width / 2) * container.retina.pixelRatio,
215
+ y: (elRect.y + elRect.height / 2) * container.retina.pixelRatio,
216
+ };
217
+ }
218
+ }
219
+ return calcPositionOrRandomFromSizeRanged({
220
+ size: this.container.canvas.size,
221
+ position: this.options.position,
222
+ });
223
+ }
224
+ _calcSize() {
225
+ const container = this.container;
226
+ if (this.options.domId) {
227
+ const element = document.getElementById(this.options.domId);
228
+ if (element) {
229
+ const elRect = element.getBoundingClientRect();
230
+ return {
231
+ width: elRect.width * container.retina.pixelRatio,
232
+ height: elRect.height * container.retina.pixelRatio,
233
+ mode: "precise",
234
+ };
235
+ }
236
+ }
237
+ return (this.options.size ??
238
+ (() => {
239
+ const size = new EmitterSize();
240
+ size.load({
241
+ height: 0,
242
+ mode: "percent",
243
+ width: 0,
244
+ });
245
+ return size;
246
+ })());
247
+ }
248
+ async _emit() {
249
+ if (this._paused) {
250
+ return;
251
+ }
252
+ const quantity = getRangeValue(this.options.rate.quantity);
253
+ await this._emitParticles(quantity);
254
+ }
255
+ async _emitParticles(quantity) {
256
+ const singleParticlesOptions = itemFromSingleOrMultiple(this._particlesOptions);
257
+ for (let i = 0; i < quantity; i++) {
258
+ const particlesOptions = deepExtend({}, singleParticlesOptions);
259
+ if (this.spawnColor) {
260
+ const hslAnimation = this.options.spawnColor?.animation;
261
+ if (hslAnimation) {
262
+ this.spawnColor.h = this._setColorAnimation(hslAnimation.h, this.spawnColor.h, 360);
263
+ this.spawnColor.s = this._setColorAnimation(hslAnimation.s, this.spawnColor.s, 100);
264
+ this.spawnColor.l = this._setColorAnimation(hslAnimation.l, this.spawnColor.l, 100);
265
+ }
266
+ setParticlesOptionsColor(particlesOptions, this.spawnColor);
267
+ }
268
+ const shapeOptions = this.options.shape;
269
+ let position = this.position;
270
+ if (this._shape) {
271
+ const shapePosData = await this._shape.randomPosition();
272
+ if (shapePosData) {
273
+ position = shapePosData.position;
274
+ const replaceData = shapeOptions.replace;
275
+ if (replaceData.color && shapePosData.color) {
276
+ setParticlesOptionsColor(particlesOptions, shapePosData.color);
277
+ }
278
+ if (replaceData.opacity) {
279
+ if (particlesOptions.opacity) {
280
+ particlesOptions.opacity.value = shapePosData.opacity;
281
+ }
282
+ else {
283
+ particlesOptions.opacity = {
284
+ value: shapePosData.opacity,
285
+ };
286
+ }
287
+ }
288
+ }
289
+ else {
290
+ position = null;
291
+ }
292
+ }
293
+ if (position) {
294
+ this.container.particles.addParticle(position, particlesOptions);
295
+ }
296
+ }
297
+ }
245
298
  }
@@ -0,0 +1,12 @@
1
+ export class EmitterShapeBase {
2
+ constructor(position, size, fill, options) {
3
+ this.position = position;
4
+ this.size = size;
5
+ this.fill = fill;
6
+ this.options = options;
7
+ }
8
+ resize(position, size) {
9
+ this.position = position;
10
+ this.size = size;
11
+ }
12
+ }
@@ -17,7 +17,7 @@ export class Emitters {
17
17
  container.getEmitter = (idxOrName) => idxOrName === undefined || isNumber(idxOrName)
18
18
  ? this.array[idxOrName || 0]
19
19
  : this.array.find((t) => t.name === idxOrName);
20
- container.addEmitter = (options, position) => this.addEmitter(options, position);
20
+ container.addEmitter = async (options, position) => this.addEmitter(options, position);
21
21
  container.removeEmitter = (idxOrName) => {
22
22
  const emitter = container.getEmitter(idxOrName);
23
23
  if (emitter) {
@@ -37,10 +37,11 @@ export class Emitters {
37
37
  }
38
38
  };
39
39
  }
40
- addEmitter(options, position) {
40
+ async addEmitter(options, position) {
41
41
  const emitterOptions = new Emitter();
42
42
  emitterOptions.load(options);
43
43
  const emitter = new EmitterInstance(this._engine, this, this.container, emitterOptions, position);
44
+ await emitter.init();
44
45
  this.array.push(emitter);
45
46
  return emitter;
46
47
  }
@@ -84,11 +85,11 @@ export class Emitters {
84
85
  }
85
86
  if (isArray(this.emitters)) {
86
87
  for (const emitterOptions of this.emitters) {
87
- this.addEmitter(emitterOptions);
88
+ await this.addEmitter(emitterOptions);
88
89
  }
89
90
  }
90
91
  else {
91
- this.addEmitter(this.emitters);
92
+ await this.addEmitter(this.emitters);
92
93
  }
93
94
  }
94
95
  pause() {
@@ -115,9 +116,9 @@ export class Emitters {
115
116
  stop() {
116
117
  this.array = [];
117
118
  }
118
- update(delta) {
119
+ async update(delta) {
119
120
  for (const emitter of this.array) {
120
- emitter.update(delta);
121
+ await emitter.update(delta);
121
122
  }
122
123
  }
123
124
  }
@@ -1,6 +1,7 @@
1
1
  import { AnimatableColor, deepExtend, executeOnSingleOrMultiple, setRangeValue, } from "@tsparticles/engine";
2
2
  import { EmitterLife } from "./EmitterLife.js";
3
3
  import { EmitterRate } from "./EmitterRate.js";
4
+ import { EmitterShape } from "./EmitterShape.js";
4
5
  import { EmitterSize } from "./EmitterSize.js";
5
6
  export class Emitter {
6
7
  constructor() {
@@ -8,7 +9,7 @@ export class Emitter {
8
9
  this.fill = true;
9
10
  this.life = new EmitterLife();
10
11
  this.rate = new EmitterRate();
11
- this.shape = "square";
12
+ this.shape = new EmitterShape();
12
13
  this.startCount = 0;
13
14
  }
14
15
  load(data) {
@@ -37,9 +38,7 @@ export class Emitter {
37
38
  return deepExtend({}, particles);
38
39
  });
39
40
  this.rate.load(data.rate);
40
- if (data.shape !== undefined) {
41
- this.shape = data.shape;
42
- }
41
+ this.shape.load(data.shape);
43
42
  if (data.position !== undefined) {
44
43
  this.position = {};
45
44
  if (data.position.x !== undefined) {
@@ -0,0 +1,21 @@
1
+ import { deepExtend } from "@tsparticles/engine";
2
+ import { EmitterShapeReplace } from "./EmitterShapeReplace.js";
3
+ export class EmitterShape {
4
+ constructor() {
5
+ this.options = {};
6
+ this.replace = new EmitterShapeReplace();
7
+ this.type = "square";
8
+ }
9
+ load(data) {
10
+ if (!data) {
11
+ return;
12
+ }
13
+ if (data.options !== undefined) {
14
+ this.options = deepExtend({}, data.options ?? {});
15
+ }
16
+ this.replace.load(data.replace);
17
+ if (data.type !== undefined) {
18
+ this.type = data.type;
19
+ }
20
+ }
21
+ }
@@ -0,0 +1,17 @@
1
+ export class EmitterShapeReplace {
2
+ constructor() {
3
+ this.color = false;
4
+ this.opacity = false;
5
+ }
6
+ load(data) {
7
+ if (!data) {
8
+ return;
9
+ }
10
+ if (data.color !== undefined) {
11
+ this.color = data.color;
12
+ }
13
+ if (data.opacity !== undefined) {
14
+ this.opacity = data.opacity;
15
+ }
16
+ }
17
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -1,17 +1,17 @@
1
- const shapes = new Map();
1
+ const shapeGeneratorss = new Map();
2
2
  export class ShapeManager {
3
3
  constructor(engine) {
4
4
  this._engine = engine;
5
5
  }
6
- addShape(name, drawer) {
7
- if (!this.getShape(name)) {
8
- shapes.set(name, drawer);
6
+ addShapeGenerator(name, generator) {
7
+ if (!this.getShapeGenerator(name)) {
8
+ shapeGeneratorss.set(name, generator);
9
9
  }
10
10
  }
11
- getShape(name) {
12
- return shapes.get(name);
11
+ getShapeGenerator(name) {
12
+ return shapeGeneratorss.get(name);
13
13
  }
14
- getSupportedShapes() {
15
- return shapes.keys();
14
+ getSupportedShapeGenerators() {
15
+ return shapeGeneratorss.keys();
16
16
  }
17
17
  }
package/browser/index.js CHANGED
@@ -1,9 +1,7 @@
1
1
  import { executeOnSingleOrMultiple, isArray, isInArray, } from "@tsparticles/engine";
2
- import { CircleShape } from "./Shapes/Circle/CircleShape.js";
3
2
  import { Emitter } from "./Options/Classes/Emitter.js";
4
3
  import { Emitters } from "./Emitters.js";
5
4
  import { ShapeManager } from "./ShapeManager.js";
6
- import { SquareShape } from "./Shapes/Square/SquareShape.js";
7
5
  class EmittersPlugin {
8
6
  constructor(engine) {
9
7
  this._engine = engine;
@@ -94,17 +92,18 @@ export async function loadEmittersPlugin(engine, refresh = true) {
94
92
  if (!engine.emitterShapeManager) {
95
93
  engine.emitterShapeManager = new ShapeManager(engine);
96
94
  }
97
- if (!engine.addEmitterShape) {
98
- engine.addEmitterShape = (name, shape) => {
99
- engine.emitterShapeManager?.addShape(name, shape);
95
+ if (!engine.addEmitterShapeGenerator) {
96
+ engine.addEmitterShapeGenerator = (name, generator) => {
97
+ engine.emitterShapeManager?.addShapeGenerator(name, generator);
100
98
  };
101
99
  }
102
100
  const plugin = new EmittersPlugin(engine);
103
101
  await engine.addPlugin(plugin, refresh);
104
- engine.addEmitterShape("circle", new CircleShape());
105
- engine.addEmitterShape("square", new SquareShape());
106
102
  }
107
103
  export * from "./EmitterContainer.js";
104
+ export * from "./EmitterShapeBase.js";
108
105
  export * from "./EmittersEngine.js";
106
+ export * from "./IEmitterShape.js";
107
+ export * from "./IEmitterShapeGenerator.js";
109
108
  export * from "./Enums/EmitterClickMode.js";
110
- export * from "./Enums/EmitterShapeType.js";
109
+ export * from "./IRandomPositionData.js";