@tsparticles/slim 4.0.0-beta.16 → 4.0.0-beta.17

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.
@@ -1,5 +1,5 @@
1
1
  (function(g){g.__tsParticlesInternals=g.__tsParticlesInternals||{};g.__tsParticlesInternals.bundles=g.__tsParticlesInternals.bundles||{};g.__tsParticlesInternals.effects=g.__tsParticlesInternals.effects||{};g.__tsParticlesInternals.engine=g.__tsParticlesInternals.engine||{};g.__tsParticlesInternals.interactions=g.__tsParticlesInternals.interactions||{};g.__tsParticlesInternals.palettes=g.__tsParticlesInternals.palettes||{};g.__tsParticlesInternals.paths=g.__tsParticlesInternals.paths||{};g.__tsParticlesInternals.plugins=g.__tsParticlesInternals.plugins||{};g.__tsParticlesInternals.plugins=g.__tsParticlesInternals.plugins||{};g.__tsParticlesInternals.plugins.emittersShapes=g.__tsParticlesInternals.plugins.emittersShapes||{};g.__tsParticlesInternals.presets=g.__tsParticlesInternals.presets||{};g.__tsParticlesInternals.shapes=g.__tsParticlesInternals.shapes||{};g.__tsParticlesInternals.updaters=g.__tsParticlesInternals.updaters||{};g.__tsParticlesInternals.utils=g.__tsParticlesInternals.utils||{};g.__tsParticlesInternals.canvas=g.__tsParticlesInternals.canvas||{};g.__tsParticlesInternals.canvas=g.__tsParticlesInternals.canvas||{};g.__tsParticlesInternals.canvas.utils=g.__tsParticlesInternals.canvas.utils||{};g.__tsParticlesInternals.path=g.__tsParticlesInternals.path||{};g.__tsParticlesInternals.path=g.__tsParticlesInternals.path||{};g.__tsParticlesInternals.path.utils=g.__tsParticlesInternals.path.utils||{};var __tsProxyFactory=typeof Proxy!=="undefined"?function(obj){return new Proxy(obj,{get:function(target,key){if(!(key in target)){target[key]={};}return target[key];}});}:function(obj){return obj;};g.__tsParticlesInternals.bundles=__tsProxyFactory(g.__tsParticlesInternals.bundles);g.__tsParticlesInternals.effects=__tsProxyFactory(g.__tsParticlesInternals.effects);g.__tsParticlesInternals.interactions=__tsProxyFactory(g.__tsParticlesInternals.interactions);g.__tsParticlesInternals.palettes=__tsProxyFactory(g.__tsParticlesInternals.palettes);g.__tsParticlesInternals.paths=__tsProxyFactory(g.__tsParticlesInternals.paths);g.__tsParticlesInternals.plugins=__tsProxyFactory(g.__tsParticlesInternals.plugins);g.__tsParticlesInternals.plugins.emittersShapes=__tsProxyFactory(g.__tsParticlesInternals.plugins.emittersShapes);g.__tsParticlesInternals.presets=__tsProxyFactory(g.__tsParticlesInternals.presets);g.__tsParticlesInternals.shapes=__tsProxyFactory(g.__tsParticlesInternals.shapes);g.__tsParticlesInternals.updaters=__tsProxyFactory(g.__tsParticlesInternals.updaters);g.__tsParticlesInternals.utils=__tsProxyFactory(g.__tsParticlesInternals.utils);g.__tsParticlesInternals.canvas=__tsProxyFactory(g.__tsParticlesInternals.canvas);g.__tsParticlesInternals.path=__tsProxyFactory(g.__tsParticlesInternals.path);g.tsparticlesInternalExports=g.tsparticlesInternalExports||{};})(typeof globalThis!=="undefined"?globalThis:typeof window!=="undefined"?window:this);
2
- /* tsParticles v4.0.0-beta.16 */
2
+ /* tsParticles v4.0.0-beta.17 */
3
3
  (function (global, factory) {
4
4
  typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
5
5
  typeof define === 'function' && define.amd ? define(['exports'], factory) :
@@ -1081,7 +1081,7 @@
1081
1081
  }
1082
1082
  }
1083
1083
  else {
1084
- const existingCanvases = domContainer.getElementsByTagName(canvasTag), foundCanvas = existingCanvases[canvasFirstIndex];
1084
+ const existingCanvases = domContainer.getElementsByTagName(canvasTag), foundCanvas = existingCanvases.item(canvasFirstIndex);
1085
1085
  if (foundCanvas) {
1086
1086
  canvasEl = foundCanvas;
1087
1087
  canvasEl.dataset[generatedAttribute] = generatedFalse;
@@ -1118,7 +1118,7 @@
1118
1118
  return this._domArray;
1119
1119
  }
1120
1120
  get version() {
1121
- return "4.0.0-beta.16";
1121
+ return "4.0.0-beta.17";
1122
1122
  }
1123
1123
  addEventListener(type, listener) {
1124
1124
  this._eventDispatcher.addEventListener(type, listener);
@@ -1149,7 +1149,11 @@
1149
1149
  }
1150
1150
  async load(params) {
1151
1151
  await this.init();
1152
- const { Container } = await Promise.resolve().then(function () { return Container$1; }), id = params.id ?? params.element?.id ?? `tsparticles${Math.floor(getRandom() * loadRandomFactor).toString()}`, { index, url } = params, options = url ? await getDataFromUrl({ fallback: params.options, url, index }) : params.options, currentOptions = itemFromSingleOrMultiple(options, index), { items } = this, oldIndex = items.findIndex(v => v.id.description === id), newItem = new Container({
1152
+ let domSourceElement;
1153
+ if (typeof HTMLElement !== "undefined" && params.element instanceof HTMLElement) {
1154
+ domSourceElement = params.element;
1155
+ }
1156
+ const { Container } = await Promise.resolve().then(function () { return Container$1; }), id = params.id ?? domSourceElement?.id ?? `tsparticles${Math.floor(getRandom() * loadRandomFactor).toString()}`, { index, url } = params, options = url ? await getDataFromUrl({ fallback: params.options, url, index }) : params.options, currentOptions = itemFromSingleOrMultiple(options, index), { items } = this, oldIndex = items.findIndex(v => v.id.description === id), newItem = new Container({
1153
1157
  dispatchCallback: (eventType, args) => {
1154
1158
  this.dispatchEvent(eventType, args);
1155
1159
  },
@@ -1176,8 +1180,10 @@
1176
1180
  else {
1177
1181
  items.push(newItem);
1178
1182
  }
1179
- const domContainer = getDomContainer(id, params.element), canvasEl = getCanvasFromContainer(domContainer);
1180
- newItem.canvas.loadCanvas(canvasEl);
1183
+ const sourceCanvas = typeof OffscreenCanvas !== "undefined" && params.element instanceof OffscreenCanvas
1184
+ ? params.element
1185
+ : getCanvasFromContainer(getDomContainer(id, domSourceElement));
1186
+ newItem.canvas.loadCanvas(sourceCanvas);
1181
1187
  await newItem.start();
1182
1188
  return newItem;
1183
1189
  }
@@ -3034,7 +3040,7 @@
3034
3040
  }
3035
3041
 
3036
3042
  async function loadCircleShape(engine) {
3037
- engine.checkVersion("4.0.0-beta.16");
3043
+ engine.checkVersion("4.0.0-beta.17");
3038
3044
  await engine.pluginManager.register(e => {
3039
3045
  e.pluginManager.addShape(["circle"], () => {
3040
3046
  return Promise.resolve(new CircleDrawer());
@@ -3082,7 +3088,7 @@
3082
3088
  }
3083
3089
 
3084
3090
  async function loadHexColorPlugin(engine) {
3085
- engine.checkVersion("4.0.0-beta.16");
3091
+ engine.checkVersion("4.0.0-beta.17");
3086
3092
  await engine.pluginManager.register(e => {
3087
3093
  e.pluginManager.addColorManager("hex", new HexColorManager());
3088
3094
  });
@@ -3135,7 +3141,7 @@
3135
3141
  }
3136
3142
 
3137
3143
  async function loadHslColorPlugin(engine) {
3138
- engine.checkVersion("4.0.0-beta.16");
3144
+ engine.checkVersion("4.0.0-beta.17");
3139
3145
  await engine.pluginManager.register(e => {
3140
3146
  e.pluginManager.addColorManager("hsl", new HslColorManager());
3141
3147
  });
@@ -3159,7 +3165,7 @@
3159
3165
  }
3160
3166
 
3161
3167
  async function loadMovePlugin(engine) {
3162
- engine.checkVersion("4.0.0-beta.16");
3168
+ engine.checkVersion("4.0.0-beta.17");
3163
3169
  await engine.pluginManager.register(e => {
3164
3170
  const moveEngine = e, movePluginManager = moveEngine.pluginManager;
3165
3171
  movePluginManager.initializers.pathGenerators ??= new Map();
@@ -3220,7 +3226,7 @@
3220
3226
  }
3221
3227
 
3222
3228
  async function loadOpacityUpdater(engine) {
3223
- engine.checkVersion("4.0.0-beta.16");
3229
+ engine.checkVersion("4.0.0-beta.17");
3224
3230
  await engine.pluginManager.register(e => {
3225
3231
  e.pluginManager.addParticleUpdater("opacity", container => {
3226
3232
  return Promise.resolve(new OpacityUpdater(container));
@@ -3572,7 +3578,7 @@
3572
3578
  }
3573
3579
 
3574
3580
  async function loadOutModesUpdater(engine) {
3575
- engine.checkVersion("4.0.0-beta.16");
3581
+ engine.checkVersion("4.0.0-beta.17");
3576
3582
  await engine.pluginManager.register(e => {
3577
3583
  e.pluginManager.addParticleUpdater("outModes", container => {
3578
3584
  return Promise.resolve(new OutOfCanvasUpdater(container));
@@ -3643,7 +3649,7 @@
3643
3649
  }
3644
3650
 
3645
3651
  async function loadPaintUpdater(engine) {
3646
- engine.checkVersion("4.0.0-beta.16");
3652
+ engine.checkVersion("4.0.0-beta.17");
3647
3653
  await engine.pluginManager.register(e => {
3648
3654
  e.pluginManager.addParticleUpdater("paint", container => {
3649
3655
  return Promise.resolve(new PaintUpdater(e.pluginManager, container));
@@ -3698,7 +3704,7 @@
3698
3704
  }
3699
3705
 
3700
3706
  async function loadRgbColorPlugin(engine) {
3701
- engine.checkVersion("4.0.0-beta.16");
3707
+ engine.checkVersion("4.0.0-beta.17");
3702
3708
  await engine.pluginManager.register(e => {
3703
3709
  e.pluginManager.addColorManager("rgb", new RgbColorManager());
3704
3710
  });
@@ -3741,7 +3747,7 @@
3741
3747
  }
3742
3748
 
3743
3749
  async function loadSizeUpdater(engine) {
3744
- engine.checkVersion("4.0.0-beta.16");
3750
+ engine.checkVersion("4.0.0-beta.17");
3745
3751
  await engine.pluginManager.register(e => {
3746
3752
  e.pluginManager.addParticleUpdater("size", container => {
3747
3753
  return Promise.resolve(new SizeUpdater(container));
@@ -3750,7 +3756,7 @@
3750
3756
  }
3751
3757
 
3752
3758
  async function loadBasic(engine) {
3753
- engine.checkVersion("4.0.0-beta.16");
3759
+ engine.checkVersion("4.0.0-beta.17");
3754
3760
  await engine.pluginManager.register(async (e) => {
3755
3761
  await Promise.all([
3756
3762
  loadHexColorPlugin(e),
@@ -3772,7 +3778,7 @@
3772
3778
  easingsFunctions.set(exports.EasingType.easeInOutQuad, value => (value < 0.5 ? 2 * value ** 2 : 1 - (-2 * value + 2) ** 2 / 2));
3773
3779
 
3774
3780
  async function loadEasingQuadPlugin(engine) {
3775
- engine.checkVersion("4.0.0-beta.16");
3781
+ engine.checkVersion("4.0.0-beta.17");
3776
3782
  await engine.pluginManager.register(e => {
3777
3783
  for (const [easing, easingFn] of easingsFunctions) {
3778
3784
  e.pluginManager.addEasing(easing, easingFn);
@@ -3865,19 +3871,7 @@
3865
3871
  particle.emojiDataKey = cacheKey;
3866
3872
  return;
3867
3873
  }
3868
- const padding = emojiOptions.padding * double, maxSize = getRangeMax(particle.size.value), fullSize = maxSize + padding, canvasSize = fullSize * double;
3869
- let cacheCanvas, context;
3870
- if (typeof OffscreenCanvas === "undefined") {
3871
- const canvas = safeDocument().createElement("canvas");
3872
- canvas.width = canvasSize;
3873
- canvas.height = canvasSize;
3874
- context = canvas.getContext("2d", container.canvas.render.settings);
3875
- cacheCanvas = canvas;
3876
- }
3877
- else {
3878
- cacheCanvas = new OffscreenCanvas(canvasSize, canvasSize);
3879
- context = cacheCanvas.getContext("2d", container.canvas.render.settings);
3880
- }
3874
+ const padding = emojiOptions.padding * double, maxSize = getRangeMax(particle.size.value), fullSize = maxSize + padding, canvasSize = fullSize * double, cacheCanvas = new OffscreenCanvas(canvasSize, canvasSize), context = cacheCanvas.getContext("2d", container.canvas.render.settings);
3881
3875
  if (!context) {
3882
3876
  return;
3883
3877
  }
@@ -3892,7 +3886,7 @@
3892
3886
  }
3893
3887
 
3894
3888
  async function loadEmojiShape(engine) {
3895
- engine.checkVersion("4.0.0-beta.16");
3889
+ engine.checkVersion("4.0.0-beta.17");
3896
3890
  await engine.pluginManager.register(e => {
3897
3891
  e.pluginManager.addShape(validTypes, () => Promise.resolve(new EmojiDrawer()));
3898
3892
  });
@@ -4159,7 +4153,7 @@
4159
4153
  }
4160
4154
 
4161
4155
  async function loadInteractivityPlugin(engine) {
4162
- engine.checkVersion("4.0.0-beta.16");
4156
+ engine.checkVersion("4.0.0-beta.17");
4163
4157
  await engine.pluginManager.register(e => {
4164
4158
  const interactivityEngine = e, interactivityPluginManager = interactivityEngine.pluginManager;
4165
4159
  interactivityPluginManager.addPlugin(new InteractivityPlugin(interactivityPluginManager));
@@ -4193,7 +4187,7 @@
4193
4187
  }
4194
4188
 
4195
4189
  const minFactor = 1, minRadius$4 = 0, updateVector = Vector.origin;
4196
- function processAttract(pluginManager, container, position, attractRadius, area, queryCb) {
4190
+ function processAttract(pluginManager, container, position, attractRadius, area, queryCb, onAttractParticle) {
4197
4191
  const attractOptions = container.actualOptions.interactivity?.modes.attract;
4198
4192
  if (!attractOptions) {
4199
4193
  return;
@@ -4203,10 +4197,11 @@
4203
4197
  const { dx, dy, distance } = getDistances(particle.position, position), velocity = attractOptions.speed * attractOptions.factor, attractFactor = clamp(pluginManager.getEasing(attractOptions.easing)(identity$3 - distance / attractRadius) * velocity, minFactor, attractOptions.maxSpeed);
4204
4198
  updateVector.x = !distance ? velocity : (dx / distance) * attractFactor;
4205
4199
  updateVector.y = !distance ? velocity : (dy / distance) * attractFactor;
4200
+ onAttractParticle?.(particle);
4206
4201
  particle.position.subFrom(updateVector);
4207
4202
  }
4208
4203
  }
4209
- function clickAttract(pluginManager, container, interactivityData, enabledCb) {
4204
+ function clickAttract(pluginManager, container, interactivityData, enabledCb, onAttractParticle) {
4210
4205
  container.attract ??= { particles: [] };
4211
4206
  const { attract } = container;
4212
4207
  if (!attract.finish) {
@@ -4221,18 +4216,18 @@
4221
4216
  if (!attractRadius || attractRadius < minRadius$4 || !mousePos) {
4222
4217
  return;
4223
4218
  }
4224
- processAttract(pluginManager, container, mousePos, attractRadius, new Circle(mousePos.x, mousePos.y, attractRadius), (p) => enabledCb(p));
4219
+ processAttract(pluginManager, container, mousePos, attractRadius, new Circle(mousePos.x, mousePos.y, attractRadius), (p) => enabledCb(p), onAttractParticle);
4225
4220
  }
4226
4221
  else if (attract.clicking === false) {
4227
4222
  attract.particles = [];
4228
4223
  }
4229
4224
  }
4230
- function hoverAttract(pluginManager, container, interactivityData, enabledCb) {
4225
+ function hoverAttract(pluginManager, container, interactivityData, enabledCb, onAttractParticle) {
4231
4226
  const mousePos = interactivityData.mouse.position, attractRadius = container.retina.attractModeDistance;
4232
4227
  if (!attractRadius || attractRadius < minRadius$4 || !mousePos) {
4233
4228
  return;
4234
4229
  }
4235
- processAttract(pluginManager, container, mousePos, attractRadius, new Circle(mousePos.x, mousePos.y, attractRadius), (p) => enabledCb(p));
4230
+ processAttract(pluginManager, container, mousePos, attractRadius, new Circle(mousePos.x, mousePos.y, attractRadius), (p) => enabledCb(p), onAttractParticle);
4236
4231
  }
4237
4232
 
4238
4233
  let Attract$1 = class Attract {
@@ -4241,6 +4236,7 @@
4241
4236
  easing;
4242
4237
  factor;
4243
4238
  maxSpeed;
4239
+ restore;
4244
4240
  speed;
4245
4241
  constructor() {
4246
4242
  this.distance = 200;
@@ -4249,6 +4245,12 @@
4249
4245
  this.factor = 1;
4250
4246
  this.maxSpeed = 50;
4251
4247
  this.speed = 1;
4248
+ this.restore = {
4249
+ enable: false,
4250
+ delay: 0,
4251
+ speed: 0.08,
4252
+ follow: true,
4253
+ };
4252
4254
  }
4253
4255
  load(data) {
4254
4256
  if (isNull(data)) {
@@ -4272,18 +4274,28 @@
4272
4274
  if (data.speed !== undefined) {
4273
4275
  this.speed = data.speed;
4274
4276
  }
4277
+ if (data.restore !== undefined) {
4278
+ this.restore.enable = data.restore.enable ?? this.restore.enable;
4279
+ this.restore.delay = data.restore.delay ?? this.restore.delay;
4280
+ this.restore.speed = data.restore.speed ?? this.restore.speed;
4281
+ this.restore.follow = data.restore.follow ?? this.restore.follow;
4282
+ }
4275
4283
  }
4276
4284
  };
4277
4285
 
4278
- const attractMode = "attract";
4286
+ const attractMode = "attract", minVelocityLengthSq = 0, minRestoreSpeed$1 = 0.001, maxRestoreSpeed$1 = 1, restoreEpsilon$1 = 0.5;
4279
4287
  let Attractor$1 = class Attractor extends ExternalInteractorBase {
4280
4288
  handleClickMode;
4289
+ _interactedThisFrame;
4281
4290
  _maxDistance;
4282
4291
  _pluginManager;
4292
+ _restoreData;
4283
4293
  constructor(pluginManager, container) {
4284
4294
  super(container);
4285
4295
  this._pluginManager = pluginManager;
4286
4296
  this._maxDistance = 0;
4297
+ this._interactedThisFrame = new Set();
4298
+ this._restoreData = new Map();
4287
4299
  container.attract ??= { particles: [] };
4288
4300
  this.handleClickMode = (mode, interactivityData) => {
4289
4301
  const options = this.container.actualOptions, attract = options.interactivity?.modes.attract;
@@ -4324,17 +4336,23 @@
4324
4336
  container.retina.attractModeDistance = attract.distance * container.retina.pixelRatio;
4325
4337
  }
4326
4338
  interact(interactivityData) {
4339
+ this._interactedThisFrame.clear();
4327
4340
  const container = this.container, options = container.actualOptions, mouseMoveStatus = interactivityData.status === mouseMoveEvent, events = options.interactivity?.events;
4328
4341
  if (!events) {
4329
4342
  return;
4330
4343
  }
4331
4344
  const { enable: hoverEnabled, mode: hoverMode } = events.onHover, { enable: clickEnabled, mode: clickMode } = events.onClick;
4332
4345
  if (mouseMoveStatus && hoverEnabled && isInArray(attractMode, hoverMode)) {
4333
- hoverAttract(this._pluginManager, this.container, interactivityData, p => this.isEnabled(interactivityData, p));
4346
+ hoverAttract(this._pluginManager, this.container, interactivityData, p => this.isEnabled(interactivityData, p), p => {
4347
+ this._trackInteractedParticle(p);
4348
+ });
4334
4349
  }
4335
4350
  else if (clickEnabled && isInArray(attractMode, clickMode)) {
4336
- clickAttract(this._pluginManager, this.container, interactivityData, p => this.isEnabled(interactivityData, p));
4351
+ clickAttract(this._pluginManager, this.container, interactivityData, p => this.isEnabled(interactivityData, p), p => {
4352
+ this._trackInteractedParticle(p);
4353
+ });
4337
4354
  }
4355
+ this._restoreParticles();
4338
4356
  }
4339
4357
  isEnabled(interactivityData, particle) {
4340
4358
  const container = this.container, options = container.actualOptions, mouse = interactivityData.mouse, events = (particle?.interactivity ?? options.interactivity)?.events;
@@ -4352,10 +4370,67 @@
4352
4370
  }
4353
4371
  reset() {
4354
4372
  }
4373
+ _restoreParticles() {
4374
+ const restore = this.container.actualOptions.interactivity?.modes.attract?.restore;
4375
+ if (!restore?.enable || !this._restoreData.size) {
4376
+ return;
4377
+ }
4378
+ const now = Date.now(), restoreDelay = restore.delay * millisecondsToSeconds, restoreSpeed = Math.max(minRestoreSpeed$1, Math.min(maxRestoreSpeed$1, restore.speed));
4379
+ for (const [particle, restoreData] of this._restoreData) {
4380
+ if (this._interactedThisFrame.has(particle)) {
4381
+ continue;
4382
+ }
4383
+ if (particle.destroyed) {
4384
+ this._restoreData.delete(particle);
4385
+ continue;
4386
+ }
4387
+ const target = restoreData.target;
4388
+ if (now - restoreData.lastInteractionTime < restoreDelay) {
4389
+ continue;
4390
+ }
4391
+ let dx = target.x - particle.position.x, dy = target.y - particle.position.y, dz = target.z - particle.position.z;
4392
+ if (restore.follow && particle.options.move.enable) {
4393
+ const { x: vx, y: vy, z: vz } = particle.velocity, velocityLengthSq = vx * vx + vy * vy + vz * vz;
4394
+ if (velocityLengthSq > minVelocityLengthSq) {
4395
+ const parallelScale = (dx * vx + dy * vy + dz * vz) / velocityLengthSq;
4396
+ dx -= vx * parallelScale;
4397
+ dy -= vy * parallelScale;
4398
+ dz -= vz * parallelScale;
4399
+ }
4400
+ }
4401
+ particle.position.x += dx * restoreSpeed;
4402
+ particle.position.y += dy * restoreSpeed;
4403
+ particle.position.z += dz * restoreSpeed;
4404
+ if (Math.abs(dx) <= restoreEpsilon$1 && Math.abs(dy) <= restoreEpsilon$1) {
4405
+ particle.position.x = target.x;
4406
+ particle.position.y = target.y;
4407
+ particle.position.z = target.z;
4408
+ this._restoreData.delete(particle);
4409
+ continue;
4410
+ }
4411
+ }
4412
+ }
4413
+ _trackInteractedParticle(particle) {
4414
+ this._interactedThisFrame.add(particle);
4415
+ const restore = this.container.actualOptions.interactivity?.modes.attract?.restore;
4416
+ if (!restore?.enable) {
4417
+ return;
4418
+ }
4419
+ const now = Date.now();
4420
+ let restoreData = this._restoreData.get(particle);
4421
+ if (!restoreData) {
4422
+ restoreData = {
4423
+ target: particle.position.copy(),
4424
+ lastInteractionTime: now,
4425
+ };
4426
+ this._restoreData.set(particle, restoreData);
4427
+ }
4428
+ restoreData.lastInteractionTime = now;
4429
+ }
4355
4430
  };
4356
4431
 
4357
4432
  async function loadExternalAttractInteraction(engine) {
4358
- engine.checkVersion("4.0.0-beta.16");
4433
+ engine.checkVersion("4.0.0-beta.17");
4359
4434
  await engine.pluginManager.register((e) => {
4360
4435
  ensureInteractivityPluginLoaded(e);
4361
4436
  e.pluginManager.addInteractor?.("externalAttract", container => {
@@ -4552,7 +4627,7 @@
4552
4627
  }
4553
4628
 
4554
4629
  async function loadExternalBounceInteraction(engine) {
4555
- engine.checkVersion("4.0.0-beta.16");
4630
+ engine.checkVersion("4.0.0-beta.17");
4556
4631
  await engine.pluginManager.register((e) => {
4557
4632
  ensureInteractivityPluginLoaded(e);
4558
4633
  e.pluginManager.addInteractor?.("externalBounce", container => {
@@ -4932,7 +5007,7 @@
4932
5007
  }
4933
5008
 
4934
5009
  async function loadExternalBubbleInteraction(engine) {
4935
- engine.checkVersion("4.0.0-beta.16");
5010
+ engine.checkVersion("4.0.0-beta.17");
4936
5011
  await engine.pluginManager.register((e) => {
4937
5012
  ensureInteractivityPluginLoaded(e);
4938
5013
  e.pluginManager.addInteractor?.("externalBubble", container => {
@@ -5077,7 +5152,7 @@
5077
5152
  }
5078
5153
 
5079
5154
  async function loadExternalConnectInteraction(engine) {
5080
- engine.checkVersion("4.0.0-beta.16");
5155
+ engine.checkVersion("4.0.0-beta.17");
5081
5156
  await engine.pluginManager.register((e) => {
5082
5157
  ensureInteractivityPluginLoaded(e);
5083
5158
  e.pluginManager.addInteractor?.("externalConnect", container => {
@@ -5189,7 +5264,7 @@
5189
5264
  }
5190
5265
 
5191
5266
  async function loadExternalDestroyInteraction(engine) {
5192
- engine.checkVersion("4.0.0-beta.16");
5267
+ engine.checkVersion("4.0.0-beta.17");
5193
5268
  await engine.pluginManager.register((e) => {
5194
5269
  ensureInteractivityPluginLoaded(e);
5195
5270
  e.pluginManager.addInteractor?.("externalDestroy", async (container) => {
@@ -5333,7 +5408,7 @@
5333
5408
  }
5334
5409
 
5335
5410
  async function loadExternalGrabInteraction(engine) {
5336
- engine.checkVersion("4.0.0-beta.16");
5411
+ engine.checkVersion("4.0.0-beta.17");
5337
5412
  await engine.pluginManager.register((e) => {
5338
5413
  ensureInteractivityPluginLoaded(e);
5339
5414
  e.pluginManager.addInteractor?.("externalGrab", container => {
@@ -5414,7 +5489,7 @@
5414
5489
  }
5415
5490
 
5416
5491
  async function loadExternalParallaxInteraction(engine) {
5417
- engine.checkVersion("4.0.0-beta.16");
5492
+ engine.checkVersion("4.0.0-beta.17");
5418
5493
  await engine.pluginManager.register((e) => {
5419
5494
  ensureInteractivityPluginLoaded(e);
5420
5495
  e.pluginManager.addInteractor?.("externalParallax", container => {
@@ -5457,7 +5532,7 @@
5457
5532
  }
5458
5533
 
5459
5534
  async function loadExternalPauseInteraction(engine) {
5460
- engine.checkVersion("4.0.0-beta.16");
5535
+ engine.checkVersion("4.0.0-beta.17");
5461
5536
  await engine.pluginManager.register((e) => {
5462
5537
  ensureInteractivityPluginLoaded(e);
5463
5538
  e.pluginManager.addInteractor?.("externalPause", container => {
@@ -5542,7 +5617,7 @@
5542
5617
  }
5543
5618
 
5544
5619
  async function loadExternalPushInteraction(engine) {
5545
- engine.checkVersion("4.0.0-beta.16");
5620
+ engine.checkVersion("4.0.0-beta.17");
5546
5621
  await engine.pluginManager.register((e) => {
5547
5622
  ensureInteractivityPluginLoaded(e);
5548
5623
  e.pluginManager.addInteractor?.("externalPush", container => {
@@ -5605,7 +5680,7 @@
5605
5680
  }
5606
5681
 
5607
5682
  async function loadExternalRemoveInteraction(engine) {
5608
- engine.checkVersion("4.0.0-beta.16");
5683
+ engine.checkVersion("4.0.0-beta.17");
5609
5684
  await engine.pluginManager.register((e) => {
5610
5685
  ensureInteractivityPluginLoaded(e);
5611
5686
  e.pluginManager.addInteractor?.("externalRemove", container => {
@@ -5620,6 +5695,7 @@
5620
5695
  easing;
5621
5696
  factor;
5622
5697
  maxSpeed;
5698
+ restore;
5623
5699
  speed;
5624
5700
  constructor() {
5625
5701
  this.distance = 200;
@@ -5628,6 +5704,12 @@
5628
5704
  this.speed = 1;
5629
5705
  this.maxSpeed = 50;
5630
5706
  this.easing = exports.EasingType.easeOutQuad;
5707
+ this.restore = {
5708
+ enable: false,
5709
+ delay: 0,
5710
+ speed: 0.08,
5711
+ follow: true,
5712
+ };
5631
5713
  }
5632
5714
  load(data) {
5633
5715
  if (isNull(data)) {
@@ -5651,6 +5733,12 @@
5651
5733
  if (data.maxSpeed !== undefined) {
5652
5734
  this.maxSpeed = data.maxSpeed;
5653
5735
  }
5736
+ if (data.restore !== undefined) {
5737
+ this.restore.enable = data.restore.enable ?? this.restore.enable;
5738
+ this.restore.delay = data.restore.delay ?? this.restore.delay;
5739
+ this.restore.speed = data.restore.speed ?? this.restore.speed;
5740
+ this.restore.follow = data.restore.follow ?? this.restore.follow;
5741
+ }
5654
5742
  }
5655
5743
  }
5656
5744
 
@@ -5686,19 +5774,23 @@
5686
5774
  }
5687
5775
  }
5688
5776
 
5689
- const repulseMode = "repulse", minDistance$2 = 0, repulseRadiusFactor = 6, repulseRadiusPower = 3, squarePower = 2, minRadius$1 = 0, minSpeed = 0, easingOffset = 1;
5777
+ const repulseMode = "repulse", minDistance$2 = 0, repulseRadiusFactor = 6, repulseRadiusPower = 3, squarePower = 2, minRadius$1 = 0, minSpeed = 0, easingOffset = 1, minRestoreSpeed = 0.001, maxRestoreSpeed = 1, restoreEpsilon = 0.5;
5690
5778
  class Repulser extends ExternalInteractorBase {
5691
5779
  handleClickMode;
5692
5780
  _clickVec;
5781
+ _interactedThisFrame;
5693
5782
  _maxDistance;
5694
5783
  _normVec;
5695
5784
  _pluginManager;
5785
+ _restoreData;
5696
5786
  constructor(pluginManager, container) {
5697
5787
  super(container);
5698
5788
  this._pluginManager = pluginManager;
5699
5789
  this._maxDistance = 0;
5700
5790
  this._normVec = Vector.origin;
5791
+ this._interactedThisFrame = new Set();
5701
5792
  this._clickVec = Vector.origin;
5793
+ this._restoreData = new Map();
5702
5794
  container.repulse ??= { particles: [] };
5703
5795
  this.handleClickMode = (mode, interactivityData) => {
5704
5796
  const options = this.container.actualOptions, repulseOpts = options.interactivity?.modes.repulse;
@@ -5739,6 +5831,7 @@
5739
5831
  container.retina.repulseModeDistance = repulse.distance * container.retina.pixelRatio;
5740
5832
  }
5741
5833
  interact(interactivityData) {
5834
+ this._interactedThisFrame.clear();
5742
5835
  const container = this.container, options = container.actualOptions, mouseMoveStatus = interactivityData.status === mouseMoveEvent, events = options.interactivity?.events;
5743
5836
  if (!events) {
5744
5837
  return;
@@ -5755,6 +5848,7 @@
5755
5848
  this._singleSelectorRepulse(interactivityData, selector, div);
5756
5849
  });
5757
5850
  }
5851
+ this._restoreParticles();
5758
5852
  }
5759
5853
  isEnabled(interactivityData, particle) {
5760
5854
  const container = this.container, options = container.actualOptions, mouse = interactivityData.mouse, events = (particle?.interactivity ?? options.interactivity)?.events;
@@ -5802,6 +5896,7 @@
5802
5896
  for (const particle of query) {
5803
5897
  const { dx, dy, distance } = getDistances(mouseClickPos, particle.position), d = distance ** squarePower, velocity = repulseOptions.speed, force = (-repulseRadius * velocity) / d;
5804
5898
  if (d <= repulseRadius) {
5899
+ this._trackInteractedParticle(particle);
5805
5900
  repulse.particles.push(particle);
5806
5901
  this._clickVec.x = dx;
5807
5902
  this._clickVec.y = dy;
@@ -5834,9 +5929,46 @@
5834
5929
  const { dx, dy, distance } = getDistances(particle.position, position), repulseFactor = clamp(easingFunc(easingOffset - distance / repulseRadius) * velocity, minSpeed, maxSpeed);
5835
5930
  this._normVec.x = !distance ? velocity : (dx / distance) * repulseFactor;
5836
5931
  this._normVec.y = !distance ? velocity : (dy / distance) * repulseFactor;
5932
+ this._trackInteractedParticle(particle);
5837
5933
  particle.position.addTo(this._normVec);
5838
5934
  }
5839
5935
  };
5936
+ _restoreParticles() {
5937
+ const restore = this.container.actualOptions.interactivity?.modes.repulse?.restore;
5938
+ if (!restore?.enable || !this._restoreData.size) {
5939
+ return;
5940
+ }
5941
+ const now = Date.now(), restoreDelay = restore.delay * millisecondsToSeconds, restoreSpeed = Math.max(minRestoreSpeed, Math.min(maxRestoreSpeed, restore.speed));
5942
+ for (const [particle, restoreData] of this._restoreData) {
5943
+ if (this._interactedThisFrame.has(particle)) {
5944
+ continue;
5945
+ }
5946
+ if (particle.destroyed) {
5947
+ this._restoreData.delete(particle);
5948
+ continue;
5949
+ }
5950
+ const target = restoreData.target;
5951
+ if (now - restoreData.lastInteractionTime < restoreDelay) {
5952
+ continue;
5953
+ }
5954
+ if (restore.follow && particle.options.move.enable) {
5955
+ target.x += particle.velocity.x;
5956
+ target.y += particle.velocity.y;
5957
+ target.z += particle.velocity.z;
5958
+ }
5959
+ const dx = target.x - particle.position.x, dy = target.y - particle.position.y, dz = target.z - particle.position.z;
5960
+ particle.position.x += dx * restoreSpeed;
5961
+ particle.position.y += dy * restoreSpeed;
5962
+ particle.position.z += dz * restoreSpeed;
5963
+ if (Math.abs(dx) <= restoreEpsilon && Math.abs(dy) <= restoreEpsilon) {
5964
+ particle.position.x = target.x;
5965
+ particle.position.y = target.y;
5966
+ particle.position.z = target.z;
5967
+ this._restoreData.delete(particle);
5968
+ continue;
5969
+ }
5970
+ }
5971
+ }
5840
5972
  _singleSelectorRepulse = (interactivityData, selector, div) => {
5841
5973
  const container = this.container, repulse = container.actualOptions.interactivity?.modes.repulse;
5842
5974
  if (!repulse) {
@@ -5856,10 +5988,32 @@
5856
5988
  this._processRepulse(interactivityData, pos, repulseRadius, area, divRepulse);
5857
5989
  });
5858
5990
  };
5991
+ _trackInteractedParticle(particle) {
5992
+ this._interactedThisFrame.add(particle);
5993
+ const restore = this.container.actualOptions.interactivity?.modes.repulse?.restore;
5994
+ if (!restore?.enable) {
5995
+ return;
5996
+ }
5997
+ const now = Date.now();
5998
+ let restoreData = this._restoreData.get(particle);
5999
+ if (!restoreData) {
6000
+ restoreData = {
6001
+ target: particle.position.copy(),
6002
+ lastInteractionTime: now,
6003
+ };
6004
+ this._restoreData.set(particle, restoreData);
6005
+ }
6006
+ restoreData.lastInteractionTime = now;
6007
+ if (restore.follow && particle.options.move.enable) {
6008
+ restoreData.target.x += particle.velocity.x;
6009
+ restoreData.target.y += particle.velocity.y;
6010
+ restoreData.target.z += particle.velocity.z;
6011
+ }
6012
+ }
5859
6013
  }
5860
6014
 
5861
6015
  async function loadExternalRepulseInteraction(engine) {
5862
- engine.checkVersion("4.0.0-beta.16");
6016
+ engine.checkVersion("4.0.0-beta.17");
5863
6017
  await engine.pluginManager.register((e) => {
5864
6018
  ensureInteractivityPluginLoaded(e);
5865
6019
  const pluginManager = e.pluginManager;
@@ -5941,7 +6095,7 @@
5941
6095
  }
5942
6096
 
5943
6097
  async function loadExternalSlowInteraction(engine) {
5944
- engine.checkVersion("4.0.0-beta.16");
6098
+ engine.checkVersion("4.0.0-beta.17");
5945
6099
  await engine.pluginManager.register((e) => {
5946
6100
  ensureInteractivityPluginLoaded(e);
5947
6101
  e.pluginManager.addInteractor?.("externalSlow", container => {
@@ -6769,7 +6923,7 @@
6769
6923
  };
6770
6924
  }
6771
6925
  async function loadImageShape(engine) {
6772
- engine.checkVersion("4.0.0-beta.16");
6926
+ engine.checkVersion("4.0.0-beta.17");
6773
6927
  await engine.pluginManager.register(e => {
6774
6928
  addLoadImageToEngine(e);
6775
6929
  e.pluginManager.addPlugin(new ImagePreloaderPlugin(e));
@@ -6936,7 +7090,7 @@
6936
7090
  }
6937
7091
 
6938
7092
  async function loadLifeUpdater(engine) {
6939
- engine.checkVersion("4.0.0-beta.16");
7093
+ engine.checkVersion("4.0.0-beta.17");
6940
7094
  await engine.pluginManager.register(e => {
6941
7095
  e.pluginManager.addParticleUpdater("life", container => {
6942
7096
  return Promise.resolve(new LifeUpdater(container));
@@ -6962,7 +7116,7 @@
6962
7116
  }
6963
7117
 
6964
7118
  async function loadLineShape(engine) {
6965
- engine.checkVersion("4.0.0-beta.16");
7119
+ engine.checkVersion("4.0.0-beta.17");
6966
7120
  await engine.pluginManager.register(e => {
6967
7121
  e.pluginManager.addShape(["line"], () => Promise.resolve(new LineDrawer()));
6968
7122
  });
@@ -7055,7 +7209,7 @@
7055
7209
  }
7056
7210
 
7057
7211
  async function loadParticlesAttractInteraction(engine) {
7058
- engine.checkVersion("4.0.0-beta.16");
7212
+ engine.checkVersion("4.0.0-beta.17");
7059
7213
  await engine.pluginManager.register((e) => {
7060
7214
  ensureInteractivityPluginLoaded(e);
7061
7215
  e.pluginManager.addInteractor?.("particlesAttract", container => {
@@ -7295,7 +7449,7 @@
7295
7449
  }
7296
7450
 
7297
7451
  async function loadParticlesCollisionsInteraction(engine) {
7298
- engine.checkVersion("4.0.0-beta.16");
7452
+ engine.checkVersion("4.0.0-beta.17");
7299
7453
  await engine.pluginManager.register((e) => {
7300
7454
  ensureInteractivityPluginLoaded(e);
7301
7455
  e.pluginManager.addPlugin(new OverlapPlugin());
@@ -7598,7 +7752,7 @@
7598
7752
  }
7599
7753
 
7600
7754
  async function loadParticlesLinksInteraction(engine) {
7601
- engine.checkVersion("4.0.0-beta.16");
7755
+ engine.checkVersion("4.0.0-beta.17");
7602
7756
  await engine.pluginManager.register((e) => {
7603
7757
  const pluginManager = e.pluginManager;
7604
7758
  ensureInteractivityPluginLoaded(e);
@@ -7688,19 +7842,19 @@
7688
7842
  }
7689
7843
 
7690
7844
  async function loadGenericPolygonShape(engine) {
7691
- engine.checkVersion("4.0.0-beta.16");
7845
+ engine.checkVersion("4.0.0-beta.17");
7692
7846
  await engine.pluginManager.register(e => {
7693
7847
  e.pluginManager.addShape(["polygon"], () => Promise.resolve(new PolygonDrawer()));
7694
7848
  });
7695
7849
  }
7696
7850
  async function loadTriangleShape(engine) {
7697
- engine.checkVersion("4.0.0-beta.16");
7851
+ engine.checkVersion("4.0.0-beta.17");
7698
7852
  await engine.pluginManager.register(e => {
7699
7853
  e.pluginManager.addShape(["triangle"], () => Promise.resolve(new TriangleDrawer()));
7700
7854
  });
7701
7855
  }
7702
7856
  async function loadPolygonShape(engine) {
7703
- engine.checkVersion("4.0.0-beta.16");
7857
+ engine.checkVersion("4.0.0-beta.17");
7704
7858
  await Promise.all([
7705
7859
  loadGenericPolygonShape(engine),
7706
7860
  loadTriangleShape(engine),
@@ -7833,7 +7987,7 @@
7833
7987
  }
7834
7988
 
7835
7989
  async function loadRotateUpdater(engine) {
7836
- engine.checkVersion("4.0.0-beta.16");
7990
+ engine.checkVersion("4.0.0-beta.17");
7837
7991
  await engine.pluginManager.register(e => {
7838
7992
  e.pluginManager.addParticleUpdater("rotate", container => {
7839
7993
  return Promise.resolve(new RotateUpdater(container));
@@ -7857,7 +8011,7 @@
7857
8011
  }
7858
8012
 
7859
8013
  async function loadSquareShape(engine) {
7860
- engine.checkVersion("4.0.0-beta.16");
8014
+ engine.checkVersion("4.0.0-beta.17");
7861
8015
  await engine.pluginManager.register(e => {
7862
8016
  e.pluginManager.addShape(["edge", "square"], () => Promise.resolve(new SquareDrawer()));
7863
8017
  });
@@ -7891,14 +8045,14 @@
7891
8045
  }
7892
8046
 
7893
8047
  async function loadStarShape(engine) {
7894
- engine.checkVersion("4.0.0-beta.16");
8048
+ engine.checkVersion("4.0.0-beta.17");
7895
8049
  await engine.pluginManager.register(e => {
7896
8050
  e.pluginManager.addShape(["star"], () => Promise.resolve(new StarDrawer()));
7897
8051
  });
7898
8052
  }
7899
8053
 
7900
8054
  async function loadSlim(engine) {
7901
- engine.checkVersion("4.0.0-beta.16");
8055
+ engine.checkVersion("4.0.0-beta.17");
7902
8056
  await engine.pluginManager.register(async (e) => {
7903
8057
  const loadInteractivityForSlim = async (e) => {
7904
8058
  await loadInteractivityPlugin(e);
@@ -8242,6 +8396,25 @@
8242
8396
  };
8243
8397
  }
8244
8398
 
8399
+ const transferredCanvases = new WeakMap(), getTransferredCanvas = (canvas) => {
8400
+ const transferredCanvas = transferredCanvases.get(canvas);
8401
+ if (transferredCanvas) {
8402
+ return transferredCanvas;
8403
+ }
8404
+ if (typeof canvas.transferControlToOffscreen !== "function") {
8405
+ throw new TypeError("OffscreenCanvas is required but not supported by this browser");
8406
+ }
8407
+ try {
8408
+ const offscreenCanvas = canvas.transferControlToOffscreen();
8409
+ transferredCanvases.set(canvas, offscreenCanvas);
8410
+ return offscreenCanvas;
8411
+ }
8412
+ catch {
8413
+ throw new TypeError("OffscreenCanvas transfer failed");
8414
+ }
8415
+ }, isHtmlCanvasElement = (canvas) => {
8416
+ return typeof HTMLCanvasElement !== "undefined" && canvas instanceof HTMLCanvasElement;
8417
+ };
8245
8418
  function setStyle(canvas, style, important = false) {
8246
8419
  if (!style) {
8247
8420
  return;
@@ -8272,8 +8445,9 @@
8272
8445
  }
8273
8446
  }
8274
8447
  class CanvasManager {
8275
- element;
8448
+ domElement;
8276
8449
  render;
8450
+ renderCanvas;
8277
8451
  size;
8278
8452
  zoom = defaultZoom;
8279
8453
  _container;
@@ -8308,9 +8482,10 @@
8308
8482
  destroy() {
8309
8483
  this.stop();
8310
8484
  if (this._generated) {
8311
- const element = this.element;
8485
+ const element = this.domElement;
8312
8486
  element?.remove();
8313
- this.element = undefined;
8487
+ this.domElement = undefined;
8488
+ this.renderCanvas = undefined;
8314
8489
  }
8315
8490
  else {
8316
8491
  this._resetOriginalStyle();
@@ -8343,16 +8518,17 @@
8343
8518
  this._initStyle();
8344
8519
  this.initBackground();
8345
8520
  this._safeMutationObserver(obs => {
8346
- if (!this.element || !(this.element instanceof Node)) {
8521
+ const element = this.domElement;
8522
+ if (!element || !(element instanceof Node)) {
8347
8523
  return;
8348
8524
  }
8349
- obs.observe(this.element, { attributes: true });
8525
+ obs.observe(element, { attributes: true });
8350
8526
  });
8351
8527
  this.initPlugins();
8352
8528
  this.render.init();
8353
8529
  }
8354
8530
  initBackground() {
8355
- const { _container } = this, options = _container.actualOptions, background = options.background, element = this.element;
8531
+ const { _container } = this, options = _container.actualOptions, background = options.background, element = this.domElement;
8356
8532
  if (!element) {
8357
8533
  return;
8358
8534
  }
@@ -8377,21 +8553,30 @@
8377
8553
  }
8378
8554
  }
8379
8555
  loadCanvas(canvas) {
8380
- if (this._generated && this.element) {
8381
- this.element.remove();
8556
+ if (this._generated && this.domElement) {
8557
+ this.domElement.remove();
8558
+ }
8559
+ const container = this._container, domCanvas = isHtmlCanvasElement(canvas) ? canvas : undefined;
8560
+ this.domElement = domCanvas;
8561
+ this._generated = domCanvas ? domCanvas.dataset[generatedAttribute] === "true" : false;
8562
+ this.renderCanvas = domCanvas ? getTransferredCanvas(domCanvas) : canvas;
8563
+ const domElement = this.domElement;
8564
+ if (domElement) {
8565
+ domElement.ariaHidden = "true";
8566
+ this._originalStyle = cloneStyle(domElement.style);
8567
+ }
8568
+ const standardSize = this._standardSize, renderCanvas = this.renderCanvas;
8569
+ if (domElement) {
8570
+ standardSize.height = domElement.offsetHeight;
8571
+ standardSize.width = domElement.offsetWidth;
8572
+ }
8573
+ else {
8574
+ standardSize.height = renderCanvas.height;
8575
+ standardSize.width = renderCanvas.width;
8382
8576
  }
8383
- const container = this._container;
8384
- this._generated =
8385
- generatedAttribute in canvas.dataset ? canvas.dataset[generatedAttribute] === "true" : this._generated;
8386
- this.element = canvas;
8387
- this.element.ariaHidden = "true";
8388
- this._originalStyle = cloneStyle(this.element.style);
8389
- const standardSize = this._standardSize;
8390
- standardSize.height = canvas.offsetHeight;
8391
- standardSize.width = canvas.offsetWidth;
8392
8577
  const pxRatio = this._container.retina.pixelRatio, retinaSize = this.size;
8393
- canvas.height = retinaSize.height = standardSize.height * pxRatio;
8394
- canvas.width = retinaSize.width = standardSize.width * pxRatio;
8578
+ renderCanvas.height = retinaSize.height = standardSize.height * pxRatio;
8579
+ renderCanvas.width = retinaSize.width = standardSize.width * pxRatio;
8395
8580
  const canSupportHdrQuery = safeMatchMedia("(color-gamut: p3)");
8396
8581
  this.render.setContextSettings({
8397
8582
  alpha: true,
@@ -8399,42 +8584,48 @@
8399
8584
  desynchronized: true,
8400
8585
  willReadFrequently: false,
8401
8586
  });
8402
- this.render.setContext(this.element.getContext("2d", this.render.settings));
8587
+ this.render.setContext(renderCanvas.getContext("2d", this.render.settings));
8403
8588
  this._safeMutationObserver(obs => {
8404
8589
  obs.disconnect();
8405
8590
  });
8406
8591
  container.retina.init();
8407
8592
  this.initBackground();
8408
8593
  this._safeMutationObserver(obs => {
8409
- if (!this.element || !(this.element instanceof Node)) {
8594
+ const element = this.domElement;
8595
+ if (!element || !(element instanceof Node)) {
8410
8596
  return;
8411
8597
  }
8412
- obs.observe(this.element, { attributes: true });
8598
+ obs.observe(element, { attributes: true });
8413
8599
  });
8414
8600
  }
8415
8601
  resize() {
8416
- if (!this.element) {
8602
+ const element = this.domElement;
8603
+ if (!element) {
8604
+ return false;
8605
+ }
8606
+ const container = this._container, renderCanvas = this.renderCanvas;
8607
+ if (renderCanvas === undefined) {
8417
8608
  return false;
8418
8609
  }
8419
- const container = this._container, currentSize = container.canvas._standardSize, newSize = {
8420
- width: this.element.offsetWidth,
8421
- height: this.element.offsetHeight,
8610
+ const currentSize = container.canvas._standardSize, newSize = {
8611
+ width: element.offsetWidth,
8612
+ height: element.offsetHeight,
8422
8613
  }, pxRatio = container.retina.pixelRatio, retinaSize = {
8423
8614
  width: newSize.width * pxRatio,
8424
8615
  height: newSize.height * pxRatio,
8425
8616
  };
8426
8617
  if (newSize.height === currentSize.height &&
8427
8618
  newSize.width === currentSize.width &&
8428
- retinaSize.height === this.element.height &&
8429
- retinaSize.width === this.element.width) {
8619
+ retinaSize.height === renderCanvas.height &&
8620
+ retinaSize.width === renderCanvas.width) {
8430
8621
  return false;
8431
8622
  }
8432
8623
  const oldSize = { ...currentSize };
8433
8624
  currentSize.height = newSize.height;
8434
8625
  currentSize.width = newSize.width;
8435
8626
  const canvasSize = this.size;
8436
- this.element.width = canvasSize.width = retinaSize.width;
8437
- this.element.height = canvasSize.height = retinaSize.height;
8627
+ renderCanvas.width = canvasSize.width = retinaSize.width;
8628
+ renderCanvas.height = canvasSize.height = retinaSize.height;
8438
8629
  if (this._container.started) {
8439
8630
  container.particles.setResizeFactor({
8440
8631
  width: currentSize.width / oldSize.width,
@@ -8444,7 +8635,7 @@
8444
8635
  return true;
8445
8636
  }
8446
8637
  setPointerEvents(type) {
8447
- const element = this.element;
8638
+ const element = this.domElement;
8448
8639
  if (!element) {
8449
8640
  return;
8450
8641
  }
@@ -8463,7 +8654,7 @@
8463
8654
  this.render.stop();
8464
8655
  }
8465
8656
  async windowResize() {
8466
- if (!this.element || !this.resize()) {
8657
+ if (!this.domElement || !this.resize()) {
8467
8658
  return;
8468
8659
  }
8469
8660
  const container = this._container, needsRefresh = container.updateActualOptions();
@@ -8479,7 +8670,7 @@
8479
8670
  }
8480
8671
  };
8481
8672
  _initStyle = () => {
8482
- const element = this.element, options = this._container.actualOptions;
8673
+ const element = this.domElement, options = this._container.actualOptions;
8483
8674
  if (!element) {
8484
8675
  return;
8485
8676
  }
@@ -8501,7 +8692,7 @@
8501
8692
  }
8502
8693
  };
8503
8694
  _repairStyle = () => {
8504
- const element = this.element;
8695
+ const element = this.domElement;
8505
8696
  if (!element) {
8506
8697
  return;
8507
8698
  }
@@ -8521,7 +8712,7 @@
8521
8712
  });
8522
8713
  };
8523
8714
  _resetOriginalStyle = () => {
8524
- const element = this.element, originalStyle = this._originalStyle;
8715
+ const element = this.domElement, originalStyle = this._originalStyle;
8525
8716
  if (!element || !originalStyle) {
8526
8717
  return;
8527
8718
  }
@@ -8534,7 +8725,7 @@
8534
8725
  callback(this._mutationObserver);
8535
8726
  };
8536
8727
  _setFullScreenStyle = () => {
8537
- const element = this.element;
8728
+ const element = this.domElement;
8538
8729
  if (!element) {
8539
8730
  return;
8540
8731
  }
@@ -8608,7 +8799,7 @@
8608
8799
  manageListener(globalThis, resizeEvent, handlers.resize, add);
8609
8800
  return;
8610
8801
  }
8611
- const canvasEl = container.canvas.element;
8802
+ const canvasEl = container.canvas.domElement;
8612
8803
  if (this._resizeObserver && !add) {
8613
8804
  if (canvasEl) {
8614
8805
  this._resizeObserver.unobserve(canvasEl);
@@ -8863,8 +9054,6 @@
8863
9054
  this._initPosition(position);
8864
9055
  this.initialVelocity = this._calculateVelocity();
8865
9056
  this.velocity = this.initialVelocity.copy();
8866
- const particles = container.particles;
8867
- particles.setLastZIndex(this.position.z);
8868
9057
  this.zIndexFactor = this.position.z / container.zLayers;
8869
9058
  this.sides = 24;
8870
9059
  let effectDrawer, shapeDrawer;
@@ -9014,7 +9203,7 @@
9014
9203
  return color;
9015
9204
  };
9016
9205
  _initPosition = position => {
9017
- const container = this._container, zIndexValue = getRangeValue(this.options.zIndex.value), initialPosition = this._calcPosition(position, clamp(zIndexValue, minZ, container.zLayers));
9206
+ const container = this._container, zIndexValue = Math.floor(getRangeValue(this.options.zIndex.value)), initialPosition = this._calcPosition(position, clamp(zIndexValue, minZ, container.zLayers));
9018
9207
  if (!initialPosition) {
9019
9208
  throw new Error("a valid position cannot be found for particle");
9020
9209
  }
@@ -9148,10 +9337,8 @@
9148
9337
  _container;
9149
9338
  _groupLimits;
9150
9339
  _limit;
9151
- _maxZIndex;
9152
- _minZIndex;
9153
- _needsSort;
9154
9340
  _nextId;
9341
+ _particleBuckets;
9155
9342
  _particleResetPlugins;
9156
9343
  _particleUpdatePlugins;
9157
9344
  _pluginManager;
@@ -9160,19 +9347,17 @@
9160
9347
  _postUpdatePlugins;
9161
9348
  _resizeFactor;
9162
9349
  _updatePlugins;
9163
- _zArray;
9350
+ _zBuckets;
9164
9351
  constructor(pluginManager, container) {
9165
9352
  this._pluginManager = pluginManager;
9166
9353
  this._container = container;
9167
9354
  this._nextId = 0;
9168
9355
  this._array = [];
9169
- this._zArray = [];
9170
9356
  this._pool = [];
9171
9357
  this._limit = 0;
9172
9358
  this._groupLimits = new Map();
9173
- this._needsSort = false;
9174
- this._minZIndex = 0;
9175
- this._maxZIndex = 0;
9359
+ this._particleBuckets = new Map();
9360
+ this._zBuckets = this._createBuckets(this._container.zLayers);
9176
9361
  this.grid = new SpatialHashGrid(spatialHashGridCellSize);
9177
9362
  this.checkParticlePositionPlugins = [];
9178
9363
  this._particleResetPlugins = [];
@@ -9214,7 +9399,7 @@
9214
9399
  return;
9215
9400
  }
9216
9401
  this._array.push(particle);
9217
- this._zArray.push(particle);
9402
+ this._insertParticleIntoBucket(particle);
9218
9403
  this._nextId++;
9219
9404
  this._container.dispatchEvent(exports.EventType.particleAdded, {
9220
9405
  particle,
@@ -9228,12 +9413,14 @@
9228
9413
  }
9229
9414
  clear() {
9230
9415
  this._array = [];
9231
- this._zArray = [];
9416
+ this._particleBuckets.clear();
9417
+ this._resetBuckets(this._container.zLayers);
9232
9418
  }
9233
9419
  destroy() {
9234
9420
  this._array = [];
9235
9421
  this._pool.length = 0;
9236
- this._zArray = [];
9422
+ this._particleBuckets.clear();
9423
+ this._zBuckets = [];
9237
9424
  this.checkParticlePositionPlugins = [];
9238
9425
  this._particleResetPlugins = [];
9239
9426
  this._particleUpdatePlugins = [];
@@ -9242,8 +9429,14 @@
9242
9429
  this._updatePlugins = [];
9243
9430
  }
9244
9431
  drawParticles(delta) {
9245
- for (const particle of this._zArray) {
9246
- particle.draw(delta);
9432
+ for (let i = this._zBuckets.length - one; i >= minIndex; i--) {
9433
+ const bucket = this._zBuckets[i];
9434
+ if (!bucket) {
9435
+ continue;
9436
+ }
9437
+ for (const particle of bucket) {
9438
+ particle.draw(delta);
9439
+ }
9247
9440
  }
9248
9441
  }
9249
9442
  filter(condition) {
@@ -9257,15 +9450,14 @@
9257
9450
  }
9258
9451
  async init() {
9259
9452
  const container = this._container, options = container.actualOptions;
9260
- this._minZIndex = 0;
9261
- this._maxZIndex = 0;
9262
- this._needsSort = false;
9263
9453
  this.checkParticlePositionPlugins = [];
9264
9454
  this._updatePlugins = [];
9265
9455
  this._particleUpdatePlugins = [];
9266
9456
  this._postUpdatePlugins = [];
9267
9457
  this._particleResetPlugins = [];
9268
9458
  this._postParticleUpdatePlugins = [];
9459
+ this._particleBuckets.clear();
9460
+ this._resetBuckets(container.zLayers);
9269
9461
  this.grid = new SpatialHashGrid(spatialHashGridCellSize * container.retina.pixelRatio);
9270
9462
  for (const plugin of container.plugins) {
9271
9463
  if (plugin.redrawInit) {
@@ -9366,79 +9558,25 @@
9366
9558
  }
9367
9559
  this._applyDensity(options.particles, pluginsCount);
9368
9560
  }
9369
- setLastZIndex(zIndex) {
9370
- this._needsSort ||= zIndex >= this._maxZIndex || (zIndex > this._minZIndex && zIndex < this._maxZIndex);
9371
- }
9372
9561
  setResizeFactor(factor) {
9373
9562
  this._resizeFactor = factor;
9374
9563
  }
9375
9564
  update(delta) {
9376
- const particlesToDelete = new Set();
9377
9565
  this.grid.clear();
9378
9566
  for (const plugin of this._updatePlugins) {
9379
9567
  plugin.update?.(delta);
9380
9568
  }
9381
- const resizeFactor = this._resizeFactor;
9382
- for (const particle of this._array) {
9383
- if (resizeFactor && !particle.ignoresResizeRatio) {
9384
- particle.position.x *= resizeFactor.width;
9385
- particle.position.y *= resizeFactor.height;
9386
- particle.initialPosition.x *= resizeFactor.width;
9387
- particle.initialPosition.y *= resizeFactor.height;
9388
- }
9389
- particle.ignoresResizeRatio = false;
9390
- for (const plugin of this._particleResetPlugins) {
9391
- plugin.particleReset?.(particle);
9392
- }
9393
- for (const plugin of this._particleUpdatePlugins) {
9394
- if (particle.destroyed) {
9395
- break;
9396
- }
9397
- plugin.particleUpdate?.(particle, delta);
9398
- }
9399
- if (particle.destroyed) {
9400
- particlesToDelete.add(particle);
9401
- continue;
9402
- }
9403
- this.grid.insert(particle);
9404
- }
9569
+ const particlesToDelete = this._updateParticlesPhase1(delta);
9405
9570
  for (const plugin of this._postUpdatePlugins) {
9406
9571
  plugin.postUpdate?.(delta);
9407
9572
  }
9408
- for (const particle of this._array) {
9409
- if (particle.destroyed) {
9410
- particlesToDelete.add(particle);
9411
- continue;
9412
- }
9413
- for (const updater of this._container.particleUpdaters) {
9414
- updater.update(particle, delta);
9415
- }
9416
- if (!particle.destroyed && !particle.spawning) {
9417
- for (const plugin of this._postParticleUpdatePlugins) {
9418
- plugin.postParticleUpdate?.(particle, delta);
9419
- }
9420
- }
9421
- else if (particle.destroyed) {
9422
- particlesToDelete.add(particle);
9423
- }
9424
- }
9573
+ this._updateParticlesPhase2(delta, particlesToDelete);
9425
9574
  if (particlesToDelete.size) {
9426
9575
  for (const particle of particlesToDelete) {
9427
9576
  this.remove(particle);
9428
9577
  }
9429
9578
  }
9430
9579
  delete this._resizeFactor;
9431
- if (this._needsSort) {
9432
- const zArray = this._zArray;
9433
- zArray.sort((a, b) => b.position.z - a.position.z || a.id - b.id);
9434
- const firstItem = zArray[minIndex], lastItem = zArray[zArray.length - lengthOffset];
9435
- if (!firstItem || !lastItem) {
9436
- return;
9437
- }
9438
- this._maxZIndex = firstItem.position.z;
9439
- this._minZIndex = lastItem.position.z;
9440
- this._needsSort = false;
9441
- }
9442
9580
  }
9443
9581
  _addToPool = (...particles) => {
9444
9582
  this._pool.push(...particles);
@@ -9468,13 +9606,52 @@
9468
9606
  this.removeQuantity(particlesCount - particlesNumber, group);
9469
9607
  }
9470
9608
  };
9609
+ _createBuckets = (zLayers) => {
9610
+ const bucketCount = Math.max(Math.floor(zLayers), one);
9611
+ return Array.from({ length: bucketCount }, () => []);
9612
+ };
9613
+ _getBucketIndex = (zIndex) => {
9614
+ const maxBucketIndex = this._zBuckets.length - one;
9615
+ if (maxBucketIndex <= minIndex) {
9616
+ return minIndex;
9617
+ }
9618
+ return Math.min(Math.max(Math.floor(zIndex), minIndex), maxBucketIndex);
9619
+ };
9620
+ _getParticleInsertIndex = (bucket, particleId) => {
9621
+ let start = minIndex, end = bucket.length;
9622
+ while (start < end) {
9623
+ const middle = Math.floor((start + end) / double), middleParticle = bucket[middle];
9624
+ if (!middleParticle) {
9625
+ end = middle;
9626
+ continue;
9627
+ }
9628
+ if (middleParticle.id < particleId) {
9629
+ start = middle + one;
9630
+ }
9631
+ else {
9632
+ end = middle;
9633
+ }
9634
+ }
9635
+ return start;
9636
+ };
9471
9637
  _initDensityFactor = densityOptions => {
9472
9638
  const container = this._container;
9473
- if (!container.canvas.element || !densityOptions.enable) {
9639
+ if (!densityOptions.enable) {
9474
9640
  return defaultDensityFactor;
9475
9641
  }
9476
- const canvas = container.canvas.element, pxRatio = container.retina.pixelRatio;
9477
- return (canvas.width * canvas.height) / (densityOptions.height * densityOptions.width * pxRatio ** squareExp$1);
9642
+ const canvasSize = container.canvas.size, pxRatio = container.retina.pixelRatio;
9643
+ if (!canvasSize.width || !canvasSize.height) {
9644
+ return defaultDensityFactor;
9645
+ }
9646
+ return ((canvasSize.width * canvasSize.height) / (densityOptions.height * densityOptions.width * pxRatio ** squareExp$1));
9647
+ };
9648
+ _insertParticleIntoBucket = (particle) => {
9649
+ const bucketIndex = this._getBucketIndex(particle.position.z), bucket = this._zBuckets[bucketIndex];
9650
+ if (!bucket) {
9651
+ return;
9652
+ }
9653
+ bucket.splice(this._getParticleInsertIndex(bucket, particle.id), empty, particle);
9654
+ this._particleBuckets.set(particle.id, bucketIndex);
9478
9655
  };
9479
9656
  _removeParticle = (index, group, override) => {
9480
9657
  const particle = this._array[index];
@@ -9484,9 +9661,8 @@
9484
9661
  if (particle.group !== group) {
9485
9662
  return false;
9486
9663
  }
9487
- const zIdx = this._zArray.indexOf(particle);
9488
9664
  this._array.splice(index, deleteCount);
9489
- this._zArray.splice(zIdx, deleteCount);
9665
+ this._removeParticleFromBucket(particle);
9490
9666
  particle.destroy(override);
9491
9667
  this._container.dispatchEvent(exports.EventType.particleRemoved, {
9492
9668
  particle,
@@ -9494,6 +9670,98 @@
9494
9670
  this._addToPool(particle);
9495
9671
  return true;
9496
9672
  };
9673
+ _removeParticleFromBucket = (particle) => {
9674
+ const bucketIndex = this._particleBuckets.get(particle.id) ?? this._getBucketIndex(particle.position.z), bucket = this._zBuckets[bucketIndex];
9675
+ if (!bucket) {
9676
+ this._particleBuckets.delete(particle.id);
9677
+ return;
9678
+ }
9679
+ const particleIndex = this._getParticleInsertIndex(bucket, particle.id);
9680
+ if (bucket[particleIndex]?.id !== particle.id) {
9681
+ this._particleBuckets.delete(particle.id);
9682
+ return;
9683
+ }
9684
+ bucket.splice(particleIndex, deleteCount);
9685
+ this._particleBuckets.delete(particle.id);
9686
+ };
9687
+ _resetBuckets = (zLayers) => {
9688
+ const bucketCount = Math.max(Math.floor(zLayers), one);
9689
+ if (this._zBuckets.length !== bucketCount) {
9690
+ this._zBuckets = this._createBuckets(bucketCount);
9691
+ return;
9692
+ }
9693
+ for (const bucket of this._zBuckets) {
9694
+ bucket.length = minIndex;
9695
+ }
9696
+ };
9697
+ _updateParticleBucket = (particle) => {
9698
+ const newBucketIndex = this._getBucketIndex(particle.position.z), currentBucketIndex = this._particleBuckets.get(particle.id);
9699
+ if (currentBucketIndex === undefined) {
9700
+ this._insertParticleIntoBucket(particle);
9701
+ return;
9702
+ }
9703
+ if (currentBucketIndex === newBucketIndex) {
9704
+ return;
9705
+ }
9706
+ const currentBucket = this._zBuckets[currentBucketIndex];
9707
+ if (currentBucket) {
9708
+ const particleIndex = this._getParticleInsertIndex(currentBucket, particle.id);
9709
+ if (currentBucket[particleIndex]?.id === particle.id) {
9710
+ currentBucket.splice(particleIndex, deleteCount);
9711
+ }
9712
+ }
9713
+ const newBucket = this._zBuckets[newBucketIndex];
9714
+ if (!newBucket) {
9715
+ this._particleBuckets.set(particle.id, newBucketIndex);
9716
+ return;
9717
+ }
9718
+ newBucket.splice(this._getParticleInsertIndex(newBucket, particle.id), empty, particle);
9719
+ this._particleBuckets.set(particle.id, newBucketIndex);
9720
+ };
9721
+ _updateParticlesPhase1 = (delta) => {
9722
+ const particlesToDelete = new Set(), resizeFactor = this._resizeFactor;
9723
+ for (const particle of this._array) {
9724
+ if (resizeFactor && !particle.ignoresResizeRatio) {
9725
+ particle.position.x *= resizeFactor.width;
9726
+ particle.position.y *= resizeFactor.height;
9727
+ particle.initialPosition.x *= resizeFactor.width;
9728
+ particle.initialPosition.y *= resizeFactor.height;
9729
+ }
9730
+ particle.ignoresResizeRatio = false;
9731
+ for (const plugin of this._particleResetPlugins) {
9732
+ plugin.particleReset?.(particle);
9733
+ }
9734
+ for (const plugin of this._particleUpdatePlugins) {
9735
+ if (particle.destroyed) {
9736
+ break;
9737
+ }
9738
+ plugin.particleUpdate?.(particle, delta);
9739
+ }
9740
+ if (particle.destroyed) {
9741
+ particlesToDelete.add(particle);
9742
+ continue;
9743
+ }
9744
+ this.grid.insert(particle);
9745
+ }
9746
+ return particlesToDelete;
9747
+ };
9748
+ _updateParticlesPhase2 = (delta, particlesToDelete) => {
9749
+ for (const particle of this._array) {
9750
+ if (particle.destroyed) {
9751
+ particlesToDelete.add(particle);
9752
+ continue;
9753
+ }
9754
+ for (const updater of this._container.particleUpdaters) {
9755
+ updater.update(particle, delta);
9756
+ }
9757
+ if (!particle.spawning) {
9758
+ for (const plugin of this._postParticleUpdatePlugins) {
9759
+ plugin.postParticleUpdate?.(particle, delta);
9760
+ }
9761
+ }
9762
+ this._updateParticleBucket(particle);
9763
+ }
9764
+ };
9497
9765
  }
9498
9766
 
9499
9767
  class Retina {
@@ -9509,9 +9777,8 @@
9509
9777
  const container = this.container, options = container.actualOptions;
9510
9778
  this.pixelRatio = options.detectRetina ? devicePixelRatio : defaultRatio$1;
9511
9779
  this.reduceFactor = defaultReduceFactor;
9512
- const ratio = this.pixelRatio, canvas = container.canvas;
9513
- if (canvas.element) {
9514
- const element = canvas.element;
9780
+ const ratio = this.pixelRatio, canvas = container.canvas, element = canvas.domElement;
9781
+ if (element) {
9515
9782
  canvas.size.width = element.offsetWidth * ratio;
9516
9783
  canvas.size.height = element.offsetHeight * ratio;
9517
9784
  }
@@ -10200,7 +10467,7 @@
10200
10467
  return;
10201
10468
  }
10202
10469
  const html = interactivityEl, canvas = container.canvas;
10203
- canvas.setPointerEvents(html === canvas.element ? "initial" : "none");
10470
+ canvas.setPointerEvents(html === canvas.domElement ? "initial" : "none");
10204
10471
  if (add && !(options.interactivity?.events.onHover.enable || options.interactivity?.events.onClick.enable)) {
10205
10472
  return;
10206
10473
  }
@@ -10227,7 +10494,7 @@
10227
10494
  manageListener(interactivityEl, touchCancelEvent, handlers.touchCancel, add);
10228
10495
  };
10229
10496
  _manageListeners = add => {
10230
- const handlers = this._handlers, container = this._container, interactionManager = this._interactionManager, options = container.actualOptions, detectType = options.interactivity?.detectsOn, canvasEl = container.canvas.element;
10497
+ const handlers = this._handlers, container = this._container, interactionManager = this._interactionManager, options = container.actualOptions, detectType = options.interactivity?.detectsOn, canvasEl = container.canvas.domElement;
10231
10498
  if (detectType === InteractivityDetect.window) {
10232
10499
  interactionManager.interactivityData.element = safeDocument();
10233
10500
  }
@@ -10274,7 +10541,7 @@
10274
10541
  mouse.clicking = false;
10275
10542
  };
10276
10543
  _mouseTouchMove = e => {
10277
- const container = this._container, interactionManager = this._interactionManager, options = container.actualOptions, interactivity = interactionManager.interactivityData, canvasEl = container.canvas.element;
10544
+ const container = this._container, interactionManager = this._interactionManager, options = container.actualOptions, interactivity = interactionManager.interactivityData, canvasEl = container.canvas.domElement;
10278
10545
  if (!interactivity.element) {
10279
10546
  return;
10280
10547
  }
@@ -10435,7 +10702,7 @@
10435
10702
  if (!lastTouch) {
10436
10703
  return;
10437
10704
  }
10438
- const element = container.canvas.element, canvasRect = element ? element.getBoundingClientRect() : undefined, pos = {
10705
+ const element = container.canvas.domElement, canvasRect = element ? element.getBoundingClientRect() : undefined, pos = {
10439
10706
  x: lastTouch.clientX - (canvasRect ? canvasRect.left : minCoordinate),
10440
10707
  y: lastTouch.clientY - (canvasRect ? canvasRect.top : minCoordinate),
10441
10708
  };