@tsparticles/interaction-external-bubble 4.2.1 → 4.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,12 @@
1
+ export class BubbleModifier {
2
+ div;
3
+ enabled = false;
4
+ fillColor;
5
+ finalColor;
6
+ id = "bubble";
7
+ inRange = false;
8
+ opacity;
9
+ priority = 100;
10
+ radius;
11
+ strokeColor;
12
+ }
@@ -1,12 +1,14 @@
1
1
  import { Circle, Rectangle, colorMix, double, getDistance, half, isInArray, itemFromSingleOrMultiple, loadOptionProperty, millisecondsToSeconds, rangeColorToHsl, rgbToHsl, safeDocument, } from "@tsparticles/engine";
2
2
  import { DivType, ExternalInteractorBase, divMode, divModeExecute, isDivModeEnabled, mouseLeaveEvent, mouseMoveEvent, } from "@tsparticles/plugin-interactivity";
3
3
  import { Bubble } from "./Options/Classes/Bubble.js";
4
+ import { BubbleModifier } from "./BubbleModifier.js";
4
5
  import { ProcessBubbleType } from "./Enums.js";
5
6
  import { calculateBubbleValue } from "./Utils.js";
6
7
  const bubbleMode = "bubble", minDistance = 0, defaultClickTime = 0, defaultOpacity = 1, ratioOffset = 1, defaultBubbleValue = 0, minRatio = 0, defaultRatio = 1;
7
8
  export class Bubbler extends ExternalInteractorBase {
8
9
  handleClickMode;
9
10
  #maxDistance;
11
+ #modifiers = new WeakMap();
10
12
  #pluginManager;
11
13
  constructor(pluginManager, container) {
12
14
  super(container);
@@ -25,13 +27,21 @@ export class Bubbler extends ExternalInteractorBase {
25
27
  return this.#maxDistance;
26
28
  }
27
29
  clear(particle, _delta, force) {
28
- if (particle.bubble.inRange && !force) {
30
+ const mod = this.#modifiers.get(particle);
31
+ if (mod?.inRange && !force) {
29
32
  return;
30
33
  }
31
- delete particle.bubble.div;
32
- delete particle.bubble.opacity;
33
- delete particle.bubble.radius;
34
- delete particle.bubble.color;
34
+ particle.removeModifier(bubbleMode);
35
+ this.#modifiers.delete(particle);
36
+ }
37
+ getOrCreateModifier(particle) {
38
+ let mod = this.#modifiers.get(particle);
39
+ if (!mod) {
40
+ mod = new BubbleModifier();
41
+ this.#modifiers.set(particle, mod);
42
+ particle.addModifier(mod);
43
+ }
44
+ return mod;
35
45
  }
36
46
  init() {
37
47
  const container = this.container, bubble = container.actualOptions.interactivity?.modes.bubble;
@@ -77,7 +87,11 @@ export class Bubbler extends ExternalInteractorBase {
77
87
  loadOptionProperty(options, "bubble", Bubble, ...sources);
78
88
  }
79
89
  reset(_interactivityData, particle) {
80
- particle.bubble.inRange = false;
90
+ const mod = this.#modifiers.get(particle);
91
+ if (mod) {
92
+ mod.enabled = false;
93
+ mod.inRange = false;
94
+ }
81
95
  }
82
96
  #clickBubble(interactivityData) {
83
97
  const container = this.container, options = container.actualOptions, mouseClickPos = interactivityData.mouse.clickPosition, bubbleOptions = options.interactivity?.modes.bubble;
@@ -94,7 +108,9 @@ export class Bubbler extends ExternalInteractorBase {
94
108
  if (!bubble.clicking) {
95
109
  continue;
96
110
  }
97
- particle.bubble.inRange = !bubble.durationEnd;
111
+ const mod = this.getOrCreateModifier(particle);
112
+ mod.enabled = !bubble.durationEnd;
113
+ mod.inRange = !bubble.durationEnd;
98
114
  const pos = particle.getPosition(), distMouse = getDistance(pos, mouseClickPos), timeSpent = (performance.now() - (interactivityData.mouse.clickTime ?? defaultClickTime)) / millisecondsToSeconds;
99
115
  if (timeSpent > bubbleOptions.duration) {
100
116
  bubble.durationEnd = true;
@@ -106,7 +122,7 @@ export class Bubbler extends ExternalInteractorBase {
106
122
  const sizeData = {
107
123
  bubbleObj: {
108
124
  optValue: container.retina.bubbleModeSize,
109
- value: particle.bubble.radius,
125
+ value: mod.radius,
110
126
  },
111
127
  particlesObj: {
112
128
  optValue: particle.size.max,
@@ -118,7 +134,7 @@ export class Bubbler extends ExternalInteractorBase {
118
134
  const opacityData = {
119
135
  bubbleObj: {
120
136
  optValue: bubbleOptions.opacity,
121
- value: particle.bubble.opacity,
137
+ value: mod.opacity,
122
138
  },
123
139
  particlesObj: {
124
140
  optValue: particle.opacity?.max ?? defaultOpacity,
@@ -131,7 +147,8 @@ export class Bubbler extends ExternalInteractorBase {
131
147
  this.#hoverBubbleColor(particle, distMouse);
132
148
  }
133
149
  else {
134
- delete particle.bubble.color;
150
+ mod.fillColor = undefined;
151
+ mod.strokeColor = undefined;
135
152
  }
136
153
  }
137
154
  }
@@ -142,7 +159,9 @@ export class Bubbler extends ExternalInteractorBase {
142
159
  }
143
160
  const query = container.particles.grid.queryCircle(mousePos, distance, p => this.isEnabled(interactivityData, p));
144
161
  for (const particle of query) {
145
- particle.bubble.inRange = true;
162
+ const mod = this.getOrCreateModifier(particle);
163
+ mod.enabled = true;
164
+ mod.inRange = true;
146
165
  const pos = particle.getPosition(), pointDistance = getDistance(pos, mousePos), ratio = ratioOffset - pointDistance / distance;
147
166
  if (pointDistance <= distance) {
148
167
  if (ratio >= minRatio && interactivityData.status === mouseMoveEvent) {
@@ -160,30 +179,38 @@ export class Bubbler extends ExternalInteractorBase {
160
179
  }
161
180
  }
162
181
  #hoverBubbleColor(particle, ratio, divBubble) {
163
- const options = this.container.actualOptions, bubbleOptions = divBubble ?? options.interactivity?.modes.bubble;
182
+ const options = this.container.actualOptions, bubbleOptions = divBubble ?? options.interactivity?.modes.bubble, mod = this.getOrCreateModifier(particle);
164
183
  if (!bubbleOptions) {
165
184
  return;
166
185
  }
167
- if (!particle.bubble.finalColor) {
186
+ if (!mod.finalColor) {
168
187
  const modeColor = bubbleOptions.color;
169
188
  if (!modeColor) {
170
189
  return;
171
190
  }
172
191
  const bubbleColor = itemFromSingleOrMultiple(modeColor);
173
- particle.bubble.finalColor = rangeColorToHsl(this.#pluginManager, bubbleColor);
192
+ mod.finalColor = rangeColorToHsl(this.#pluginManager, bubbleColor);
174
193
  }
175
- if (!particle.bubble.finalColor) {
194
+ if (!mod.finalColor) {
176
195
  return;
177
196
  }
178
197
  if (bubbleOptions.mix) {
179
- particle.bubble.color = undefined;
198
+ mod.fillColor = undefined;
199
+ mod.strokeColor = undefined;
180
200
  const pColor = particle.getFillColor();
181
- particle.bubble.color = pColor
182
- ? rgbToHsl(colorMix(pColor, particle.bubble.finalColor, ratioOffset - ratio, ratio))
183
- : particle.bubble.finalColor;
201
+ if (pColor) {
202
+ const mixedColor = rgbToHsl(colorMix(pColor, mod.finalColor, ratioOffset - ratio, ratio));
203
+ mod.fillColor = mixedColor;
204
+ mod.strokeColor = mixedColor;
205
+ }
206
+ else {
207
+ mod.fillColor = mod.finalColor;
208
+ mod.strokeColor = mod.finalColor;
209
+ }
184
210
  }
185
211
  else {
186
- particle.bubble.color = particle.bubble.finalColor;
212
+ mod.fillColor = mod.finalColor;
213
+ mod.strokeColor = mod.finalColor;
187
214
  }
188
215
  }
189
216
  #hoverBubbleOpacity(particle, ratio, divBubble) {
@@ -193,7 +220,8 @@ export class Bubbler extends ExternalInteractorBase {
193
220
  }
194
221
  const pOpacity = particle.opacity?.value ?? defaultOpacity, opacity = calculateBubbleValue(pOpacity, modeOpacity, particle.opacity?.max ?? defaultOpacity, ratio);
195
222
  if (opacity !== undefined) {
196
- particle.bubble.opacity = opacity;
223
+ const mod = this.getOrCreateModifier(particle);
224
+ mod.opacity = opacity;
197
225
  }
198
226
  }
199
227
  #hoverBubbleSize(particle, ratio, divBubble) {
@@ -203,7 +231,8 @@ export class Bubbler extends ExternalInteractorBase {
203
231
  }
204
232
  const pSize = particle.size.value, size = calculateBubbleValue(pSize, modeSize, particle.size.max, ratio);
205
233
  if (size !== undefined) {
206
- particle.bubble.radius = size;
234
+ const mod = this.getOrCreateModifier(particle);
235
+ mod.radius = size;
207
236
  }
208
237
  }
209
238
  #process(particle, distMouse, timeSpent, data) {
@@ -216,13 +245,14 @@ export class Bubbler extends ExternalInteractorBase {
216
245
  return;
217
246
  }
218
247
  container.bubble ??= {};
248
+ const mod = this.getOrCreateModifier(particle);
219
249
  if (container.bubble.durationEnd) {
220
250
  if (pObjBubble) {
221
251
  if (type === ProcessBubbleType.size) {
222
- delete particle.bubble.radius;
252
+ mod.radius = undefined;
223
253
  }
224
254
  if (type === ProcessBubbleType.opacity) {
225
- delete particle.bubble.opacity;
255
+ mod.opacity = undefined;
226
256
  }
227
257
  }
228
258
  }
@@ -232,19 +262,19 @@ export class Bubbler extends ExternalInteractorBase {
232
262
  if (obj !== bubbleParam) {
233
263
  const value = pObj - (timeSpent * (pObj - bubbleParam)) / bubbleDuration;
234
264
  if (type === ProcessBubbleType.size) {
235
- particle.bubble.radius = value;
265
+ mod.radius = value;
236
266
  }
237
267
  if (type === ProcessBubbleType.opacity) {
238
- particle.bubble.opacity = value;
268
+ mod.opacity = value;
239
269
  }
240
270
  }
241
271
  }
242
272
  else {
243
273
  if (type === ProcessBubbleType.size) {
244
- delete particle.bubble.radius;
274
+ mod.radius = undefined;
245
275
  }
246
276
  if (type === ProcessBubbleType.opacity) {
247
- delete particle.bubble.opacity;
277
+ mod.opacity = undefined;
248
278
  }
249
279
  }
250
280
  }
@@ -265,11 +295,13 @@ export class Bubbler extends ExternalInteractorBase {
265
295
  if (!area.contains(particle.getPosition())) {
266
296
  continue;
267
297
  }
268
- particle.bubble.inRange = true;
298
+ const mod = this.getOrCreateModifier(particle);
299
+ mod.enabled = true;
300
+ mod.inRange = true;
269
301
  const divs = bubble.divs, divBubble = divMode(divs, elem);
270
- if (!particle.bubble.div || particle.bubble.div !== elem) {
302
+ if (!mod.div || mod.div !== elem) {
271
303
  this.clear(particle, delta, true);
272
- particle.bubble.div = elem;
304
+ mod.div = elem;
273
305
  }
274
306
  this.#hoverBubbleSize(particle, defaultRatio, divBubble);
275
307
  this.#hoverBubbleOpacity(particle, defaultRatio, divBubble);
package/browser/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { ensureInteractivityPluginLoaded } from "@tsparticles/plugin-interactivity";
2
2
  import { Bubbler } from "./Bubbler.js";
3
3
  export async function loadExternalBubbleInteraction(engine) {
4
- engine.checkVersion("4.2.1");
4
+ engine.checkVersion("4.3.0");
5
5
  await engine.pluginManager.register((e) => {
6
6
  ensureInteractivityPluginLoaded(e);
7
7
  e.pluginManager.addInteractor?.("externalBubble", container => {
@@ -9,6 +9,7 @@ export async function loadExternalBubbleInteraction(engine) {
9
9
  });
10
10
  });
11
11
  }
12
+ export * from "./BubbleModifier.js";
12
13
  export * from "./Options/Classes/BubbleBase.js";
13
14
  export * from "./Options/Classes/BubbleDiv.js";
14
15
  export * from "./Options/Classes/Bubble.js";
@@ -1,5 +1,5 @@
1
1
  export async function loadExternalBubbleInteraction(engine) {
2
- engine.checkVersion("4.2.1");
2
+ engine.checkVersion("4.3.0");
3
3
  await engine.pluginManager.register(async (e) => {
4
4
  const { ensureInteractivityPluginLoaded } = await import("@tsparticles/plugin-interactivity/lazy");
5
5
  ensureInteractivityPluginLoaded(e);
@@ -0,0 +1,12 @@
1
+ export class BubbleModifier {
2
+ div;
3
+ enabled = false;
4
+ fillColor;
5
+ finalColor;
6
+ id = "bubble";
7
+ inRange = false;
8
+ opacity;
9
+ priority = 100;
10
+ radius;
11
+ strokeColor;
12
+ }
package/cjs/Bubbler.js CHANGED
@@ -1,12 +1,14 @@
1
1
  import { Circle, Rectangle, colorMix, double, getDistance, half, isInArray, itemFromSingleOrMultiple, loadOptionProperty, millisecondsToSeconds, rangeColorToHsl, rgbToHsl, safeDocument, } from "@tsparticles/engine";
2
2
  import { DivType, ExternalInteractorBase, divMode, divModeExecute, isDivModeEnabled, mouseLeaveEvent, mouseMoveEvent, } from "@tsparticles/plugin-interactivity";
3
3
  import { Bubble } from "./Options/Classes/Bubble.js";
4
+ import { BubbleModifier } from "./BubbleModifier.js";
4
5
  import { ProcessBubbleType } from "./Enums.js";
5
6
  import { calculateBubbleValue } from "./Utils.js";
6
7
  const bubbleMode = "bubble", minDistance = 0, defaultClickTime = 0, defaultOpacity = 1, ratioOffset = 1, defaultBubbleValue = 0, minRatio = 0, defaultRatio = 1;
7
8
  export class Bubbler extends ExternalInteractorBase {
8
9
  handleClickMode;
9
10
  #maxDistance;
11
+ #modifiers = new WeakMap();
10
12
  #pluginManager;
11
13
  constructor(pluginManager, container) {
12
14
  super(container);
@@ -25,13 +27,21 @@ export class Bubbler extends ExternalInteractorBase {
25
27
  return this.#maxDistance;
26
28
  }
27
29
  clear(particle, _delta, force) {
28
- if (particle.bubble.inRange && !force) {
30
+ const mod = this.#modifiers.get(particle);
31
+ if (mod?.inRange && !force) {
29
32
  return;
30
33
  }
31
- delete particle.bubble.div;
32
- delete particle.bubble.opacity;
33
- delete particle.bubble.radius;
34
- delete particle.bubble.color;
34
+ particle.removeModifier(bubbleMode);
35
+ this.#modifiers.delete(particle);
36
+ }
37
+ getOrCreateModifier(particle) {
38
+ let mod = this.#modifiers.get(particle);
39
+ if (!mod) {
40
+ mod = new BubbleModifier();
41
+ this.#modifiers.set(particle, mod);
42
+ particle.addModifier(mod);
43
+ }
44
+ return mod;
35
45
  }
36
46
  init() {
37
47
  const container = this.container, bubble = container.actualOptions.interactivity?.modes.bubble;
@@ -77,7 +87,11 @@ export class Bubbler extends ExternalInteractorBase {
77
87
  loadOptionProperty(options, "bubble", Bubble, ...sources);
78
88
  }
79
89
  reset(_interactivityData, particle) {
80
- particle.bubble.inRange = false;
90
+ const mod = this.#modifiers.get(particle);
91
+ if (mod) {
92
+ mod.enabled = false;
93
+ mod.inRange = false;
94
+ }
81
95
  }
82
96
  #clickBubble(interactivityData) {
83
97
  const container = this.container, options = container.actualOptions, mouseClickPos = interactivityData.mouse.clickPosition, bubbleOptions = options.interactivity?.modes.bubble;
@@ -94,7 +108,9 @@ export class Bubbler extends ExternalInteractorBase {
94
108
  if (!bubble.clicking) {
95
109
  continue;
96
110
  }
97
- particle.bubble.inRange = !bubble.durationEnd;
111
+ const mod = this.getOrCreateModifier(particle);
112
+ mod.enabled = !bubble.durationEnd;
113
+ mod.inRange = !bubble.durationEnd;
98
114
  const pos = particle.getPosition(), distMouse = getDistance(pos, mouseClickPos), timeSpent = (performance.now() - (interactivityData.mouse.clickTime ?? defaultClickTime)) / millisecondsToSeconds;
99
115
  if (timeSpent > bubbleOptions.duration) {
100
116
  bubble.durationEnd = true;
@@ -106,7 +122,7 @@ export class Bubbler extends ExternalInteractorBase {
106
122
  const sizeData = {
107
123
  bubbleObj: {
108
124
  optValue: container.retina.bubbleModeSize,
109
- value: particle.bubble.radius,
125
+ value: mod.radius,
110
126
  },
111
127
  particlesObj: {
112
128
  optValue: particle.size.max,
@@ -118,7 +134,7 @@ export class Bubbler extends ExternalInteractorBase {
118
134
  const opacityData = {
119
135
  bubbleObj: {
120
136
  optValue: bubbleOptions.opacity,
121
- value: particle.bubble.opacity,
137
+ value: mod.opacity,
122
138
  },
123
139
  particlesObj: {
124
140
  optValue: particle.opacity?.max ?? defaultOpacity,
@@ -131,7 +147,8 @@ export class Bubbler extends ExternalInteractorBase {
131
147
  this.#hoverBubbleColor(particle, distMouse);
132
148
  }
133
149
  else {
134
- delete particle.bubble.color;
150
+ mod.fillColor = undefined;
151
+ mod.strokeColor = undefined;
135
152
  }
136
153
  }
137
154
  }
@@ -142,7 +159,9 @@ export class Bubbler extends ExternalInteractorBase {
142
159
  }
143
160
  const query = container.particles.grid.queryCircle(mousePos, distance, p => this.isEnabled(interactivityData, p));
144
161
  for (const particle of query) {
145
- particle.bubble.inRange = true;
162
+ const mod = this.getOrCreateModifier(particle);
163
+ mod.enabled = true;
164
+ mod.inRange = true;
146
165
  const pos = particle.getPosition(), pointDistance = getDistance(pos, mousePos), ratio = ratioOffset - pointDistance / distance;
147
166
  if (pointDistance <= distance) {
148
167
  if (ratio >= minRatio && interactivityData.status === mouseMoveEvent) {
@@ -160,30 +179,38 @@ export class Bubbler extends ExternalInteractorBase {
160
179
  }
161
180
  }
162
181
  #hoverBubbleColor(particle, ratio, divBubble) {
163
- const options = this.container.actualOptions, bubbleOptions = divBubble ?? options.interactivity?.modes.bubble;
182
+ const options = this.container.actualOptions, bubbleOptions = divBubble ?? options.interactivity?.modes.bubble, mod = this.getOrCreateModifier(particle);
164
183
  if (!bubbleOptions) {
165
184
  return;
166
185
  }
167
- if (!particle.bubble.finalColor) {
186
+ if (!mod.finalColor) {
168
187
  const modeColor = bubbleOptions.color;
169
188
  if (!modeColor) {
170
189
  return;
171
190
  }
172
191
  const bubbleColor = itemFromSingleOrMultiple(modeColor);
173
- particle.bubble.finalColor = rangeColorToHsl(this.#pluginManager, bubbleColor);
192
+ mod.finalColor = rangeColorToHsl(this.#pluginManager, bubbleColor);
174
193
  }
175
- if (!particle.bubble.finalColor) {
194
+ if (!mod.finalColor) {
176
195
  return;
177
196
  }
178
197
  if (bubbleOptions.mix) {
179
- particle.bubble.color = undefined;
198
+ mod.fillColor = undefined;
199
+ mod.strokeColor = undefined;
180
200
  const pColor = particle.getFillColor();
181
- particle.bubble.color = pColor
182
- ? rgbToHsl(colorMix(pColor, particle.bubble.finalColor, ratioOffset - ratio, ratio))
183
- : particle.bubble.finalColor;
201
+ if (pColor) {
202
+ const mixedColor = rgbToHsl(colorMix(pColor, mod.finalColor, ratioOffset - ratio, ratio));
203
+ mod.fillColor = mixedColor;
204
+ mod.strokeColor = mixedColor;
205
+ }
206
+ else {
207
+ mod.fillColor = mod.finalColor;
208
+ mod.strokeColor = mod.finalColor;
209
+ }
184
210
  }
185
211
  else {
186
- particle.bubble.color = particle.bubble.finalColor;
212
+ mod.fillColor = mod.finalColor;
213
+ mod.strokeColor = mod.finalColor;
187
214
  }
188
215
  }
189
216
  #hoverBubbleOpacity(particle, ratio, divBubble) {
@@ -193,7 +220,8 @@ export class Bubbler extends ExternalInteractorBase {
193
220
  }
194
221
  const pOpacity = particle.opacity?.value ?? defaultOpacity, opacity = calculateBubbleValue(pOpacity, modeOpacity, particle.opacity?.max ?? defaultOpacity, ratio);
195
222
  if (opacity !== undefined) {
196
- particle.bubble.opacity = opacity;
223
+ const mod = this.getOrCreateModifier(particle);
224
+ mod.opacity = opacity;
197
225
  }
198
226
  }
199
227
  #hoverBubbleSize(particle, ratio, divBubble) {
@@ -203,7 +231,8 @@ export class Bubbler extends ExternalInteractorBase {
203
231
  }
204
232
  const pSize = particle.size.value, size = calculateBubbleValue(pSize, modeSize, particle.size.max, ratio);
205
233
  if (size !== undefined) {
206
- particle.bubble.radius = size;
234
+ const mod = this.getOrCreateModifier(particle);
235
+ mod.radius = size;
207
236
  }
208
237
  }
209
238
  #process(particle, distMouse, timeSpent, data) {
@@ -216,13 +245,14 @@ export class Bubbler extends ExternalInteractorBase {
216
245
  return;
217
246
  }
218
247
  container.bubble ??= {};
248
+ const mod = this.getOrCreateModifier(particle);
219
249
  if (container.bubble.durationEnd) {
220
250
  if (pObjBubble) {
221
251
  if (type === ProcessBubbleType.size) {
222
- delete particle.bubble.radius;
252
+ mod.radius = undefined;
223
253
  }
224
254
  if (type === ProcessBubbleType.opacity) {
225
- delete particle.bubble.opacity;
255
+ mod.opacity = undefined;
226
256
  }
227
257
  }
228
258
  }
@@ -232,19 +262,19 @@ export class Bubbler extends ExternalInteractorBase {
232
262
  if (obj !== bubbleParam) {
233
263
  const value = pObj - (timeSpent * (pObj - bubbleParam)) / bubbleDuration;
234
264
  if (type === ProcessBubbleType.size) {
235
- particle.bubble.radius = value;
265
+ mod.radius = value;
236
266
  }
237
267
  if (type === ProcessBubbleType.opacity) {
238
- particle.bubble.opacity = value;
268
+ mod.opacity = value;
239
269
  }
240
270
  }
241
271
  }
242
272
  else {
243
273
  if (type === ProcessBubbleType.size) {
244
- delete particle.bubble.radius;
274
+ mod.radius = undefined;
245
275
  }
246
276
  if (type === ProcessBubbleType.opacity) {
247
- delete particle.bubble.opacity;
277
+ mod.opacity = undefined;
248
278
  }
249
279
  }
250
280
  }
@@ -265,11 +295,13 @@ export class Bubbler extends ExternalInteractorBase {
265
295
  if (!area.contains(particle.getPosition())) {
266
296
  continue;
267
297
  }
268
- particle.bubble.inRange = true;
298
+ const mod = this.getOrCreateModifier(particle);
299
+ mod.enabled = true;
300
+ mod.inRange = true;
269
301
  const divs = bubble.divs, divBubble = divMode(divs, elem);
270
- if (!particle.bubble.div || particle.bubble.div !== elem) {
302
+ if (!mod.div || mod.div !== elem) {
271
303
  this.clear(particle, delta, true);
272
- particle.bubble.div = elem;
304
+ mod.div = elem;
273
305
  }
274
306
  this.#hoverBubbleSize(particle, defaultRatio, divBubble);
275
307
  this.#hoverBubbleOpacity(particle, defaultRatio, divBubble);
package/cjs/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { ensureInteractivityPluginLoaded } from "@tsparticles/plugin-interactivity";
2
2
  import { Bubbler } from "./Bubbler.js";
3
3
  export async function loadExternalBubbleInteraction(engine) {
4
- engine.checkVersion("4.2.1");
4
+ engine.checkVersion("4.3.0");
5
5
  await engine.pluginManager.register((e) => {
6
6
  ensureInteractivityPluginLoaded(e);
7
7
  e.pluginManager.addInteractor?.("externalBubble", container => {
@@ -9,6 +9,7 @@ export async function loadExternalBubbleInteraction(engine) {
9
9
  });
10
10
  });
11
11
  }
12
+ export * from "./BubbleModifier.js";
12
13
  export * from "./Options/Classes/BubbleBase.js";
13
14
  export * from "./Options/Classes/BubbleDiv.js";
14
15
  export * from "./Options/Classes/Bubble.js";
package/cjs/index.lazy.js CHANGED
@@ -1,5 +1,5 @@
1
1
  export async function loadExternalBubbleInteraction(engine) {
2
- engine.checkVersion("4.2.1");
2
+ engine.checkVersion("4.3.0");
3
3
  await engine.pluginManager.register(async (e) => {
4
4
  const { ensureInteractivityPluginLoaded } = await import("@tsparticles/plugin-interactivity/lazy");
5
5
  ensureInteractivityPluginLoaded(e);
@@ -0,0 +1,12 @@
1
+ export class BubbleModifier {
2
+ div;
3
+ enabled = false;
4
+ fillColor;
5
+ finalColor;
6
+ id = "bubble";
7
+ inRange = false;
8
+ opacity;
9
+ priority = 100;
10
+ radius;
11
+ strokeColor;
12
+ }
package/esm/Bubbler.js CHANGED
@@ -1,12 +1,14 @@
1
1
  import { Circle, Rectangle, colorMix, double, getDistance, half, isInArray, itemFromSingleOrMultiple, loadOptionProperty, millisecondsToSeconds, rangeColorToHsl, rgbToHsl, safeDocument, } from "@tsparticles/engine";
2
2
  import { DivType, ExternalInteractorBase, divMode, divModeExecute, isDivModeEnabled, mouseLeaveEvent, mouseMoveEvent, } from "@tsparticles/plugin-interactivity";
3
3
  import { Bubble } from "./Options/Classes/Bubble.js";
4
+ import { BubbleModifier } from "./BubbleModifier.js";
4
5
  import { ProcessBubbleType } from "./Enums.js";
5
6
  import { calculateBubbleValue } from "./Utils.js";
6
7
  const bubbleMode = "bubble", minDistance = 0, defaultClickTime = 0, defaultOpacity = 1, ratioOffset = 1, defaultBubbleValue = 0, minRatio = 0, defaultRatio = 1;
7
8
  export class Bubbler extends ExternalInteractorBase {
8
9
  handleClickMode;
9
10
  #maxDistance;
11
+ #modifiers = new WeakMap();
10
12
  #pluginManager;
11
13
  constructor(pluginManager, container) {
12
14
  super(container);
@@ -25,13 +27,21 @@ export class Bubbler extends ExternalInteractorBase {
25
27
  return this.#maxDistance;
26
28
  }
27
29
  clear(particle, _delta, force) {
28
- if (particle.bubble.inRange && !force) {
30
+ const mod = this.#modifiers.get(particle);
31
+ if (mod?.inRange && !force) {
29
32
  return;
30
33
  }
31
- delete particle.bubble.div;
32
- delete particle.bubble.opacity;
33
- delete particle.bubble.radius;
34
- delete particle.bubble.color;
34
+ particle.removeModifier(bubbleMode);
35
+ this.#modifiers.delete(particle);
36
+ }
37
+ getOrCreateModifier(particle) {
38
+ let mod = this.#modifiers.get(particle);
39
+ if (!mod) {
40
+ mod = new BubbleModifier();
41
+ this.#modifiers.set(particle, mod);
42
+ particle.addModifier(mod);
43
+ }
44
+ return mod;
35
45
  }
36
46
  init() {
37
47
  const container = this.container, bubble = container.actualOptions.interactivity?.modes.bubble;
@@ -77,7 +87,11 @@ export class Bubbler extends ExternalInteractorBase {
77
87
  loadOptionProperty(options, "bubble", Bubble, ...sources);
78
88
  }
79
89
  reset(_interactivityData, particle) {
80
- particle.bubble.inRange = false;
90
+ const mod = this.#modifiers.get(particle);
91
+ if (mod) {
92
+ mod.enabled = false;
93
+ mod.inRange = false;
94
+ }
81
95
  }
82
96
  #clickBubble(interactivityData) {
83
97
  const container = this.container, options = container.actualOptions, mouseClickPos = interactivityData.mouse.clickPosition, bubbleOptions = options.interactivity?.modes.bubble;
@@ -94,7 +108,9 @@ export class Bubbler extends ExternalInteractorBase {
94
108
  if (!bubble.clicking) {
95
109
  continue;
96
110
  }
97
- particle.bubble.inRange = !bubble.durationEnd;
111
+ const mod = this.getOrCreateModifier(particle);
112
+ mod.enabled = !bubble.durationEnd;
113
+ mod.inRange = !bubble.durationEnd;
98
114
  const pos = particle.getPosition(), distMouse = getDistance(pos, mouseClickPos), timeSpent = (performance.now() - (interactivityData.mouse.clickTime ?? defaultClickTime)) / millisecondsToSeconds;
99
115
  if (timeSpent > bubbleOptions.duration) {
100
116
  bubble.durationEnd = true;
@@ -106,7 +122,7 @@ export class Bubbler extends ExternalInteractorBase {
106
122
  const sizeData = {
107
123
  bubbleObj: {
108
124
  optValue: container.retina.bubbleModeSize,
109
- value: particle.bubble.radius,
125
+ value: mod.radius,
110
126
  },
111
127
  particlesObj: {
112
128
  optValue: particle.size.max,
@@ -118,7 +134,7 @@ export class Bubbler extends ExternalInteractorBase {
118
134
  const opacityData = {
119
135
  bubbleObj: {
120
136
  optValue: bubbleOptions.opacity,
121
- value: particle.bubble.opacity,
137
+ value: mod.opacity,
122
138
  },
123
139
  particlesObj: {
124
140
  optValue: particle.opacity?.max ?? defaultOpacity,
@@ -131,7 +147,8 @@ export class Bubbler extends ExternalInteractorBase {
131
147
  this.#hoverBubbleColor(particle, distMouse);
132
148
  }
133
149
  else {
134
- delete particle.bubble.color;
150
+ mod.fillColor = undefined;
151
+ mod.strokeColor = undefined;
135
152
  }
136
153
  }
137
154
  }
@@ -142,7 +159,9 @@ export class Bubbler extends ExternalInteractorBase {
142
159
  }
143
160
  const query = container.particles.grid.queryCircle(mousePos, distance, p => this.isEnabled(interactivityData, p));
144
161
  for (const particle of query) {
145
- particle.bubble.inRange = true;
162
+ const mod = this.getOrCreateModifier(particle);
163
+ mod.enabled = true;
164
+ mod.inRange = true;
146
165
  const pos = particle.getPosition(), pointDistance = getDistance(pos, mousePos), ratio = ratioOffset - pointDistance / distance;
147
166
  if (pointDistance <= distance) {
148
167
  if (ratio >= minRatio && interactivityData.status === mouseMoveEvent) {
@@ -160,30 +179,38 @@ export class Bubbler extends ExternalInteractorBase {
160
179
  }
161
180
  }
162
181
  #hoverBubbleColor(particle, ratio, divBubble) {
163
- const options = this.container.actualOptions, bubbleOptions = divBubble ?? options.interactivity?.modes.bubble;
182
+ const options = this.container.actualOptions, bubbleOptions = divBubble ?? options.interactivity?.modes.bubble, mod = this.getOrCreateModifier(particle);
164
183
  if (!bubbleOptions) {
165
184
  return;
166
185
  }
167
- if (!particle.bubble.finalColor) {
186
+ if (!mod.finalColor) {
168
187
  const modeColor = bubbleOptions.color;
169
188
  if (!modeColor) {
170
189
  return;
171
190
  }
172
191
  const bubbleColor = itemFromSingleOrMultiple(modeColor);
173
- particle.bubble.finalColor = rangeColorToHsl(this.#pluginManager, bubbleColor);
192
+ mod.finalColor = rangeColorToHsl(this.#pluginManager, bubbleColor);
174
193
  }
175
- if (!particle.bubble.finalColor) {
194
+ if (!mod.finalColor) {
176
195
  return;
177
196
  }
178
197
  if (bubbleOptions.mix) {
179
- particle.bubble.color = undefined;
198
+ mod.fillColor = undefined;
199
+ mod.strokeColor = undefined;
180
200
  const pColor = particle.getFillColor();
181
- particle.bubble.color = pColor
182
- ? rgbToHsl(colorMix(pColor, particle.bubble.finalColor, ratioOffset - ratio, ratio))
183
- : particle.bubble.finalColor;
201
+ if (pColor) {
202
+ const mixedColor = rgbToHsl(colorMix(pColor, mod.finalColor, ratioOffset - ratio, ratio));
203
+ mod.fillColor = mixedColor;
204
+ mod.strokeColor = mixedColor;
205
+ }
206
+ else {
207
+ mod.fillColor = mod.finalColor;
208
+ mod.strokeColor = mod.finalColor;
209
+ }
184
210
  }
185
211
  else {
186
- particle.bubble.color = particle.bubble.finalColor;
212
+ mod.fillColor = mod.finalColor;
213
+ mod.strokeColor = mod.finalColor;
187
214
  }
188
215
  }
189
216
  #hoverBubbleOpacity(particle, ratio, divBubble) {
@@ -193,7 +220,8 @@ export class Bubbler extends ExternalInteractorBase {
193
220
  }
194
221
  const pOpacity = particle.opacity?.value ?? defaultOpacity, opacity = calculateBubbleValue(pOpacity, modeOpacity, particle.opacity?.max ?? defaultOpacity, ratio);
195
222
  if (opacity !== undefined) {
196
- particle.bubble.opacity = opacity;
223
+ const mod = this.getOrCreateModifier(particle);
224
+ mod.opacity = opacity;
197
225
  }
198
226
  }
199
227
  #hoverBubbleSize(particle, ratio, divBubble) {
@@ -203,7 +231,8 @@ export class Bubbler extends ExternalInteractorBase {
203
231
  }
204
232
  const pSize = particle.size.value, size = calculateBubbleValue(pSize, modeSize, particle.size.max, ratio);
205
233
  if (size !== undefined) {
206
- particle.bubble.radius = size;
234
+ const mod = this.getOrCreateModifier(particle);
235
+ mod.radius = size;
207
236
  }
208
237
  }
209
238
  #process(particle, distMouse, timeSpent, data) {
@@ -216,13 +245,14 @@ export class Bubbler extends ExternalInteractorBase {
216
245
  return;
217
246
  }
218
247
  container.bubble ??= {};
248
+ const mod = this.getOrCreateModifier(particle);
219
249
  if (container.bubble.durationEnd) {
220
250
  if (pObjBubble) {
221
251
  if (type === ProcessBubbleType.size) {
222
- delete particle.bubble.radius;
252
+ mod.radius = undefined;
223
253
  }
224
254
  if (type === ProcessBubbleType.opacity) {
225
- delete particle.bubble.opacity;
255
+ mod.opacity = undefined;
226
256
  }
227
257
  }
228
258
  }
@@ -232,19 +262,19 @@ export class Bubbler extends ExternalInteractorBase {
232
262
  if (obj !== bubbleParam) {
233
263
  const value = pObj - (timeSpent * (pObj - bubbleParam)) / bubbleDuration;
234
264
  if (type === ProcessBubbleType.size) {
235
- particle.bubble.radius = value;
265
+ mod.radius = value;
236
266
  }
237
267
  if (type === ProcessBubbleType.opacity) {
238
- particle.bubble.opacity = value;
268
+ mod.opacity = value;
239
269
  }
240
270
  }
241
271
  }
242
272
  else {
243
273
  if (type === ProcessBubbleType.size) {
244
- delete particle.bubble.radius;
274
+ mod.radius = undefined;
245
275
  }
246
276
  if (type === ProcessBubbleType.opacity) {
247
- delete particle.bubble.opacity;
277
+ mod.opacity = undefined;
248
278
  }
249
279
  }
250
280
  }
@@ -265,11 +295,13 @@ export class Bubbler extends ExternalInteractorBase {
265
295
  if (!area.contains(particle.getPosition())) {
266
296
  continue;
267
297
  }
268
- particle.bubble.inRange = true;
298
+ const mod = this.getOrCreateModifier(particle);
299
+ mod.enabled = true;
300
+ mod.inRange = true;
269
301
  const divs = bubble.divs, divBubble = divMode(divs, elem);
270
- if (!particle.bubble.div || particle.bubble.div !== elem) {
302
+ if (!mod.div || mod.div !== elem) {
271
303
  this.clear(particle, delta, true);
272
- particle.bubble.div = elem;
304
+ mod.div = elem;
273
305
  }
274
306
  this.#hoverBubbleSize(particle, defaultRatio, divBubble);
275
307
  this.#hoverBubbleOpacity(particle, defaultRatio, divBubble);
package/esm/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { ensureInteractivityPluginLoaded } from "@tsparticles/plugin-interactivity";
2
2
  import { Bubbler } from "./Bubbler.js";
3
3
  export async function loadExternalBubbleInteraction(engine) {
4
- engine.checkVersion("4.2.1");
4
+ engine.checkVersion("4.3.0");
5
5
  await engine.pluginManager.register((e) => {
6
6
  ensureInteractivityPluginLoaded(e);
7
7
  e.pluginManager.addInteractor?.("externalBubble", container => {
@@ -9,6 +9,7 @@ export async function loadExternalBubbleInteraction(engine) {
9
9
  });
10
10
  });
11
11
  }
12
+ export * from "./BubbleModifier.js";
12
13
  export * from "./Options/Classes/BubbleBase.js";
13
14
  export * from "./Options/Classes/BubbleDiv.js";
14
15
  export * from "./Options/Classes/Bubble.js";
package/esm/index.lazy.js CHANGED
@@ -1,5 +1,5 @@
1
1
  export async function loadExternalBubbleInteraction(engine) {
2
- engine.checkVersion("4.2.1");
2
+ engine.checkVersion("4.3.0");
3
3
  await engine.pluginManager.register(async (e) => {
4
4
  const { ensureInteractivityPluginLoaded } = await import("@tsparticles/plugin-interactivity/lazy");
5
5
  ensureInteractivityPluginLoaded(e);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tsparticles/interaction-external-bubble",
3
- "version": "4.2.1",
3
+ "version": "4.3.0",
4
4
  "description": "tsParticles bubble external interaction",
5
5
  "homepage": "https://particles.js.org",
6
6
  "repository": {
@@ -97,7 +97,7 @@
97
97
  },
98
98
  "type": "module",
99
99
  "peerDependencies": {
100
- "@tsparticles/engine": "4.2.1",
101
- "@tsparticles/plugin-interactivity": "4.2.1"
100
+ "@tsparticles/engine": "4.3.0",
101
+ "@tsparticles/plugin-interactivity": "4.3.0"
102
102
  }
103
103
  }
package/report.html CHANGED
@@ -4930,7 +4930,7 @@ var drawChart = (function (exports) {
4930
4930
  </script>
4931
4931
  <script>
4932
4932
  /*<!--*/
4933
- const data = {"version":2,"tree":{"name":"root","children":[{"name":"tsparticles.interaction.external.bubble.js","children":[{"name":"dist/browser","children":[{"name":"Options/Classes","children":[{"uid":"cf70f7e3-1","name":"BubbleBase.js"},{"uid":"cf70f7e3-3","name":"BubbleDiv.js"},{"uid":"cf70f7e3-5","name":"Bubble.js"}]},{"uid":"cf70f7e3-7","name":"Enums.js"},{"uid":"cf70f7e3-9","name":"Utils.js"},{"uid":"cf70f7e3-11","name":"Bubbler.js"},{"uid":"cf70f7e3-13","name":"index.js"},{"uid":"cf70f7e3-15","name":"browser.js"}]}]}],"isRoot":true},"nodeParts":{"cf70f7e3-1":{"renderedLength":893,"gzipLength":0,"brotliLength":0,"metaUid":"cf70f7e3-0"},"cf70f7e3-3":{"renderedLength":276,"gzipLength":0,"brotliLength":0,"metaUid":"cf70f7e3-2"},"cf70f7e3-5":{"renderedLength":392,"gzipLength":0,"brotliLength":0,"metaUid":"cf70f7e3-4"},"cf70f7e3-7":{"renderedLength":257,"gzipLength":0,"brotliLength":0,"metaUid":"cf70f7e3-6"},"cf70f7e3-9":{"renderedLength":508,"gzipLength":0,"brotliLength":0,"metaUid":"cf70f7e3-8"},"cf70f7e3-11":{"renderedLength":13938,"gzipLength":0,"brotliLength":0,"metaUid":"cf70f7e3-10"},"cf70f7e3-13":{"renderedLength":409,"gzipLength":0,"brotliLength":0,"metaUid":"cf70f7e3-12"},"cf70f7e3-15":{"renderedLength":201,"gzipLength":0,"brotliLength":0,"metaUid":"cf70f7e3-14"}},"nodeMetas":{"cf70f7e3-0":{"id":"/dist/browser/Options/Classes/BubbleBase.js","moduleParts":{"tsparticles.interaction.external.bubble.js":"cf70f7e3-1"},"imported":[{"uid":"cf70f7e3-17"}],"importedBy":[{"uid":"cf70f7e3-12"},{"uid":"cf70f7e3-2"},{"uid":"cf70f7e3-4"}]},"cf70f7e3-2":{"id":"/dist/browser/Options/Classes/BubbleDiv.js","moduleParts":{"tsparticles.interaction.external.bubble.js":"cf70f7e3-3"},"imported":[{"uid":"cf70f7e3-17"},{"uid":"cf70f7e3-0"}],"importedBy":[{"uid":"cf70f7e3-12"},{"uid":"cf70f7e3-4"}]},"cf70f7e3-4":{"id":"/dist/browser/Options/Classes/Bubble.js","moduleParts":{"tsparticles.interaction.external.bubble.js":"cf70f7e3-5"},"imported":[{"uid":"cf70f7e3-17"},{"uid":"cf70f7e3-0"},{"uid":"cf70f7e3-2"}],"importedBy":[{"uid":"cf70f7e3-12"},{"uid":"cf70f7e3-10"}]},"cf70f7e3-6":{"id":"/dist/browser/Enums.js","moduleParts":{"tsparticles.interaction.external.bubble.js":"cf70f7e3-7"},"imported":[],"importedBy":[{"uid":"cf70f7e3-10"}]},"cf70f7e3-8":{"id":"/dist/browser/Utils.js","moduleParts":{"tsparticles.interaction.external.bubble.js":"cf70f7e3-9"},"imported":[{"uid":"cf70f7e3-17"}],"importedBy":[{"uid":"cf70f7e3-10"}]},"cf70f7e3-10":{"id":"/dist/browser/Bubbler.js","moduleParts":{"tsparticles.interaction.external.bubble.js":"cf70f7e3-11"},"imported":[{"uid":"cf70f7e3-17"},{"uid":"cf70f7e3-16"},{"uid":"cf70f7e3-4"},{"uid":"cf70f7e3-6"},{"uid":"cf70f7e3-8"}],"importedBy":[{"uid":"cf70f7e3-12"}]},"cf70f7e3-12":{"id":"/dist/browser/index.js","moduleParts":{"tsparticles.interaction.external.bubble.js":"cf70f7e3-13"},"imported":[{"uid":"cf70f7e3-16"},{"uid":"cf70f7e3-10"},{"uid":"cf70f7e3-0"},{"uid":"cf70f7e3-2"},{"uid":"cf70f7e3-4"}],"importedBy":[{"uid":"cf70f7e3-14"}]},"cf70f7e3-14":{"id":"/dist/browser/browser.js","moduleParts":{"tsparticles.interaction.external.bubble.js":"cf70f7e3-15"},"imported":[{"uid":"cf70f7e3-12"}],"importedBy":[],"isEntry":true},"cf70f7e3-16":{"id":"@tsparticles/plugin-interactivity","moduleParts":{},"imported":[],"importedBy":[{"uid":"cf70f7e3-12"},{"uid":"cf70f7e3-10"}],"isExternal":true},"cf70f7e3-17":{"id":"@tsparticles/engine","moduleParts":{},"imported":[],"importedBy":[{"uid":"cf70f7e3-10"},{"uid":"cf70f7e3-0"},{"uid":"cf70f7e3-2"},{"uid":"cf70f7e3-4"},{"uid":"cf70f7e3-8"}],"isExternal":true}},"env":{"rollup":"4.62.0"},"options":{"gzip":false,"brotli":false,"sourcemap":false}};
4933
+ const data = {"version":2,"tree":{"name":"root","children":[{"name":"tsparticles.interaction.external.bubble.js","children":[{"name":"dist/browser","children":[{"name":"Options/Classes","children":[{"uid":"a0810d36-1","name":"BubbleBase.js"},{"uid":"a0810d36-3","name":"BubbleDiv.js"},{"uid":"a0810d36-5","name":"Bubble.js"}]},{"uid":"a0810d36-7","name":"BubbleModifier.js"},{"uid":"a0810d36-9","name":"Enums.js"},{"uid":"a0810d36-11","name":"Utils.js"},{"uid":"a0810d36-13","name":"Bubbler.js"},{"uid":"a0810d36-15","name":"index.js"},{"uid":"a0810d36-17","name":"browser.js"}]}]}],"isRoot":true},"nodeParts":{"a0810d36-1":{"renderedLength":893,"gzipLength":0,"brotliLength":0,"metaUid":"a0810d36-0"},"a0810d36-3":{"renderedLength":276,"gzipLength":0,"brotliLength":0,"metaUid":"a0810d36-2"},"a0810d36-5":{"renderedLength":392,"gzipLength":0,"brotliLength":0,"metaUid":"a0810d36-4"},"a0810d36-7":{"renderedLength":235,"gzipLength":0,"brotliLength":0,"metaUid":"a0810d36-6"},"a0810d36-9":{"renderedLength":257,"gzipLength":0,"brotliLength":0,"metaUid":"a0810d36-8"},"a0810d36-11":{"renderedLength":508,"gzipLength":0,"brotliLength":0,"metaUid":"a0810d36-10"},"a0810d36-13":{"renderedLength":15029,"gzipLength":0,"brotliLength":0,"metaUid":"a0810d36-12"},"a0810d36-15":{"renderedLength":409,"gzipLength":0,"brotliLength":0,"metaUid":"a0810d36-14"},"a0810d36-17":{"renderedLength":201,"gzipLength":0,"brotliLength":0,"metaUid":"a0810d36-16"}},"nodeMetas":{"a0810d36-0":{"id":"/dist/browser/Options/Classes/BubbleBase.js","moduleParts":{"tsparticles.interaction.external.bubble.js":"a0810d36-1"},"imported":[{"uid":"a0810d36-19"}],"importedBy":[{"uid":"a0810d36-14"},{"uid":"a0810d36-2"},{"uid":"a0810d36-4"}]},"a0810d36-2":{"id":"/dist/browser/Options/Classes/BubbleDiv.js","moduleParts":{"tsparticles.interaction.external.bubble.js":"a0810d36-3"},"imported":[{"uid":"a0810d36-19"},{"uid":"a0810d36-0"}],"importedBy":[{"uid":"a0810d36-14"},{"uid":"a0810d36-4"}]},"a0810d36-4":{"id":"/dist/browser/Options/Classes/Bubble.js","moduleParts":{"tsparticles.interaction.external.bubble.js":"a0810d36-5"},"imported":[{"uid":"a0810d36-19"},{"uid":"a0810d36-0"},{"uid":"a0810d36-2"}],"importedBy":[{"uid":"a0810d36-14"},{"uid":"a0810d36-12"}]},"a0810d36-6":{"id":"/dist/browser/BubbleModifier.js","moduleParts":{"tsparticles.interaction.external.bubble.js":"a0810d36-7"},"imported":[],"importedBy":[{"uid":"a0810d36-14"},{"uid":"a0810d36-12"}]},"a0810d36-8":{"id":"/dist/browser/Enums.js","moduleParts":{"tsparticles.interaction.external.bubble.js":"a0810d36-9"},"imported":[],"importedBy":[{"uid":"a0810d36-12"}]},"a0810d36-10":{"id":"/dist/browser/Utils.js","moduleParts":{"tsparticles.interaction.external.bubble.js":"a0810d36-11"},"imported":[{"uid":"a0810d36-19"}],"importedBy":[{"uid":"a0810d36-12"}]},"a0810d36-12":{"id":"/dist/browser/Bubbler.js","moduleParts":{"tsparticles.interaction.external.bubble.js":"a0810d36-13"},"imported":[{"uid":"a0810d36-19"},{"uid":"a0810d36-18"},{"uid":"a0810d36-4"},{"uid":"a0810d36-6"},{"uid":"a0810d36-8"},{"uid":"a0810d36-10"}],"importedBy":[{"uid":"a0810d36-14"}]},"a0810d36-14":{"id":"/dist/browser/index.js","moduleParts":{"tsparticles.interaction.external.bubble.js":"a0810d36-15"},"imported":[{"uid":"a0810d36-18"},{"uid":"a0810d36-12"},{"uid":"a0810d36-6"},{"uid":"a0810d36-0"},{"uid":"a0810d36-2"},{"uid":"a0810d36-4"}],"importedBy":[{"uid":"a0810d36-16"}]},"a0810d36-16":{"id":"/dist/browser/browser.js","moduleParts":{"tsparticles.interaction.external.bubble.js":"a0810d36-17"},"imported":[{"uid":"a0810d36-14"}],"importedBy":[],"isEntry":true},"a0810d36-18":{"id":"@tsparticles/plugin-interactivity","moduleParts":{},"imported":[],"importedBy":[{"uid":"a0810d36-14"},{"uid":"a0810d36-12"}],"isExternal":true},"a0810d36-19":{"id":"@tsparticles/engine","moduleParts":{},"imported":[],"importedBy":[{"uid":"a0810d36-12"},{"uid":"a0810d36-0"},{"uid":"a0810d36-2"},{"uid":"a0810d36-4"},{"uid":"a0810d36-10"}],"isExternal":true}},"env":{"rollup":"4.62.2"},"options":{"gzip":false,"brotli":false,"sourcemap":false}};
4934
4934
 
4935
4935
  const run = () => {
4936
4936
  const width = window.innerWidth;
@@ -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
- /* External Interaction v4.2.1 */
2
+ /* External Interaction v4.3.0 */
3
3
  (function (global, factory) {
4
4
  typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@tsparticles/plugin-interactivity'), require('@tsparticles/engine')) :
5
5
  typeof define === 'function' && define.amd ? define(['exports', '@tsparticles/plugin-interactivity', '@tsparticles/engine'], factory) :
@@ -57,6 +57,19 @@
57
57
  }
58
58
  }
59
59
 
60
+ class BubbleModifier {
61
+ div;
62
+ enabled = false;
63
+ fillColor;
64
+ finalColor;
65
+ id = "bubble";
66
+ inRange = false;
67
+ opacity;
68
+ priority = 100;
69
+ radius;
70
+ strokeColor;
71
+ }
72
+
60
73
  var ProcessBubbleType;
61
74
  (function (ProcessBubbleType) {
62
75
  ProcessBubbleType["color"] = "color";
@@ -80,6 +93,7 @@
80
93
  class Bubbler extends pluginInteractivity.ExternalInteractorBase {
81
94
  handleClickMode;
82
95
  #maxDistance;
96
+ #modifiers = new WeakMap();
83
97
  #pluginManager;
84
98
  constructor(pluginManager, container) {
85
99
  super(container);
@@ -98,13 +112,21 @@
98
112
  return this.#maxDistance;
99
113
  }
100
114
  clear(particle, _delta, force) {
101
- if (particle.bubble.inRange && !force) {
115
+ const mod = this.#modifiers.get(particle);
116
+ if (mod?.inRange && !force) {
102
117
  return;
103
118
  }
104
- delete particle.bubble.div;
105
- delete particle.bubble.opacity;
106
- delete particle.bubble.radius;
107
- delete particle.bubble.color;
119
+ particle.removeModifier(bubbleMode);
120
+ this.#modifiers.delete(particle);
121
+ }
122
+ getOrCreateModifier(particle) {
123
+ let mod = this.#modifiers.get(particle);
124
+ if (!mod) {
125
+ mod = new BubbleModifier();
126
+ this.#modifiers.set(particle, mod);
127
+ particle.addModifier(mod);
128
+ }
129
+ return mod;
108
130
  }
109
131
  init() {
110
132
  const container = this.container, bubble = container.actualOptions.interactivity?.modes.bubble;
@@ -150,7 +172,11 @@
150
172
  engine.loadOptionProperty(options, "bubble", Bubble, ...sources);
151
173
  }
152
174
  reset(_interactivityData, particle) {
153
- particle.bubble.inRange = false;
175
+ const mod = this.#modifiers.get(particle);
176
+ if (mod) {
177
+ mod.enabled = false;
178
+ mod.inRange = false;
179
+ }
154
180
  }
155
181
  #clickBubble(interactivityData) {
156
182
  const container = this.container, options = container.actualOptions, mouseClickPos = interactivityData.mouse.clickPosition, bubbleOptions = options.interactivity?.modes.bubble;
@@ -167,7 +193,9 @@
167
193
  if (!bubble.clicking) {
168
194
  continue;
169
195
  }
170
- particle.bubble.inRange = !bubble.durationEnd;
196
+ const mod = this.getOrCreateModifier(particle);
197
+ mod.enabled = !bubble.durationEnd;
198
+ mod.inRange = !bubble.durationEnd;
171
199
  const pos = particle.getPosition(), distMouse = engine.getDistance(pos, mouseClickPos), timeSpent = (performance.now() - (interactivityData.mouse.clickTime ?? defaultClickTime)) / engine.millisecondsToSeconds;
172
200
  if (timeSpent > bubbleOptions.duration) {
173
201
  bubble.durationEnd = true;
@@ -179,7 +207,7 @@
179
207
  const sizeData = {
180
208
  bubbleObj: {
181
209
  optValue: container.retina.bubbleModeSize,
182
- value: particle.bubble.radius,
210
+ value: mod.radius,
183
211
  },
184
212
  particlesObj: {
185
213
  optValue: particle.size.max,
@@ -191,7 +219,7 @@
191
219
  const opacityData = {
192
220
  bubbleObj: {
193
221
  optValue: bubbleOptions.opacity,
194
- value: particle.bubble.opacity,
222
+ value: mod.opacity,
195
223
  },
196
224
  particlesObj: {
197
225
  optValue: particle.opacity?.max ?? defaultOpacity,
@@ -204,7 +232,8 @@
204
232
  this.#hoverBubbleColor(particle, distMouse);
205
233
  }
206
234
  else {
207
- delete particle.bubble.color;
235
+ mod.fillColor = undefined;
236
+ mod.strokeColor = undefined;
208
237
  }
209
238
  }
210
239
  }
@@ -215,7 +244,9 @@
215
244
  }
216
245
  const query = container.particles.grid.queryCircle(mousePos, distance, p => this.isEnabled(interactivityData, p));
217
246
  for (const particle of query) {
218
- particle.bubble.inRange = true;
247
+ const mod = this.getOrCreateModifier(particle);
248
+ mod.enabled = true;
249
+ mod.inRange = true;
219
250
  const pos = particle.getPosition(), pointDistance = engine.getDistance(pos, mousePos), ratio = ratioOffset - pointDistance / distance;
220
251
  if (pointDistance <= distance) {
221
252
  if (ratio >= minRatio && interactivityData.status === pluginInteractivity.mouseMoveEvent) {
@@ -233,30 +264,38 @@
233
264
  }
234
265
  }
235
266
  #hoverBubbleColor(particle, ratio, divBubble) {
236
- const options = this.container.actualOptions, bubbleOptions = divBubble ?? options.interactivity?.modes.bubble;
267
+ const options = this.container.actualOptions, bubbleOptions = divBubble ?? options.interactivity?.modes.bubble, mod = this.getOrCreateModifier(particle);
237
268
  if (!bubbleOptions) {
238
269
  return;
239
270
  }
240
- if (!particle.bubble.finalColor) {
271
+ if (!mod.finalColor) {
241
272
  const modeColor = bubbleOptions.color;
242
273
  if (!modeColor) {
243
274
  return;
244
275
  }
245
276
  const bubbleColor = engine.itemFromSingleOrMultiple(modeColor);
246
- particle.bubble.finalColor = engine.rangeColorToHsl(this.#pluginManager, bubbleColor);
277
+ mod.finalColor = engine.rangeColorToHsl(this.#pluginManager, bubbleColor);
247
278
  }
248
- if (!particle.bubble.finalColor) {
279
+ if (!mod.finalColor) {
249
280
  return;
250
281
  }
251
282
  if (bubbleOptions.mix) {
252
- particle.bubble.color = undefined;
283
+ mod.fillColor = undefined;
284
+ mod.strokeColor = undefined;
253
285
  const pColor = particle.getFillColor();
254
- particle.bubble.color = pColor
255
- ? engine.rgbToHsl(engine.colorMix(pColor, particle.bubble.finalColor, ratioOffset - ratio, ratio))
256
- : particle.bubble.finalColor;
286
+ if (pColor) {
287
+ const mixedColor = engine.rgbToHsl(engine.colorMix(pColor, mod.finalColor, ratioOffset - ratio, ratio));
288
+ mod.fillColor = mixedColor;
289
+ mod.strokeColor = mixedColor;
290
+ }
291
+ else {
292
+ mod.fillColor = mod.finalColor;
293
+ mod.strokeColor = mod.finalColor;
294
+ }
257
295
  }
258
296
  else {
259
- particle.bubble.color = particle.bubble.finalColor;
297
+ mod.fillColor = mod.finalColor;
298
+ mod.strokeColor = mod.finalColor;
260
299
  }
261
300
  }
262
301
  #hoverBubbleOpacity(particle, ratio, divBubble) {
@@ -266,7 +305,8 @@
266
305
  }
267
306
  const pOpacity = particle.opacity?.value ?? defaultOpacity, opacity = calculateBubbleValue(pOpacity, modeOpacity, particle.opacity?.max ?? defaultOpacity, ratio);
268
307
  if (opacity !== undefined) {
269
- particle.bubble.opacity = opacity;
308
+ const mod = this.getOrCreateModifier(particle);
309
+ mod.opacity = opacity;
270
310
  }
271
311
  }
272
312
  #hoverBubbleSize(particle, ratio, divBubble) {
@@ -276,7 +316,8 @@
276
316
  }
277
317
  const pSize = particle.size.value, size = calculateBubbleValue(pSize, modeSize, particle.size.max, ratio);
278
318
  if (size !== undefined) {
279
- particle.bubble.radius = size;
319
+ const mod = this.getOrCreateModifier(particle);
320
+ mod.radius = size;
280
321
  }
281
322
  }
282
323
  #process(particle, distMouse, timeSpent, data) {
@@ -289,13 +330,14 @@
289
330
  return;
290
331
  }
291
332
  container.bubble ??= {};
333
+ const mod = this.getOrCreateModifier(particle);
292
334
  if (container.bubble.durationEnd) {
293
335
  if (pObjBubble) {
294
336
  if (type === ProcessBubbleType.size) {
295
- delete particle.bubble.radius;
337
+ mod.radius = undefined;
296
338
  }
297
339
  if (type === ProcessBubbleType.opacity) {
298
- delete particle.bubble.opacity;
340
+ mod.opacity = undefined;
299
341
  }
300
342
  }
301
343
  }
@@ -305,19 +347,19 @@
305
347
  if (obj !== bubbleParam) {
306
348
  const value = pObj - (timeSpent * (pObj - bubbleParam)) / bubbleDuration;
307
349
  if (type === ProcessBubbleType.size) {
308
- particle.bubble.radius = value;
350
+ mod.radius = value;
309
351
  }
310
352
  if (type === ProcessBubbleType.opacity) {
311
- particle.bubble.opacity = value;
353
+ mod.opacity = value;
312
354
  }
313
355
  }
314
356
  }
315
357
  else {
316
358
  if (type === ProcessBubbleType.size) {
317
- delete particle.bubble.radius;
359
+ mod.radius = undefined;
318
360
  }
319
361
  if (type === ProcessBubbleType.opacity) {
320
- delete particle.bubble.opacity;
362
+ mod.opacity = undefined;
321
363
  }
322
364
  }
323
365
  }
@@ -338,11 +380,13 @@
338
380
  if (!area.contains(particle.getPosition())) {
339
381
  continue;
340
382
  }
341
- particle.bubble.inRange = true;
383
+ const mod = this.getOrCreateModifier(particle);
384
+ mod.enabled = true;
385
+ mod.inRange = true;
342
386
  const divs = bubble.divs, divBubble = pluginInteractivity.divMode(divs, elem);
343
- if (!particle.bubble.div || particle.bubble.div !== elem) {
387
+ if (!mod.div || mod.div !== elem) {
344
388
  this.clear(particle, delta, true);
345
- particle.bubble.div = elem;
389
+ mod.div = elem;
346
390
  }
347
391
  this.#hoverBubbleSize(particle, defaultRatio, divBubble);
348
392
  this.#hoverBubbleOpacity(particle, defaultRatio, divBubble);
@@ -353,7 +397,7 @@
353
397
  }
354
398
 
355
399
  async function loadExternalBubbleInteraction(engine) {
356
- engine.checkVersion("4.2.1");
400
+ engine.checkVersion("4.3.0");
357
401
  await engine.pluginManager.register((e) => {
358
402
  pluginInteractivity.ensureInteractivityPluginLoaded(e);
359
403
  e.pluginManager.addInteractor?.("externalBubble", container => {
@@ -369,6 +413,7 @@
369
413
  exports.Bubble = Bubble;
370
414
  exports.BubbleBase = BubbleBase;
371
415
  exports.BubbleDiv = BubbleDiv;
416
+ exports.BubbleModifier = BubbleModifier;
372
417
  exports.loadExternalBubbleInteraction = loadExternalBubbleInteraction;
373
418
 
374
419
  }));
@@ -1 +1 @@
1
- !function(e){e.__tsParticlesInternals=e.__tsParticlesInternals||{},e.__tsParticlesInternals.bundles=e.__tsParticlesInternals.bundles||{},e.__tsParticlesInternals.effects=e.__tsParticlesInternals.effects||{},e.__tsParticlesInternals.engine=e.__tsParticlesInternals.engine||{},e.__tsParticlesInternals.interactions=e.__tsParticlesInternals.interactions||{},e.__tsParticlesInternals.palettes=e.__tsParticlesInternals.palettes||{},e.__tsParticlesInternals.paths=e.__tsParticlesInternals.paths||{},e.__tsParticlesInternals.plugins=e.__tsParticlesInternals.plugins||{},e.__tsParticlesInternals.plugins=e.__tsParticlesInternals.plugins||{},e.__tsParticlesInternals.plugins.emittersShapes=e.__tsParticlesInternals.plugins.emittersShapes||{},e.__tsParticlesInternals.presets=e.__tsParticlesInternals.presets||{},e.__tsParticlesInternals.shapes=e.__tsParticlesInternals.shapes||{},e.__tsParticlesInternals.updaters=e.__tsParticlesInternals.updaters||{},e.__tsParticlesInternals.utils=e.__tsParticlesInternals.utils||{},e.__tsParticlesInternals.canvas=e.__tsParticlesInternals.canvas||{},e.__tsParticlesInternals.canvas=e.__tsParticlesInternals.canvas||{},e.__tsParticlesInternals.canvas.utils=e.__tsParticlesInternals.canvas.utils||{},e.__tsParticlesInternals.path=e.__tsParticlesInternals.path||{},e.__tsParticlesInternals.path=e.__tsParticlesInternals.path||{},e.__tsParticlesInternals.path.utils=e.__tsParticlesInternals.path.utils||{};var t="undefined"!=typeof Proxy?function(e){return new Proxy(e,{get:function(e,t){return t in e||(e[t]={}),e[t]}})}:function(e){return e};e.__tsParticlesInternals.bundles=t(e.__tsParticlesInternals.bundles),e.__tsParticlesInternals.effects=t(e.__tsParticlesInternals.effects),e.__tsParticlesInternals.interactions=t(e.__tsParticlesInternals.interactions),e.__tsParticlesInternals.palettes=t(e.__tsParticlesInternals.palettes),e.__tsParticlesInternals.paths=t(e.__tsParticlesInternals.paths),e.__tsParticlesInternals.plugins=t(e.__tsParticlesInternals.plugins),e.__tsParticlesInternals.plugins.emittersShapes=t(e.__tsParticlesInternals.plugins.emittersShapes),e.__tsParticlesInternals.presets=t(e.__tsParticlesInternals.presets),e.__tsParticlesInternals.shapes=t(e.__tsParticlesInternals.shapes),e.__tsParticlesInternals.updaters=t(e.__tsParticlesInternals.updaters),e.__tsParticlesInternals.utils=t(e.__tsParticlesInternals.utils),e.__tsParticlesInternals.canvas=t(e.__tsParticlesInternals.canvas),e.__tsParticlesInternals.path=t(e.__tsParticlesInternals.path),e.tsparticlesInternalExports=e.tsparticlesInternalExports||{}}("undefined"!=typeof globalThis?globalThis:"undefined"!=typeof window?window:this),function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("@tsparticles/plugin-interactivity"),require("@tsparticles/engine")):"function"==typeof define&&define.amd?define(["exports","@tsparticles/plugin-interactivity","@tsparticles/engine"],t):t(((e="undefined"!=typeof globalThis?globalThis:e||self).__tsParticlesInternals=e.__tsParticlesInternals||{},e.__tsParticlesInternals.interactions=e.__tsParticlesInternals.interactions||{},e.__tsParticlesInternals.interactions.externalBubble=e.__tsParticlesInternals.interactions.externalBubble||{}),e.__tsParticlesInternals.plugins.interactivity,e.__tsParticlesInternals.engine)}(this,function(e,t,s){"use strict";class i{color;distance=200;duration=.4;mix=!1;opacity;size;load(e){if(!s.isNull(e)){if(s.loadProperty(this,"distance",e.distance),s.loadProperty(this,"duration",e.duration),s.loadProperty(this,"mix",e.mix),s.loadRangeProperty(this,"opacity",e.opacity),void 0!==e.color){const t=s.isArray(this.color)?void 0:this.color;this.color=s.executeOnSingleOrMultiple(e.color,e=>s.OptionsColor.create(t,e))}s.loadProperty(this,"size",e.size)}}}class n extends i{selectors=[];load(e){super.load(e),s.isNull(e)||s.loadProperty(this,"selectors",e.selectors)}}class l extends i{divs;load(e){super.load(e),s.isNull(e)||(this.divs=s.executeOnSingleOrMultiple(e.divs,e=>{const t=new n;return t.load(e),t}))}}var a;function r(e,t,i,n){if(t>=i){const l=e+(t-i)*n;return s.clamp(l,e,t)}if(t<i){const l=e-(i-t)*n;return s.clamp(l,t,e)}}!function(e){e.color="color",e.opacity="opacity",e.size="size"}(a||(a={}));const o="bubble";class c extends t.ExternalInteractorBase{handleClickMode;#e;#t;constructor(e,t){super(t),this.#t=e,this.#e=0,t.bubble??={},this.handleClickMode=e=>{e===o&&(t.bubble??={},t.bubble.clicking=!0)}}get maxDistance(){return this.#e}clear(e,t,s){e.bubble.inRange&&!s||(delete e.bubble.div,delete e.bubble.opacity,delete e.bubble.radius,delete e.bubble.color)}init(){const e=this.container,t=e.actualOptions.interactivity?.modes.bubble;t&&(this.#e=t.distance,e.retina.bubbleModeDistance=t.distance*e.retina.pixelRatio,void 0!==t.size&&(e.retina.bubbleModeSize=t.size*e.retina.pixelRatio))}interact(e,i){const n=this.container.actualOptions,l=n.interactivity?.events;if(!l)return;const a=l.onHover,r=l.onClick,c=a.enable,b=a.mode,u=r.enable,_=r.mode,p=l.onDiv;c&&s.isInArray(o,b)?this.#s(e):u&&s.isInArray(o,_)?this.#i(e):t.divModeExecute(o,p,(t,s)=>{this.#n(e,i,t,s)})}isEnabled(e,i){const n=this.container.actualOptions,l=e.mouse,a=(i?.interactivity??n.interactivity)?.events;if(!a)return!1;const{onClick:r,onDiv:c,onHover:b}=a,u=t.isDivModeEnabled(o,c);return!!(u||b.enable&&l.position||r.enable&&l.clickPosition)&&(s.isInArray(o,b.mode)||s.isInArray(o,r.mode)||u)}loadModeOptions(e,...t){s.loadOptionProperty(e,"bubble",l,...t)}reset(e,t){t.bubble.inRange=!1}#i(e){const t=this.container,i=t.actualOptions,n=e.mouse.clickPosition,l=i.interactivity?.modes.bubble;if(!l||!n)return;t.bubble??={};const r=t.retina.bubbleModeDistance;if(!r||r<0)return;const o=t.particles.grid.queryCircle(n,r,t=>this.isEnabled(e,t)),{bubble:c}=t;for(const i of o){if(!c.clicking)continue;i.bubble.inRange=!c.durationEnd;const o=i.getPosition(),b=s.getDistance(o,n),u=(performance.now()-(e.mouse.clickTime??0))/s.millisecondsToSeconds;u>l.duration&&(c.durationEnd=!0),u>l.duration*s.double&&(c.clicking=!1,c.durationEnd=!1);const _={bubbleObj:{optValue:t.retina.bubbleModeSize,value:i.bubble.radius},particlesObj:{optValue:i.size.max,value:i.size.value},type:a.size};this.#l(i,b,u,_);const p={bubbleObj:{optValue:l.opacity,value:i.bubble.opacity},particlesObj:{optValue:i.opacity?.max??1,value:i.opacity?.value??1},type:a.opacity};this.#l(i,b,u,p),!c.durationEnd&&b<=r?this.#a(i,b):delete i.bubble.color}}#s(e){const i=this.container,n=e.mouse.position,l=i.retina.bubbleModeDistance;if(!l||l<0||!n)return;const a=i.particles.grid.queryCircle(n,l,t=>this.isEnabled(e,t));for(const i of a){i.bubble.inRange=!0;const a=i.getPosition(),r=s.getDistance(a,n),o=1-r/l;r<=l?o>=0&&e.status===t.mouseMoveEvent&&(this.#r(i,o),this.#o(i,o),this.#a(i,o)):this.reset(e,i),e.status===t.mouseLeaveEvent&&this.reset(e,i)}}#a(e,t,i){const n=this.container.actualOptions,l=i??n.interactivity?.modes.bubble;if(l){if(!e.bubble.finalColor){const t=l.color;if(!t)return;const i=s.itemFromSingleOrMultiple(t);e.bubble.finalColor=s.rangeColorToHsl(this.#t,i)}if(e.bubble.finalColor)if(l.mix){e.bubble.color=void 0;const i=e.getFillColor();e.bubble.color=i?s.rgbToHsl(s.colorMix(i,e.bubble.finalColor,1-t,t)):e.bubble.finalColor}else e.bubble.color=e.bubble.finalColor}}#o(e,t,s){const i=this.container.actualOptions,n=s?.opacity??i.interactivity?.modes.bubble?.opacity;if(!n)return;const l=r(e.opacity?.value??1,n,e.opacity?.max??1,t);void 0!==l&&(e.bubble.opacity=l)}#r(e,t,s){const i=this.container,n=s?.size?s.size*i.retina.pixelRatio:i.retina.bubbleModeSize;if(void 0===n)return;const l=r(e.size.value,n,e.size.max,t);void 0!==l&&(e.bubble.radius=l)}#l(e,t,s,i){const n=this.container,l=i.bubbleObj.optValue,r=n.actualOptions,o=r.interactivity?.modes.bubble;if(!o||void 0===l)return;const c=o.duration,b=n.retina.bubbleModeDistance,u=i.particlesObj.optValue,_=i.bubbleObj.value,p=i.particlesObj.value??0,d=i.type;if(b&&!(b<0)&&l!==u)if(n.bubble??={},n.bubble.durationEnd)_&&(d===a.size&&delete e.bubble.radius,d===a.opacity&&delete e.bubble.opacity);else if(t<=b){if((_??p)!==l){const t=p-s*(p-l)/c;d===a.size&&(e.bubble.radius=t),d===a.opacity&&(e.bubble.opacity=t)}}else d===a.size&&delete e.bubble.radius,d===a.opacity&&delete e.bubble.opacity}#n(e,i,n,l){const a=this.container,r=s.safeDocument().querySelectorAll(n),o=a.actualOptions.interactivity?.modes.bubble;o&&r.length&&r.forEach(n=>{const r=n,c=a.retina.pixelRatio,b={x:(r.offsetLeft+r.offsetWidth*s.half)*c,y:(r.offsetTop+r.offsetHeight*s.half)*c},u=r.offsetWidth*s.half*c,_=l.type===t.DivType.circle?new s.Circle(b.x,b.y,u):new s.Rectangle(r.offsetLeft*c,r.offsetTop*c,r.offsetWidth*c,r.offsetHeight*c),p=a.particles.grid.query(_,t=>this.isEnabled(e,t));for(const e of p){if(!_.contains(e.getPosition()))continue;e.bubble.inRange=!0;const s=o.divs,n=t.divMode(s,r);e.bubble.div&&e.bubble.div===r||(this.clear(e,i,!0),e.bubble.div=r),this.#r(e,1,n),this.#o(e,1,n),this.#a(e,1,n)}})}}async function b(e){e.checkVersion("4.2.1"),await e.pluginManager.register(e=>{t.ensureInteractivityPluginLoaded(e),e.pluginManager.addInteractor?.("externalBubble",t=>Promise.resolve(new c(e.pluginManager,t)))})}const u=globalThis;u.__tsParticlesInternals=u.__tsParticlesInternals??{},u.loadExternalBubbleInteraction=b,e.Bubble=l,e.BubbleBase=i,e.BubbleDiv=n,e.loadExternalBubbleInteraction=b}),Object.assign(globalThis.window||globalThis,{loadExternalBubbleInteraction:(globalThis.__tsParticlesInternals.interactions.externalBubble||{}).loadExternalBubbleInteraction}),delete(globalThis.window||globalThis).tsparticlesInternalExports;
1
+ !function(t){t.__tsParticlesInternals=t.__tsParticlesInternals||{},t.__tsParticlesInternals.bundles=t.__tsParticlesInternals.bundles||{},t.__tsParticlesInternals.effects=t.__tsParticlesInternals.effects||{},t.__tsParticlesInternals.engine=t.__tsParticlesInternals.engine||{},t.__tsParticlesInternals.interactions=t.__tsParticlesInternals.interactions||{},t.__tsParticlesInternals.palettes=t.__tsParticlesInternals.palettes||{},t.__tsParticlesInternals.paths=t.__tsParticlesInternals.paths||{},t.__tsParticlesInternals.plugins=t.__tsParticlesInternals.plugins||{},t.__tsParticlesInternals.plugins=t.__tsParticlesInternals.plugins||{},t.__tsParticlesInternals.plugins.emittersShapes=t.__tsParticlesInternals.plugins.emittersShapes||{},t.__tsParticlesInternals.presets=t.__tsParticlesInternals.presets||{},t.__tsParticlesInternals.shapes=t.__tsParticlesInternals.shapes||{},t.__tsParticlesInternals.updaters=t.__tsParticlesInternals.updaters||{},t.__tsParticlesInternals.utils=t.__tsParticlesInternals.utils||{},t.__tsParticlesInternals.canvas=t.__tsParticlesInternals.canvas||{},t.__tsParticlesInternals.canvas=t.__tsParticlesInternals.canvas||{},t.__tsParticlesInternals.canvas.utils=t.__tsParticlesInternals.canvas.utils||{},t.__tsParticlesInternals.path=t.__tsParticlesInternals.path||{},t.__tsParticlesInternals.path=t.__tsParticlesInternals.path||{},t.__tsParticlesInternals.path.utils=t.__tsParticlesInternals.path.utils||{};var e="undefined"!=typeof Proxy?function(t){return new Proxy(t,{get:function(t,e){return e in t||(t[e]={}),t[e]}})}:function(t){return t};t.__tsParticlesInternals.bundles=e(t.__tsParticlesInternals.bundles),t.__tsParticlesInternals.effects=e(t.__tsParticlesInternals.effects),t.__tsParticlesInternals.interactions=e(t.__tsParticlesInternals.interactions),t.__tsParticlesInternals.palettes=e(t.__tsParticlesInternals.palettes),t.__tsParticlesInternals.paths=e(t.__tsParticlesInternals.paths),t.__tsParticlesInternals.plugins=e(t.__tsParticlesInternals.plugins),t.__tsParticlesInternals.plugins.emittersShapes=e(t.__tsParticlesInternals.plugins.emittersShapes),t.__tsParticlesInternals.presets=e(t.__tsParticlesInternals.presets),t.__tsParticlesInternals.shapes=e(t.__tsParticlesInternals.shapes),t.__tsParticlesInternals.updaters=e(t.__tsParticlesInternals.updaters),t.__tsParticlesInternals.utils=e(t.__tsParticlesInternals.utils),t.__tsParticlesInternals.canvas=e(t.__tsParticlesInternals.canvas),t.__tsParticlesInternals.path=e(t.__tsParticlesInternals.path),t.tsparticlesInternalExports=t.tsparticlesInternalExports||{}}("undefined"!=typeof globalThis?globalThis:"undefined"!=typeof window?window:this),function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports,require("@tsparticles/plugin-interactivity"),require("@tsparticles/engine")):"function"==typeof define&&define.amd?define(["exports","@tsparticles/plugin-interactivity","@tsparticles/engine"],e):e(((t="undefined"!=typeof globalThis?globalThis:t||self).__tsParticlesInternals=t.__tsParticlesInternals||{},t.__tsParticlesInternals.interactions=t.__tsParticlesInternals.interactions||{},t.__tsParticlesInternals.interactions.externalBubble=t.__tsParticlesInternals.interactions.externalBubble||{}),t.__tsParticlesInternals.plugins.interactivity,t.__tsParticlesInternals.engine)}(this,function(t,e,s){"use strict";class i{color;distance=200;duration=.4;mix=!1;opacity;size;load(t){if(!s.isNull(t)){if(s.loadProperty(this,"distance",t.distance),s.loadProperty(this,"duration",t.duration),s.loadProperty(this,"mix",t.mix),s.loadRangeProperty(this,"opacity",t.opacity),void 0!==t.color){const e=s.isArray(this.color)?void 0:this.color;this.color=s.executeOnSingleOrMultiple(t.color,t=>s.OptionsColor.create(e,t))}s.loadProperty(this,"size",t.size)}}}class n extends i{selectors=[];load(t){super.load(t),s.isNull(t)||s.loadProperty(this,"selectors",t.selectors)}}class a extends i{divs;load(t){super.load(t),s.isNull(t)||(this.divs=s.executeOnSingleOrMultiple(t.divs,t=>{const e=new n;return e.load(t),e}))}}class r{div;enabled=!1;fillColor;finalColor;id="bubble";inRange=!1;opacity;priority=100;radius;strokeColor}var l;function o(t,e,i,n){if(e>=i){const a=t+(e-i)*n;return s.clamp(a,t,e)}if(e<i){const a=t-(i-e)*n;return s.clamp(a,e,t)}}!function(t){t.color="color",t.opacity="opacity",t.size="size"}(l||(l={}));const c="bubble";class u extends e.ExternalInteractorBase{handleClickMode;#t;#e=new WeakMap;#s;constructor(t,e){super(e),this.#s=t,this.#t=0,e.bubble??={},this.handleClickMode=t=>{t===c&&(e.bubble??={},e.bubble.clicking=!0)}}get maxDistance(){return this.#t}clear(t,e,s){const i=this.#e.get(t);i?.inRange&&!s||(t.removeModifier(c),this.#e.delete(t))}getOrCreateModifier(t){let e=this.#e.get(t);return e||(e=new r,this.#e.set(t,e),t.addModifier(e)),e}init(){const t=this.container,e=t.actualOptions.interactivity?.modes.bubble;e&&(this.#t=e.distance,t.retina.bubbleModeDistance=e.distance*t.retina.pixelRatio,void 0!==e.size&&(t.retina.bubbleModeSize=e.size*t.retina.pixelRatio))}interact(t,i){const n=this.container.actualOptions,a=n.interactivity?.events;if(!a)return;const r=a.onHover,l=a.onClick,o=r.enable,u=r.mode,b=l.enable,d=l.mode,p=a.onDiv;o&&s.isInArray(c,u)?this.#i(t):b&&s.isInArray(c,d)?this.#n(t):e.divModeExecute(c,p,(e,s)=>{this.#a(t,i,e,s)})}isEnabled(t,i){const n=this.container.actualOptions,a=t.mouse,r=(i?.interactivity??n.interactivity)?.events;if(!r)return!1;const{onClick:l,onDiv:o,onHover:u}=r,b=e.isDivModeEnabled(c,o);return!!(b||u.enable&&a.position||l.enable&&a.clickPosition)&&(s.isInArray(c,u.mode)||s.isInArray(c,l.mode)||b)}loadModeOptions(t,...e){s.loadOptionProperty(t,"bubble",a,...e)}reset(t,e){const s=this.#e.get(e);s&&(s.enabled=!1,s.inRange=!1)}#n(t){const e=this.container,i=e.actualOptions,n=t.mouse.clickPosition,a=i.interactivity?.modes.bubble;if(!a||!n)return;e.bubble??={};const r=e.retina.bubbleModeDistance;if(!r||r<0)return;const o=e.particles.grid.queryCircle(n,r,e=>this.isEnabled(t,e)),{bubble:c}=e;for(const i of o){if(!c.clicking)continue;const o=this.getOrCreateModifier(i);o.enabled=!c.durationEnd,o.inRange=!c.durationEnd;const u=i.getPosition(),b=s.getDistance(u,n),d=(performance.now()-(t.mouse.clickTime??0))/s.millisecondsToSeconds;d>a.duration&&(c.durationEnd=!0),d>a.duration*s.double&&(c.clicking=!1,c.durationEnd=!1);const p={bubbleObj:{optValue:e.retina.bubbleModeSize,value:o.radius},particlesObj:{optValue:i.size.max,value:i.size.value},type:l.size};this.#r(i,b,d,p);const _={bubbleObj:{optValue:a.opacity,value:o.opacity},particlesObj:{optValue:i.opacity?.max??1,value:i.opacity?.value??1},type:l.opacity};this.#r(i,b,d,_),!c.durationEnd&&b<=r?this.#l(i,b):(o.fillColor=void 0,o.strokeColor=void 0)}}#i(t){const i=this.container,n=t.mouse.position,a=i.retina.bubbleModeDistance;if(!a||a<0||!n)return;const r=i.particles.grid.queryCircle(n,a,e=>this.isEnabled(t,e));for(const i of r){const r=this.getOrCreateModifier(i);r.enabled=!0,r.inRange=!0;const l=i.getPosition(),o=s.getDistance(l,n),c=1-o/a;o<=a?c>=0&&t.status===e.mouseMoveEvent&&(this.#o(i,c),this.#c(i,c),this.#l(i,c)):this.reset(t,i),t.status===e.mouseLeaveEvent&&this.reset(t,i)}}#l(t,e,i){const n=this.container.actualOptions,a=i??n.interactivity?.modes.bubble,r=this.getOrCreateModifier(t);if(a){if(!r.finalColor){const t=a.color;if(!t)return;const e=s.itemFromSingleOrMultiple(t);r.finalColor=s.rangeColorToHsl(this.#s,e)}if(r.finalColor)if(a.mix){r.fillColor=void 0,r.strokeColor=void 0;const i=t.getFillColor();if(i){const t=s.rgbToHsl(s.colorMix(i,r.finalColor,1-e,e));r.fillColor=t,r.strokeColor=t}else r.fillColor=r.finalColor,r.strokeColor=r.finalColor}else r.fillColor=r.finalColor,r.strokeColor=r.finalColor}}#c(t,e,s){const i=this.container.actualOptions,n=s?.opacity??i.interactivity?.modes.bubble?.opacity;if(!n)return;const a=o(t.opacity?.value??1,n,t.opacity?.max??1,e);if(void 0!==a){this.getOrCreateModifier(t).opacity=a}}#o(t,e,s){const i=this.container,n=s?.size?s.size*i.retina.pixelRatio:i.retina.bubbleModeSize;if(void 0===n)return;const a=o(t.size.value,n,t.size.max,e);if(void 0!==a){this.getOrCreateModifier(t).radius=a}}#r(t,e,s,i){const n=this.container,a=i.bubbleObj.optValue,r=n.actualOptions,o=r.interactivity?.modes.bubble;if(!o||void 0===a)return;const c=o.duration,u=n.retina.bubbleModeDistance,b=i.particlesObj.optValue,d=i.bubbleObj.value,p=i.particlesObj.value??0,_=i.type;if(!u||u<0||a===b)return;n.bubble??={};const f=this.getOrCreateModifier(t);if(n.bubble.durationEnd)d&&(_===l.size&&(f.radius=void 0),_===l.opacity&&(f.opacity=void 0));else if(e<=u){if((d??p)!==a){const t=p-s*(p-a)/c;_===l.size&&(f.radius=t),_===l.opacity&&(f.opacity=t)}}else _===l.size&&(f.radius=void 0),_===l.opacity&&(f.opacity=void 0)}#a(t,i,n,a){const r=this.container,l=s.safeDocument().querySelectorAll(n),o=r.actualOptions.interactivity?.modes.bubble;o&&l.length&&l.forEach(n=>{const l=n,c=r.retina.pixelRatio,u={x:(l.offsetLeft+l.offsetWidth*s.half)*c,y:(l.offsetTop+l.offsetHeight*s.half)*c},b=l.offsetWidth*s.half*c,d=a.type===e.DivType.circle?new s.Circle(u.x,u.y,b):new s.Rectangle(l.offsetLeft*c,l.offsetTop*c,l.offsetWidth*c,l.offsetHeight*c),p=r.particles.grid.query(d,e=>this.isEnabled(t,e));for(const t of p){if(!d.contains(t.getPosition()))continue;const s=this.getOrCreateModifier(t);s.enabled=!0,s.inRange=!0;const n=o.divs,a=e.divMode(n,l);s.div&&s.div===l||(this.clear(t,i,!0),s.div=l),this.#o(t,1,a),this.#c(t,1,a),this.#l(t,1,a)}})}}async function b(t){t.checkVersion("4.3.0"),await t.pluginManager.register(t=>{e.ensureInteractivityPluginLoaded(t),t.pluginManager.addInteractor?.("externalBubble",e=>Promise.resolve(new u(t.pluginManager,e)))})}const d=globalThis;d.__tsParticlesInternals=d.__tsParticlesInternals??{},d.loadExternalBubbleInteraction=b,t.Bubble=a,t.BubbleBase=i,t.BubbleDiv=n,t.BubbleModifier=r,t.loadExternalBubbleInteraction=b}),Object.assign(globalThis.window||globalThis,{loadExternalBubbleInteraction:(globalThis.__tsParticlesInternals.interactions.externalBubble||{}).loadExternalBubbleInteraction}),delete(globalThis.window||globalThis).tsparticlesInternalExports;
@@ -0,0 +1,13 @@
1
+ import type { IHsl, IParticleModifier } from "@tsparticles/engine";
2
+ export declare class BubbleModifier implements IParticleModifier {
3
+ div?: HTMLElement;
4
+ enabled: boolean;
5
+ fillColor?: IHsl;
6
+ finalColor?: IHsl;
7
+ readonly id = "bubble";
8
+ inRange: boolean;
9
+ opacity?: number;
10
+ readonly priority = 100;
11
+ radius?: number;
12
+ strokeColor?: IHsl;
13
+ }
@@ -1,12 +1,14 @@
1
1
  import type { BubbleContainer, BubbleMode, IBubbleMode } from "./Types.js";
2
2
  import { type IDelta, type Particle, type PluginManager, type RecursivePartial } from "@tsparticles/engine";
3
3
  import { ExternalInteractorBase, type IInteractivityData, type IModes, type InteractivityParticle, type Modes } from "@tsparticles/plugin-interactivity";
4
+ import { BubbleModifier } from "./BubbleModifier.js";
4
5
  export declare class Bubbler extends ExternalInteractorBase<BubbleContainer> {
5
6
  #private;
6
7
  handleClickMode: (mode: string, interactivityData: IInteractivityData) => void;
7
8
  constructor(pluginManager: PluginManager, container: BubbleContainer);
8
9
  get maxDistance(): number;
9
10
  clear(particle: Particle, _delta: IDelta, force?: boolean): void;
11
+ getOrCreateModifier(particle: Particle): BubbleModifier;
10
12
  init(): void;
11
13
  interact(interactivityData: IInteractivityData, delta: IDelta): void;
12
14
  isEnabled(interactivityData: IInteractivityData, particle?: InteractivityParticle): boolean;
package/types/index.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { type Engine } from "@tsparticles/engine";
2
2
  export declare function loadExternalBubbleInteraction(engine: Engine): Promise<void>;
3
+ export * from "./BubbleModifier.js";
3
4
  export * from "./Options/Classes/BubbleBase.js";
4
5
  export * from "./Options/Classes/BubbleDiv.js";
5
6
  export * from "./Options/Classes/Bubble.js";