@tsparticles/engine 4.0.5 → 4.1.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 (102) hide show
  1. package/browser/Core/CanvasManager.js +72 -72
  2. package/browser/Core/Container.js +85 -85
  3. package/browser/Core/Engine.js +11 -11
  4. package/browser/Core/Particle.js +142 -63
  5. package/browser/Core/ParticlesManager.js +138 -138
  6. package/browser/Core/RenderManager.js +110 -110
  7. package/browser/Core/Retina.js +3 -4
  8. package/browser/Core/Utils/EventListeners.js +31 -31
  9. package/browser/Core/Utils/PluginManager.js +26 -26
  10. package/browser/Core/Utils/SpatialHashGrid.js +36 -36
  11. package/browser/Core/Utils/Vectors.js +3 -3
  12. package/browser/Options/Classes/Options.js +13 -13
  13. package/browser/Options/Classes/Particles/ParticlesOptions.js +11 -19
  14. package/browser/Utils/EventDispatcher.js +10 -10
  15. package/browser/exports.js +0 -4
  16. package/cjs/Core/CanvasManager.js +72 -72
  17. package/cjs/Core/Container.js +85 -85
  18. package/cjs/Core/Engine.js +11 -11
  19. package/cjs/Core/Particle.js +142 -63
  20. package/cjs/Core/ParticlesManager.js +138 -138
  21. package/cjs/Core/RenderManager.js +110 -110
  22. package/cjs/Core/Retina.js +3 -4
  23. package/cjs/Core/Utils/EventListeners.js +31 -31
  24. package/cjs/Core/Utils/PluginManager.js +26 -26
  25. package/cjs/Core/Utils/SpatialHashGrid.js +36 -36
  26. package/cjs/Core/Utils/Vectors.js +3 -3
  27. package/cjs/Options/Classes/Options.js +13 -13
  28. package/cjs/Options/Classes/Particles/ParticlesOptions.js +11 -19
  29. package/cjs/Utils/EventDispatcher.js +10 -10
  30. package/cjs/exports.js +0 -4
  31. package/esm/Core/CanvasManager.js +72 -72
  32. package/esm/Core/Container.js +85 -85
  33. package/esm/Core/Engine.js +11 -11
  34. package/esm/Core/Particle.js +142 -63
  35. package/esm/Core/ParticlesManager.js +138 -138
  36. package/esm/Core/RenderManager.js +110 -110
  37. package/esm/Core/Retina.js +3 -4
  38. package/esm/Core/Utils/EventListeners.js +31 -31
  39. package/esm/Core/Utils/PluginManager.js +26 -26
  40. package/esm/Core/Utils/SpatialHashGrid.js +36 -36
  41. package/esm/Core/Utils/Vectors.js +3 -3
  42. package/esm/Options/Classes/Options.js +13 -13
  43. package/esm/Options/Classes/Particles/ParticlesOptions.js +11 -19
  44. package/esm/Utils/EventDispatcher.js +10 -10
  45. package/esm/exports.js +0 -4
  46. package/package.json +1 -1
  47. package/report.html +1 -1
  48. package/tsparticles.engine.js +690 -697
  49. package/tsparticles.engine.min.js +1 -1
  50. package/types/Core/CanvasManager.d.ts +1 -16
  51. package/types/Core/Container.d.ts +1 -18
  52. package/types/Core/Engine.d.ts +1 -3
  53. package/types/Core/Interfaces/IEffectDrawer.d.ts +2 -0
  54. package/types/Core/Interfaces/IParticleCanvasBounds.d.ts +16 -0
  55. package/types/Core/Interfaces/IParticleUpdater.d.ts +1 -0
  56. package/types/Core/Interfaces/IShapeDrawer.d.ts +2 -0
  57. package/types/Core/Particle.d.ts +5 -13
  58. package/types/Core/ParticlesManager.d.ts +1 -28
  59. package/types/Core/RenderManager.d.ts +1 -23
  60. package/types/Core/Retina.d.ts +1 -1
  61. package/types/Core/Utils/EventListeners.d.ts +1 -8
  62. package/types/Core/Utils/PluginManager.d.ts +1 -8
  63. package/types/Core/Utils/SpatialHashGrid.d.ts +1 -12
  64. package/types/Core/Utils/Vectors.d.ts +1 -1
  65. package/types/Options/Classes/Options.d.ts +1 -4
  66. package/types/Options/Classes/Particles/ParticlesOptions.d.ts +1 -7
  67. package/types/Options/Interfaces/Particles/IParticlesOptions.d.ts +0 -4
  68. package/types/Utils/EventDispatcher.d.ts +1 -1
  69. package/types/export-types.d.ts +1 -4
  70. package/types/exports.d.ts +0 -4
  71. package/browser/Options/Classes/Particles/Opacity/Opacity.js +0 -21
  72. package/browser/Options/Classes/Particles/Opacity/OpacityAnimation.js +0 -20
  73. package/browser/Options/Classes/Particles/Size/Size.js +0 -21
  74. package/browser/Options/Classes/Particles/Size/SizeAnimation.js +0 -20
  75. package/browser/Options/Interfaces/Particles/Size/ISizeAnimation.js +0 -1
  76. package/cjs/Options/Classes/Particles/Opacity/Opacity.js +0 -21
  77. package/cjs/Options/Classes/Particles/Opacity/OpacityAnimation.js +0 -20
  78. package/cjs/Options/Classes/Particles/Size/Size.js +0 -21
  79. package/cjs/Options/Classes/Particles/Size/SizeAnimation.js +0 -20
  80. package/cjs/Options/Interfaces/Particles/Opacity/IOpacity.js +0 -1
  81. package/cjs/Options/Interfaces/Particles/Opacity/IOpacityAnimation.js +0 -1
  82. package/cjs/Options/Interfaces/Particles/Size/ISize.js +0 -1
  83. package/cjs/Options/Interfaces/Particles/Size/ISizeAnimation.js +0 -1
  84. package/esm/Options/Classes/Particles/Opacity/Opacity.js +0 -21
  85. package/esm/Options/Classes/Particles/Opacity/OpacityAnimation.js +0 -20
  86. package/esm/Options/Classes/Particles/Size/Size.js +0 -21
  87. package/esm/Options/Classes/Particles/Size/SizeAnimation.js +0 -20
  88. package/esm/Options/Interfaces/Particles/Opacity/IOpacity.js +0 -1
  89. package/esm/Options/Interfaces/Particles/Opacity/IOpacityAnimation.js +0 -1
  90. package/esm/Options/Interfaces/Particles/Size/ISize.js +0 -1
  91. package/esm/Options/Interfaces/Particles/Size/ISizeAnimation.js +0 -1
  92. package/types/Options/Classes/Particles/Opacity/Opacity.d.ts +0 -10
  93. package/types/Options/Classes/Particles/Opacity/OpacityAnimation.d.ts +0 -10
  94. package/types/Options/Classes/Particles/Size/Size.d.ts +0 -10
  95. package/types/Options/Classes/Particles/Size/SizeAnimation.d.ts +0 -10
  96. package/types/Options/Interfaces/Particles/Opacity/IOpacity.d.ts +0 -5
  97. package/types/Options/Interfaces/Particles/Opacity/IOpacityAnimation.d.ts +0 -5
  98. package/types/Options/Interfaces/Particles/Size/ISize.d.ts +0 -5
  99. package/types/Options/Interfaces/Particles/Size/ISizeAnimation.d.ts +0 -5
  100. /package/browser/{Options/Interfaces/Particles/Opacity/IOpacity.js → Core/Interfaces/IParticleCanvasBounds.js} +0 -0
  101. /package/{browser/Options/Interfaces/Particles/Opacity/IOpacityAnimation.js → cjs/Core/Interfaces/IParticleCanvasBounds.js} +0 -0
  102. /package/{browser/Options/Interfaces/Particles/Size/ISize.js → esm/Core/Interfaces/IParticleCanvasBounds.js} +0 -0
@@ -62,17 +62,17 @@ const getCanvasFromContainer = (domContainer) => {
62
62
  };
63
63
  export class Engine {
64
64
  pluginManager = new PluginManager(this);
65
- _domArray = [];
66
- _eventDispatcher = new EventDispatcher();
67
- _initialized = false;
65
+ #domArray = [];
66
+ #eventDispatcher = new EventDispatcher();
67
+ #initialized = false;
68
68
  get items() {
69
- return this._domArray;
69
+ return this.#domArray;
70
70
  }
71
71
  get version() {
72
- return "4.0.5";
72
+ return "4.1.0";
73
73
  }
74
74
  addEventListener(type, listener) {
75
- this._eventDispatcher.addEventListener(type, listener);
75
+ this.#eventDispatcher.addEventListener(type, listener);
76
76
  }
77
77
  checkVersion(pluginVersion) {
78
78
  if (this.version === pluginVersion) {
@@ -81,17 +81,17 @@ export class Engine {
81
81
  throw new Error(`The tsParticles version is different from the loaded plugins version. Engine version: ${this.version}. Plugin version: ${pluginVersion}`);
82
82
  }
83
83
  dispatchEvent(type, args) {
84
- this._eventDispatcher.dispatchEvent(type, args);
84
+ this.#eventDispatcher.dispatchEvent(type, args);
85
85
  }
86
86
  async init() {
87
- if (this._initialized) {
87
+ if (this.#initialized) {
88
88
  return;
89
89
  }
90
90
  await this.pluginManager.init();
91
- this._initialized = true;
91
+ this.#initialized = true;
92
92
  }
93
93
  item(index) {
94
- const { items } = this, item = items[index];
94
+ const items = this.items, item = items[index];
95
95
  if (item?.destroyed) {
96
96
  items.splice(index, removeDeleteCount);
97
97
  return;
@@ -145,6 +145,6 @@ export class Engine {
145
145
  await Promise.all(this.items.map(t => t.refresh()));
146
146
  }
147
147
  removeEventListener(type, listener) {
148
- this._eventDispatcher.removeEventListener(type, listener);
148
+ this.#eventDispatcher.removeEventListener(type, listener);
149
149
  }
150
150
  }
@@ -1,11 +1,12 @@
1
1
  import { Vector, Vector3d } from "./Utils/Vectors.js";
2
2
  import { alterHsl, getHslFromAnimation } from "../Utils/ColorUtils.js";
3
3
  import { calcExactPositionOrRandomFromSize, clamp, degToRad, getParticleBaseVelocity, getParticleDirectionAngle, getRandom, getRangeValue, randomInRangeValue, setRangeValue, } from "../Utils/MathUtils.js";
4
- import { deepExtend, getPosition, initParticleNumericAnimationValue, isInArray, itemFromSingleOrMultiple, } from "../Utils/Utils.js";
4
+ import { deepExtend, getPosition, isInArray, itemFromSingleOrMultiple } from "../Utils/Utils.js";
5
5
  import { defaultAngle, defaultOpacity, defaultRetryCount, defaultTransform, double, doublePI, half, identity, minZ, randomColorValue, squareExp, triple, tryCountIncrement, zIndexFactorOffset, } from "./Utils/Constants.js";
6
6
  import { EventType } from "../Enums/Types/EventType.js";
7
7
  import { MoveDirection } from "../Enums/Directions/MoveDirection.js";
8
8
  import { OutMode } from "../Enums/Modes/OutMode.js";
9
+ import { OutModeDirection } from "../Enums/Directions/OutModeDirection.js";
9
10
  import { ParticleOutType } from "../Enums/Types/ParticleOutType.js";
10
11
  import { loadParticlesOptions } from "../Utils/OptionsUtils.js";
11
12
  function loadEffectData(effect, effectOptions, id, reduceDuplicates) {
@@ -76,24 +77,24 @@ export class Particle {
76
77
  unbreakable;
77
78
  velocity;
78
79
  zIndexFactor;
79
- _cachedOpacityData = {
80
+ #cachedOpacityData = {
80
81
  fillOpacity: defaultOpacity,
81
82
  opacity: defaultOpacity,
82
83
  strokeOpacity: defaultOpacity,
83
84
  };
84
- _cachedPosition = Vector3d.origin;
85
- _cachedRotateData = { sin: 0, cos: 0 };
86
- _cachedTransform = {
85
+ #cachedPosition = Vector3d.origin;
86
+ #cachedRotateData = { sin: 0, cos: 0 };
87
+ #cachedTransform = {
87
88
  a: 1,
88
89
  b: 0,
89
90
  c: 0,
90
91
  d: 1,
91
92
  };
92
- _container;
93
- _pluginManager;
93
+ #container;
94
+ #pluginManager;
94
95
  constructor(pluginManager, container) {
95
- this._pluginManager = pluginManager;
96
- this._container = container;
96
+ this.#pluginManager = pluginManager;
97
+ this.#container = container;
97
98
  }
98
99
  destroy(override) {
99
100
  if (this.unbreakable || this.destroyed) {
@@ -102,7 +103,7 @@ export class Particle {
102
103
  this.destroyed = true;
103
104
  this.bubble.inRange = false;
104
105
  this.slow.inRange = false;
105
- const container = this._container, shapeDrawer = this.shape ? container.shapeDrawers.get(this.shape) : undefined;
106
+ const container = this.#container, shapeDrawer = this.shape ? container.shapeDrawers.get(this.shape) : undefined;
106
107
  shapeDrawer?.particleDestroy?.(this);
107
108
  for (const plugin of container.particleDestroyedPlugins) {
108
109
  plugin.particleDestroyed?.(this, override);
@@ -110,12 +111,12 @@ export class Particle {
110
111
  for (const updater of container.particleUpdaters) {
111
112
  updater.particleDestroyed?.(this, override);
112
113
  }
113
- this._container.dispatchEvent(EventType.particleDestroyed, {
114
+ this.#container.dispatchEvent(EventType.particleDestroyed, {
114
115
  particle: this,
115
116
  });
116
117
  }
117
118
  draw(delta) {
118
- const container = this._container, render = container.canvas.render;
119
+ const container = this.#container, render = container.canvas.render;
119
120
  render.drawParticlePlugins(this, delta);
120
121
  render.drawParticle(this, delta);
121
122
  }
@@ -123,50 +124,50 @@ export class Particle {
123
124
  return this.rotation + (this.pathRotation ? this.velocity.angle : defaultAngle);
124
125
  }
125
126
  getFillColor() {
126
- return this._getRollColor(this.bubble.color ?? getHslFromAnimation(this.fillColor));
127
+ return this.#getRollColor(this.bubble.color ?? getHslFromAnimation(this.fillColor));
127
128
  }
128
129
  getMass() {
129
130
  return this.getRadius() ** squareExp * Math.PI * half;
130
131
  }
131
132
  getOpacity() {
132
133
  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;
133
- this._cachedOpacityData.fillOpacity = opacity * fillOpacity * zOpacityFactor;
134
- this._cachedOpacityData.opacity = opacity * zOpacityFactor;
135
- this._cachedOpacityData.strokeOpacity = opacity * strokeOpacity * zOpacityFactor;
136
- return this._cachedOpacityData;
134
+ this.#cachedOpacityData.fillOpacity = opacity * fillOpacity * zOpacityFactor;
135
+ this.#cachedOpacityData.opacity = opacity * zOpacityFactor;
136
+ this.#cachedOpacityData.strokeOpacity = opacity * strokeOpacity * zOpacityFactor;
137
+ return this.#cachedOpacityData;
137
138
  }
138
139
  getPosition() {
139
- this._cachedPosition.x = this.position.x + this.offset.x;
140
- this._cachedPosition.y = this.position.y + this.offset.y;
141
- this._cachedPosition.z = this.position.z;
142
- return this._cachedPosition;
140
+ this.#cachedPosition.x = this.position.x + this.offset.x;
141
+ this.#cachedPosition.y = this.position.y + this.offset.y;
142
+ this.#cachedPosition.z = this.position.z;
143
+ return this.#cachedPosition;
143
144
  }
144
145
  getRadius() {
145
146
  return this.bubble.radius ?? this.size.value;
146
147
  }
147
148
  getRotateData() {
148
149
  const angle = this.getAngle();
149
- this._cachedRotateData.sin = Math.sin(angle);
150
- this._cachedRotateData.cos = Math.cos(angle);
151
- return this._cachedRotateData;
150
+ this.#cachedRotateData.sin = Math.sin(angle);
151
+ this.#cachedRotateData.cos = Math.cos(angle);
152
+ return this.#cachedRotateData;
152
153
  }
153
154
  getStrokeColor() {
154
- return this._getRollColor(this.bubble.color ?? getHslFromAnimation(this.strokeColor));
155
+ return this.#getRollColor(this.bubble.color ?? getHslFromAnimation(this.strokeColor));
155
156
  }
156
157
  getTransformData(externalTransform) {
157
158
  const rotateData = this.getRotateData(), rotating = this.isRotating;
158
- this._cachedTransform.a = rotateData.cos * (externalTransform.a ?? defaultTransform.a);
159
- this._cachedTransform.b = rotating
159
+ this.#cachedTransform.a = rotateData.cos * (externalTransform.a ?? defaultTransform.a);
160
+ this.#cachedTransform.b = rotating
160
161
  ? rotateData.sin * (externalTransform.b ?? identity)
161
162
  : (externalTransform.b ?? defaultTransform.b);
162
- this._cachedTransform.c = rotating
163
+ this.#cachedTransform.c = rotating
163
164
  ? -rotateData.sin * (externalTransform.c ?? identity)
164
165
  : (externalTransform.c ?? defaultTransform.c);
165
- this._cachedTransform.d = rotateData.cos * (externalTransform.d ?? defaultTransform.d);
166
- return this._cachedTransform;
166
+ this.#cachedTransform.d = rotateData.cos * (externalTransform.d ?? defaultTransform.d);
167
+ return this.#cachedTransform;
167
168
  }
168
169
  init(id, position, overrideOptions, group) {
169
- const container = this._container;
170
+ const container = this.#container;
170
171
  this.id = id;
171
172
  this.group = group;
172
173
  this.justWarped = false;
@@ -186,21 +187,27 @@ export class Particle {
186
187
  moveSpeed: 0,
187
188
  sizeAnimationSpeed: 0,
188
189
  };
190
+ this.size = {
191
+ value: 1,
192
+ max: 1,
193
+ min: 1,
194
+ enable: false,
195
+ };
189
196
  this.outType = ParticleOutType.normal;
190
197
  this.ignoresResizeRatio = true;
191
- const pxRatio = container.retina.pixelRatio, mainOptions = container.actualOptions, particlesOptions = loadParticlesOptions(this._pluginManager, container, mainOptions.particles), reduceDuplicates = particlesOptions.reduceDuplicates, effectType = particlesOptions.effect.type, shapeType = particlesOptions.shape.type;
198
+ const mainOptions = container.actualOptions, particlesOptions = loadParticlesOptions(this.#pluginManager, container, mainOptions.particles), reduceDuplicates = particlesOptions.reduceDuplicates, effectType = particlesOptions.effect.type, shapeType = particlesOptions.shape.type;
192
199
  this.effect = itemFromSingleOrMultiple(effectType, this.id, reduceDuplicates);
193
200
  this.shape = itemFromSingleOrMultiple(shapeType, this.id, reduceDuplicates);
194
201
  const effectOptions = particlesOptions.effect, shapeOptions = particlesOptions.shape;
195
202
  if (overrideOptions) {
196
- if (overrideOptions.effect?.type) {
203
+ if (overrideOptions.effect?.type && overrideOptions.effect.type !== this.effect) {
197
204
  const overrideEffectType = overrideOptions.effect.type, effect = itemFromSingleOrMultiple(overrideEffectType, this.id, reduceDuplicates);
198
205
  if (effect) {
199
206
  this.effect = effect;
200
207
  effectOptions.load(overrideOptions.effect);
201
208
  }
202
209
  }
203
- if (overrideOptions.shape?.type) {
210
+ if (overrideOptions.shape?.type && overrideOptions.shape.type !== this.shape) {
204
211
  const overrideShapeType = overrideOptions.shape.type, shape = itemFromSingleOrMultiple(overrideShapeType, this.id, reduceDuplicates);
205
212
  if (shape) {
206
213
  this.shape = shape;
@@ -209,21 +216,20 @@ export class Particle {
209
216
  }
210
217
  }
211
218
  if (this.effect === randomColorValue) {
212
- const availableEffects = [...this._container.effectDrawers.keys()];
219
+ const availableEffects = [...this.#container.effectDrawers.keys()];
213
220
  this.effect = availableEffects[Math.floor(getRandom() * availableEffects.length)];
214
221
  }
215
222
  if (this.shape === randomColorValue) {
216
- const availableShapes = [...this._container.shapeDrawers.keys()];
223
+ const availableShapes = [...this.#container.shapeDrawers.keys()];
217
224
  this.shape = availableShapes[Math.floor(getRandom() * availableShapes.length)];
218
225
  }
219
226
  this.effectData = this.effect ? loadEffectData(this.effect, effectOptions, this.id, reduceDuplicates) : undefined;
220
227
  this.shapeData = this.shape ? loadShapeData(this.shape, shapeOptions, this.id, reduceDuplicates) : undefined;
221
228
  particlesOptions.load(overrideOptions);
222
- const effectData = this.effectData;
229
+ const effectData = this.effectData, shapeData = this.shapeData;
223
230
  if (effectData) {
224
231
  particlesOptions.load(effectData.particles);
225
232
  }
226
- const shapeData = this.shapeData;
227
233
  if (shapeData) {
228
234
  particlesOptions.load(shapeData.particles);
229
235
  }
@@ -231,7 +237,9 @@ export class Particle {
231
237
  this.shapeClose = shapeData?.close ?? particlesOptions.shape.close;
232
238
  this.options = particlesOptions;
233
239
  container.retina.initParticle(this);
234
- this.size = initParticleNumericAnimationValue(this.options.size, pxRatio);
240
+ for (const updater of container.particleUpdaters) {
241
+ updater.preInit?.(this);
242
+ }
235
243
  this.bubble = {
236
244
  inRange: false,
237
245
  };
@@ -239,8 +247,8 @@ export class Particle {
239
247
  inRange: false,
240
248
  factor: 1,
241
249
  };
242
- this._initPosition(position);
243
- this.initialVelocity = this._calculateVelocity();
250
+ this.#initPosition(position);
251
+ this.initialVelocity = this.#calculateVelocity();
244
252
  this.velocity = this.initialVelocity.copy();
245
253
  this.zIndexFactor = this.position.z / container.zLayers;
246
254
  this.sides = 24;
@@ -271,12 +279,11 @@ export class Particle {
271
279
  plugin.particleCreated?.(this);
272
280
  }
273
281
  }
274
- isInsideCanvas() {
275
- const radius = this.getRadius(), canvasSize = this._container.canvas.size, position = this.position;
276
- return (position.x >= -radius &&
277
- position.y >= -radius &&
278
- position.y <= canvasSize.height + radius &&
279
- position.x <= canvasSize.width + radius);
282
+ isInsideCanvas(direction) {
283
+ return this.#getInsideCanvasResult({ direction }).inside;
284
+ }
285
+ isInsideCanvasForOutMode(outMode, direction) {
286
+ return this.#getInsideCanvasResult({ direction, outMode }).inside;
280
287
  }
281
288
  isShowingBack() {
282
289
  if (!this.roll) {
@@ -301,13 +308,13 @@ export class Particle {
301
308
  return !this.destroyed && !this.spawning && this.isInsideCanvas();
302
309
  }
303
310
  reset() {
304
- for (const updater of this._container.particleUpdaters) {
311
+ for (const updater of this.#container.particleUpdaters) {
305
312
  updater.reset?.(this);
306
313
  }
307
314
  }
308
- _calcPosition = (position, zIndex) => {
315
+ #calcPosition = (position, zIndex) => {
309
316
  let tryCount = defaultRetryCount, posVec = position ? Vector3d.create(position.x, position.y, zIndex) : undefined;
310
- const container = this._container, plugins = container.particlePositionPlugins, outModes = this.options.move.outModes, radius = this.getRadius(), canvasSize = container.canvas.size, abortController = new AbortController(), { signal } = abortController;
317
+ const container = this.#container, plugins = container.particlePositionPlugins, outModes = this.options.move.outModes, radius = this.getRadius(), canvasSize = container.canvas.size, abortController = new AbortController(), { signal } = abortController;
311
318
  while (!signal.aborted) {
312
319
  for (const plugin of plugins) {
313
320
  const pluginPos = plugin.particlePosition?.(posVec, this);
@@ -319,10 +326,10 @@ export class Particle {
319
326
  size: canvasSize,
320
327
  position: posVec,
321
328
  }), pos = Vector3d.create(exactPosition.x, exactPosition.y, zIndex);
322
- this._fixHorizontal(pos, radius, outModes.left ?? outModes.default);
323
- this._fixHorizontal(pos, radius, outModes.right ?? outModes.default);
324
- this._fixVertical(pos, radius, outModes.top ?? outModes.default);
325
- this._fixVertical(pos, radius, outModes.bottom ?? outModes.default);
329
+ this.#fixHorizontal(pos, radius, outModes.left ?? outModes.default);
330
+ this.#fixHorizontal(pos, radius, outModes.right ?? outModes.default);
331
+ this.#fixVertical(pos, radius, outModes.top ?? outModes.default);
332
+ this.#fixVertical(pos, radius, outModes.bottom ?? outModes.default);
326
333
  let isValidPosition = true;
327
334
  for (const plugin of container.particles.checkParticlePositionPlugins) {
328
335
  isValidPosition = plugin.checkParticlePosition?.(this, pos, tryCount) ?? true;
@@ -338,8 +345,8 @@ export class Particle {
338
345
  }
339
346
  return posVec;
340
347
  };
341
- _calculateVelocity = () => {
342
- const baseVelocity = getParticleBaseVelocity(this.direction), res = baseVelocity.copy(), moveOptions = this.options.move;
348
+ #calculateVelocity = () => {
349
+ const moveOptions = this.options.move, baseVelocity = getParticleBaseVelocity(this.direction), res = baseVelocity.copy();
343
350
  if (moveOptions.direction === MoveDirection.inside || moveOptions.direction === MoveDirection.outside) {
344
351
  return res;
345
352
  }
@@ -355,27 +362,86 @@ export class Particle {
355
362
  }
356
363
  return res;
357
364
  };
358
- _fixHorizontal = (pos, radius, outMode) => {
365
+ #fixHorizontal = (pos, radius, outMode) => {
359
366
  fixOutMode({
360
367
  outMode,
361
368
  checkModes: [OutMode.bounce],
362
369
  coord: pos.x,
363
- maxCoord: this._container.canvas.size.width,
370
+ maxCoord: this.#container.canvas.size.width,
364
371
  setCb: (value) => (pos.x += value),
365
372
  radius,
366
373
  });
367
374
  };
368
- _fixVertical = (pos, radius, outMode) => {
375
+ #fixVertical = (pos, radius, outMode) => {
369
376
  fixOutMode({
370
377
  outMode,
371
378
  checkModes: [OutMode.bounce],
372
379
  coord: pos.y,
373
- maxCoord: this._container.canvas.size.height,
380
+ maxCoord: this.#container.canvas.size.height,
374
381
  setCb: (value) => (pos.y += value),
375
382
  radius,
376
383
  });
377
384
  };
378
- _getRollColor = color => {
385
+ #getDefaultInsideCanvasResult = (direction, outMode) => {
386
+ const radius = this.getRadius(), canvasSize = this.#container.canvas.size, position = this.position, isBounce = outMode === OutMode.bounce;
387
+ if (direction === OutModeDirection.bottom) {
388
+ return {
389
+ inside: isBounce ? position.y + radius < canvasSize.height : position.y - radius < canvasSize.height,
390
+ reason: "default",
391
+ };
392
+ }
393
+ if (direction === OutModeDirection.left) {
394
+ return {
395
+ inside: isBounce ? position.x - radius > defaultAngle : position.x + radius > defaultAngle,
396
+ reason: "default",
397
+ };
398
+ }
399
+ if (direction === OutModeDirection.right) {
400
+ return {
401
+ inside: isBounce ? position.x + radius < canvasSize.width : position.x - radius < canvasSize.width,
402
+ reason: "default",
403
+ };
404
+ }
405
+ if (direction === OutModeDirection.top) {
406
+ return {
407
+ inside: isBounce ? position.y - radius > defaultAngle : position.y + radius > defaultAngle,
408
+ reason: "default",
409
+ };
410
+ }
411
+ return {
412
+ inside: position.x >= -radius &&
413
+ position.y >= -radius &&
414
+ position.y <= canvasSize.height + radius &&
415
+ position.x <= canvasSize.width + radius,
416
+ reason: "default",
417
+ };
418
+ };
419
+ #getInsideCanvasCallbackData = (direction, outMode) => {
420
+ return {
421
+ canvasSize: this.#container.canvas.size,
422
+ direction,
423
+ outMode,
424
+ particle: this,
425
+ radius: this.getRadius(),
426
+ };
427
+ };
428
+ #getInsideCanvasResult = (data) => {
429
+ 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;
430
+ if (!shapeCheck && !effectCheck) {
431
+ return defaultResult;
432
+ }
433
+ const callbackData = this.#getInsideCanvasCallbackData(data.direction, data.outMode), shapeResult = shapeCheck ? this.#normalizeInsideCanvasResult(shapeCheck(callbackData), "shape") : undefined, effectResult = effectCheck ? this.#normalizeInsideCanvasResult(effectCheck(callbackData), "effect") : undefined;
434
+ if (shapeResult && effectResult) {
435
+ const margin = Math.max(shapeResult.margin ?? defaultAngle, effectResult.margin ?? defaultAngle);
436
+ return {
437
+ inside: shapeResult.inside && effectResult.inside,
438
+ margin: margin > defaultAngle ? margin : undefined,
439
+ reason: "combined",
440
+ };
441
+ }
442
+ return shapeResult ?? effectResult ?? defaultResult;
443
+ };
444
+ #getRollColor = color => {
379
445
  if (!color || !this.roll || (!this.backColor && !this.roll.alter)) {
380
446
  return color;
381
447
  }
@@ -390,8 +456,8 @@ export class Particle {
390
456
  }
391
457
  return color;
392
458
  };
393
- _initPosition = position => {
394
- const container = this._container, zIndexValue = Math.floor(getRangeValue(this.options.zIndex.value)), initialPosition = this._calcPosition(position, clamp(zIndexValue, minZ, container.zLayers));
459
+ #initPosition = position => {
460
+ const container = this.#container, zIndexValue = Math.floor(getRangeValue(this.options.zIndex.value)), initialPosition = this.#calcPosition(position, clamp(zIndexValue, minZ, container.zLayers));
395
461
  if (!initialPosition) {
396
462
  throw new Error("a valid position cannot be found for particle");
397
463
  }
@@ -416,4 +482,17 @@ export class Particle {
416
482
  }
417
483
  this.offset = Vector.origin;
418
484
  };
485
+ #normalizeInsideCanvasResult = (result, reason) => {
486
+ if (typeof result === "boolean") {
487
+ return {
488
+ inside: result,
489
+ reason,
490
+ };
491
+ }
492
+ return {
493
+ inside: result.inside,
494
+ margin: result.margin,
495
+ reason: result.reason ?? reason,
496
+ };
497
+ };
419
498
  }