@tsparticles/interaction-external-bubble 4.0.0-alpha.8 → 4.0.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/esm/Bubbler.js CHANGED
@@ -5,207 +5,13 @@ import { ProcessBubbleType } from "./Enums.js";
5
5
  import { calculateBubbleValue } from "./Utils.js";
6
6
  const bubbleMode = "bubble", minDistance = 0, defaultClickTime = 0, defaultOpacity = 1, ratioOffset = 1, defaultBubbleValue = 0, minRatio = 0, defaultRatio = 1;
7
7
  export class Bubbler extends ExternalInteractorBase {
8
- constructor(engine, container) {
8
+ handleClickMode;
9
+ _maxDistance;
10
+ _pluginManager;
11
+ constructor(pluginManager, container) {
9
12
  super(container);
10
- this._clickBubble = interactivityData => {
11
- const container = this.container, options = container.actualOptions, mouseClickPos = interactivityData.mouse.clickPosition, bubbleOptions = options.interactivity?.modes.bubble;
12
- if (!bubbleOptions || !mouseClickPos) {
13
- return;
14
- }
15
- container.bubble ??= {};
16
- const distance = container.retina.bubbleModeDistance;
17
- if (!distance || distance < minDistance) {
18
- return;
19
- }
20
- const query = container.particles.quadTree.queryCircle(mouseClickPos, distance, p => this.isEnabled(interactivityData, p)), { bubble } = container;
21
- for (const particle of query) {
22
- if (!bubble.clicking) {
23
- continue;
24
- }
25
- particle.bubble.inRange = !bubble.durationEnd;
26
- const pos = particle.getPosition(), distMouse = getDistance(pos, mouseClickPos), timeSpent = (new Date().getTime() - (interactivityData.mouse.clickTime ?? defaultClickTime)) / millisecondsToSeconds;
27
- if (timeSpent > bubbleOptions.duration) {
28
- bubble.durationEnd = true;
29
- }
30
- if (timeSpent > bubbleOptions.duration * double) {
31
- bubble.clicking = false;
32
- bubble.durationEnd = false;
33
- }
34
- const sizeData = {
35
- bubbleObj: {
36
- optValue: container.retina.bubbleModeSize,
37
- value: particle.bubble.radius,
38
- },
39
- particlesObj: {
40
- optValue: getRangeMax(particle.options.size.value) * container.retina.pixelRatio,
41
- value: particle.size.value,
42
- },
43
- type: ProcessBubbleType.size,
44
- };
45
- this._process(particle, distMouse, timeSpent, sizeData);
46
- const opacityData = {
47
- bubbleObj: {
48
- optValue: bubbleOptions.opacity,
49
- value: particle.bubble.opacity,
50
- },
51
- particlesObj: {
52
- optValue: getRangeMax(particle.options.opacity.value),
53
- value: particle.opacity?.value ?? defaultOpacity,
54
- },
55
- type: ProcessBubbleType.opacity,
56
- };
57
- this._process(particle, distMouse, timeSpent, opacityData);
58
- if (!bubble.durationEnd && distMouse <= distance) {
59
- this._hoverBubbleColor(particle, distMouse);
60
- }
61
- else {
62
- delete particle.bubble.color;
63
- }
64
- }
65
- };
66
- this._hoverBubble = interactivityData => {
67
- const container = this.container, mousePos = interactivityData.mouse.position, distance = container.retina.bubbleModeDistance;
68
- if (!distance || distance < minDistance || !mousePos) {
69
- return;
70
- }
71
- const query = container.particles.quadTree.queryCircle(mousePos, distance, p => this.isEnabled(interactivityData, p));
72
- for (const particle of query) {
73
- particle.bubble.inRange = true;
74
- const pos = particle.getPosition(), pointDistance = getDistance(pos, mousePos), ratio = ratioOffset - pointDistance / distance;
75
- if (pointDistance <= distance) {
76
- if (ratio >= minRatio && interactivityData.status === mouseMoveEvent) {
77
- this._hoverBubbleSize(particle, ratio);
78
- this._hoverBubbleOpacity(particle, ratio);
79
- this._hoverBubbleColor(particle, ratio);
80
- }
81
- }
82
- else {
83
- this.reset(interactivityData, particle);
84
- }
85
- if (interactivityData.status === mouseLeaveEvent) {
86
- this.reset(interactivityData, particle);
87
- }
88
- }
89
- };
90
- this._hoverBubbleColor = (particle, ratio, divBubble) => {
91
- const options = this.container.actualOptions, bubbleOptions = divBubble ?? options.interactivity?.modes.bubble;
92
- if (!bubbleOptions) {
93
- return;
94
- }
95
- if (!particle.bubble.finalColor) {
96
- const modeColor = bubbleOptions.color;
97
- if (!modeColor) {
98
- return;
99
- }
100
- const bubbleColor = itemFromSingleOrMultiple(modeColor);
101
- particle.bubble.finalColor = rangeColorToHsl(this._engine, bubbleColor);
102
- }
103
- if (!particle.bubble.finalColor) {
104
- return;
105
- }
106
- if (bubbleOptions.mix) {
107
- particle.bubble.color = undefined;
108
- const pColor = particle.getFillColor();
109
- particle.bubble.color = pColor
110
- ? rgbToHsl(colorMix(pColor, particle.bubble.finalColor, ratioOffset - ratio, ratio))
111
- : particle.bubble.finalColor;
112
- }
113
- else {
114
- particle.bubble.color = particle.bubble.finalColor;
115
- }
116
- };
117
- this._hoverBubbleOpacity = (particle, ratio, divBubble) => {
118
- const container = this.container, options = container.actualOptions, modeOpacity = divBubble?.opacity ?? options.interactivity?.modes.bubble?.opacity;
119
- if (!modeOpacity) {
120
- return;
121
- }
122
- const optOpacity = particle.options.opacity.value, pOpacity = particle.opacity?.value ?? defaultOpacity, opacity = calculateBubbleValue(pOpacity, modeOpacity, getRangeMax(optOpacity), ratio);
123
- if (opacity !== undefined) {
124
- particle.bubble.opacity = opacity;
125
- }
126
- };
127
- this._hoverBubbleSize = (particle, ratio, divBubble) => {
128
- const container = this.container, modeSize = divBubble?.size ? divBubble.size * container.retina.pixelRatio : container.retina.bubbleModeSize;
129
- if (modeSize === undefined) {
130
- return;
131
- }
132
- const optSize = getRangeMax(particle.options.size.value) * container.retina.pixelRatio, pSize = particle.size.value, size = calculateBubbleValue(pSize, modeSize, optSize, ratio);
133
- if (size !== undefined) {
134
- particle.bubble.radius = size;
135
- }
136
- };
137
- this._process = (particle, distMouse, timeSpent, data) => {
138
- const container = this.container, bubbleParam = data.bubbleObj.optValue, options = container.actualOptions, bubbleOptions = options.interactivity?.modes.bubble;
139
- if (!bubbleOptions || bubbleParam === undefined) {
140
- return;
141
- }
142
- const bubbleDuration = bubbleOptions.duration, bubbleDistance = container.retina.bubbleModeDistance, particlesParam = data.particlesObj.optValue, pObjBubble = data.bubbleObj.value, pObj = data.particlesObj.value ?? defaultBubbleValue, type = data.type;
143
- if (!bubbleDistance || bubbleDistance < minDistance || bubbleParam === particlesParam) {
144
- return;
145
- }
146
- container.bubble ??= {};
147
- if (container.bubble.durationEnd) {
148
- if (pObjBubble) {
149
- if (type === ProcessBubbleType.size) {
150
- delete particle.bubble.radius;
151
- }
152
- if (type === ProcessBubbleType.opacity) {
153
- delete particle.bubble.opacity;
154
- }
155
- }
156
- }
157
- else {
158
- if (distMouse <= bubbleDistance) {
159
- const obj = pObjBubble ?? pObj;
160
- if (obj !== bubbleParam) {
161
- const value = pObj - (timeSpent * (pObj - bubbleParam)) / bubbleDuration;
162
- if (type === ProcessBubbleType.size) {
163
- particle.bubble.radius = value;
164
- }
165
- if (type === ProcessBubbleType.opacity) {
166
- particle.bubble.opacity = value;
167
- }
168
- }
169
- }
170
- else {
171
- if (type === ProcessBubbleType.size) {
172
- delete particle.bubble.radius;
173
- }
174
- if (type === ProcessBubbleType.opacity) {
175
- delete particle.bubble.opacity;
176
- }
177
- }
178
- }
179
- };
180
- this._singleSelectorHover = (interactivityData, delta, selector, div) => {
181
- const container = this.container, selectors = safeDocument().querySelectorAll(selector), bubble = container.actualOptions.interactivity?.modes.bubble;
182
- if (!bubble || !selectors.length) {
183
- return;
184
- }
185
- selectors.forEach(item => {
186
- const elem = item, pxRatio = container.retina.pixelRatio, pos = {
187
- x: (elem.offsetLeft + elem.offsetWidth * half) * pxRatio,
188
- y: (elem.offsetTop + elem.offsetHeight * half) * pxRatio,
189
- }, repulseRadius = elem.offsetWidth * half * pxRatio, area = div.type === DivType.circle
190
- ? new Circle(pos.x, pos.y, repulseRadius)
191
- : new Rectangle(elem.offsetLeft * pxRatio, elem.offsetTop * pxRatio, elem.offsetWidth * pxRatio, elem.offsetHeight * pxRatio), query = container.particles.quadTree.query(area, p => this.isEnabled(interactivityData, p));
192
- for (const particle of query) {
193
- if (!area.contains(particle.getPosition())) {
194
- continue;
195
- }
196
- particle.bubble.inRange = true;
197
- const divs = bubble.divs, divBubble = divMode(divs, elem);
198
- if (!particle.bubble.div || particle.bubble.div !== elem) {
199
- this.clear(particle, delta, true);
200
- particle.bubble.div = elem;
201
- }
202
- this._hoverBubbleSize(particle, defaultRatio, divBubble);
203
- this._hoverBubbleOpacity(particle, defaultRatio, divBubble);
204
- this._hoverBubbleColor(particle, defaultRatio, divBubble);
205
- }
206
- });
207
- };
208
- this._engine = engine;
13
+ this._pluginManager = pluginManager;
14
+ this._maxDistance = 0;
209
15
  container.bubble ??= {};
210
16
  this.handleClickMode = (mode) => {
211
17
  if (mode !== bubbleMode) {
@@ -215,6 +21,9 @@ export class Bubbler extends ExternalInteractorBase {
215
21
  container.bubble.clicking = true;
216
22
  };
217
23
  }
24
+ get maxDistance() {
25
+ return this._maxDistance;
26
+ }
218
27
  clear(particle, _delta, force) {
219
28
  if (particle.bubble.inRange && !force) {
220
29
  return;
@@ -229,6 +38,7 @@ export class Bubbler extends ExternalInteractorBase {
229
38
  if (!bubble) {
230
39
  return;
231
40
  }
41
+ this._maxDistance = bubble.distance;
232
42
  container.retina.bubbleModeDistance = bubble.distance * container.retina.pixelRatio;
233
43
  if (bubble.size !== undefined) {
234
44
  container.retina.bubbleModeSize = bubble.size * container.retina.pixelRatio;
@@ -272,4 +82,202 @@ export class Bubbler extends ExternalInteractorBase {
272
82
  reset(_interactivityData, particle) {
273
83
  particle.bubble.inRange = false;
274
84
  }
85
+ _clickBubble = interactivityData => {
86
+ const container = this.container, options = container.actualOptions, mouseClickPos = interactivityData.mouse.clickPosition, bubbleOptions = options.interactivity?.modes.bubble;
87
+ if (!bubbleOptions || !mouseClickPos) {
88
+ return;
89
+ }
90
+ container.bubble ??= {};
91
+ const distance = container.retina.bubbleModeDistance;
92
+ if (!distance || distance < minDistance) {
93
+ return;
94
+ }
95
+ const query = container.particles.grid.queryCircle(mouseClickPos, distance, p => this.isEnabled(interactivityData, p)), { bubble } = container;
96
+ for (const particle of query) {
97
+ if (!bubble.clicking) {
98
+ continue;
99
+ }
100
+ particle.bubble.inRange = !bubble.durationEnd;
101
+ const pos = particle.getPosition(), distMouse = getDistance(pos, mouseClickPos), timeSpent = (performance.now() - (interactivityData.mouse.clickTime ?? defaultClickTime)) / millisecondsToSeconds;
102
+ if (timeSpent > bubbleOptions.duration) {
103
+ bubble.durationEnd = true;
104
+ }
105
+ if (timeSpent > bubbleOptions.duration * double) {
106
+ bubble.clicking = false;
107
+ bubble.durationEnd = false;
108
+ }
109
+ const sizeData = {
110
+ bubbleObj: {
111
+ optValue: container.retina.bubbleModeSize,
112
+ value: particle.bubble.radius,
113
+ },
114
+ particlesObj: {
115
+ optValue: getRangeMax(particle.options.size.value) * container.retina.pixelRatio,
116
+ value: particle.size.value,
117
+ },
118
+ type: ProcessBubbleType.size,
119
+ };
120
+ this._process(particle, distMouse, timeSpent, sizeData);
121
+ const opacityData = {
122
+ bubbleObj: {
123
+ optValue: bubbleOptions.opacity,
124
+ value: particle.bubble.opacity,
125
+ },
126
+ particlesObj: {
127
+ optValue: getRangeMax(particle.options.opacity.value),
128
+ value: particle.opacity?.value ?? defaultOpacity,
129
+ },
130
+ type: ProcessBubbleType.opacity,
131
+ };
132
+ this._process(particle, distMouse, timeSpent, opacityData);
133
+ if (!bubble.durationEnd && distMouse <= distance) {
134
+ this._hoverBubbleColor(particle, distMouse);
135
+ }
136
+ else {
137
+ delete particle.bubble.color;
138
+ }
139
+ }
140
+ };
141
+ _hoverBubble = interactivityData => {
142
+ const container = this.container, mousePos = interactivityData.mouse.position, distance = container.retina.bubbleModeDistance;
143
+ if (!distance || distance < minDistance || !mousePos) {
144
+ return;
145
+ }
146
+ const query = container.particles.grid.queryCircle(mousePos, distance, p => this.isEnabled(interactivityData, p));
147
+ for (const particle of query) {
148
+ particle.bubble.inRange = true;
149
+ const pos = particle.getPosition(), pointDistance = getDistance(pos, mousePos), ratio = ratioOffset - pointDistance / distance;
150
+ if (pointDistance <= distance) {
151
+ if (ratio >= minRatio && interactivityData.status === mouseMoveEvent) {
152
+ this._hoverBubbleSize(particle, ratio);
153
+ this._hoverBubbleOpacity(particle, ratio);
154
+ this._hoverBubbleColor(particle, ratio);
155
+ }
156
+ }
157
+ else {
158
+ this.reset(interactivityData, particle);
159
+ }
160
+ if (interactivityData.status === mouseLeaveEvent) {
161
+ this.reset(interactivityData, particle);
162
+ }
163
+ }
164
+ };
165
+ _hoverBubbleColor = (particle, ratio, divBubble) => {
166
+ const options = this.container.actualOptions, bubbleOptions = divBubble ?? options.interactivity?.modes.bubble;
167
+ if (!bubbleOptions) {
168
+ return;
169
+ }
170
+ if (!particle.bubble.finalColor) {
171
+ const modeColor = bubbleOptions.color;
172
+ if (!modeColor) {
173
+ return;
174
+ }
175
+ const bubbleColor = itemFromSingleOrMultiple(modeColor);
176
+ particle.bubble.finalColor = rangeColorToHsl(this._pluginManager, bubbleColor);
177
+ }
178
+ if (!particle.bubble.finalColor) {
179
+ return;
180
+ }
181
+ if (bubbleOptions.mix) {
182
+ particle.bubble.color = undefined;
183
+ const pColor = particle.getFillColor();
184
+ particle.bubble.color = pColor
185
+ ? rgbToHsl(colorMix(pColor, particle.bubble.finalColor, ratioOffset - ratio, ratio))
186
+ : particle.bubble.finalColor;
187
+ }
188
+ else {
189
+ particle.bubble.color = particle.bubble.finalColor;
190
+ }
191
+ };
192
+ _hoverBubbleOpacity = (particle, ratio, divBubble) => {
193
+ const container = this.container, options = container.actualOptions, modeOpacity = divBubble?.opacity ?? options.interactivity?.modes.bubble?.opacity;
194
+ if (!modeOpacity) {
195
+ return;
196
+ }
197
+ const optOpacity = particle.options.opacity.value, pOpacity = particle.opacity?.value ?? defaultOpacity, opacity = calculateBubbleValue(pOpacity, modeOpacity, getRangeMax(optOpacity), ratio);
198
+ if (opacity !== undefined) {
199
+ particle.bubble.opacity = opacity;
200
+ }
201
+ };
202
+ _hoverBubbleSize = (particle, ratio, divBubble) => {
203
+ const container = this.container, modeSize = divBubble?.size ? divBubble.size * container.retina.pixelRatio : container.retina.bubbleModeSize;
204
+ if (modeSize === undefined) {
205
+ return;
206
+ }
207
+ const optSize = getRangeMax(particle.options.size.value) * container.retina.pixelRatio, pSize = particle.size.value, size = calculateBubbleValue(pSize, modeSize, optSize, ratio);
208
+ if (size !== undefined) {
209
+ particle.bubble.radius = size;
210
+ }
211
+ };
212
+ _process = (particle, distMouse, timeSpent, data) => {
213
+ const container = this.container, bubbleParam = data.bubbleObj.optValue, options = container.actualOptions, bubbleOptions = options.interactivity?.modes.bubble;
214
+ if (!bubbleOptions || bubbleParam === undefined) {
215
+ return;
216
+ }
217
+ const bubbleDuration = bubbleOptions.duration, bubbleDistance = container.retina.bubbleModeDistance, particlesParam = data.particlesObj.optValue, pObjBubble = data.bubbleObj.value, pObj = data.particlesObj.value ?? defaultBubbleValue, type = data.type;
218
+ if (!bubbleDistance || bubbleDistance < minDistance || bubbleParam === particlesParam) {
219
+ return;
220
+ }
221
+ container.bubble ??= {};
222
+ if (container.bubble.durationEnd) {
223
+ if (pObjBubble) {
224
+ if (type === ProcessBubbleType.size) {
225
+ delete particle.bubble.radius;
226
+ }
227
+ if (type === ProcessBubbleType.opacity) {
228
+ delete particle.bubble.opacity;
229
+ }
230
+ }
231
+ }
232
+ else {
233
+ if (distMouse <= bubbleDistance) {
234
+ const obj = pObjBubble ?? pObj;
235
+ if (obj !== bubbleParam) {
236
+ const value = pObj - (timeSpent * (pObj - bubbleParam)) / bubbleDuration;
237
+ if (type === ProcessBubbleType.size) {
238
+ particle.bubble.radius = value;
239
+ }
240
+ if (type === ProcessBubbleType.opacity) {
241
+ particle.bubble.opacity = value;
242
+ }
243
+ }
244
+ }
245
+ else {
246
+ if (type === ProcessBubbleType.size) {
247
+ delete particle.bubble.radius;
248
+ }
249
+ if (type === ProcessBubbleType.opacity) {
250
+ delete particle.bubble.opacity;
251
+ }
252
+ }
253
+ }
254
+ };
255
+ _singleSelectorHover = (interactivityData, delta, selector, div) => {
256
+ const container = this.container, selectors = safeDocument().querySelectorAll(selector), bubble = container.actualOptions.interactivity?.modes.bubble;
257
+ if (!bubble || !selectors.length) {
258
+ return;
259
+ }
260
+ selectors.forEach(item => {
261
+ const elem = item, pxRatio = container.retina.pixelRatio, pos = {
262
+ x: (elem.offsetLeft + elem.offsetWidth * half) * pxRatio,
263
+ y: (elem.offsetTop + elem.offsetHeight * half) * pxRatio,
264
+ }, repulseRadius = elem.offsetWidth * half * pxRatio, area = div.type === DivType.circle
265
+ ? new Circle(pos.x, pos.y, repulseRadius)
266
+ : new Rectangle(elem.offsetLeft * pxRatio, elem.offsetTop * pxRatio, elem.offsetWidth * pxRatio, elem.offsetHeight * pxRatio), query = container.particles.grid.query(area, p => this.isEnabled(interactivityData, p));
267
+ for (const particle of query) {
268
+ if (!area.contains(particle.getPosition())) {
269
+ continue;
270
+ }
271
+ particle.bubble.inRange = true;
272
+ const divs = bubble.divs, divBubble = divMode(divs, elem);
273
+ if (!particle.bubble.div || particle.bubble.div !== elem) {
274
+ this.clear(particle, delta, true);
275
+ particle.bubble.div = elem;
276
+ }
277
+ this._hoverBubbleSize(particle, defaultRatio, divBubble);
278
+ this._hoverBubbleOpacity(particle, defaultRatio, divBubble);
279
+ this._hoverBubbleColor(particle, defaultRatio, divBubble);
280
+ }
281
+ });
282
+ };
275
283
  }
@@ -2,6 +2,7 @@ import { executeOnSingleOrMultiple, isNull, } from "@tsparticles/engine";
2
2
  import { BubbleBase } from "./BubbleBase.js";
3
3
  import { BubbleDiv } from "./BubbleDiv.js";
4
4
  export class Bubble extends BubbleBase {
5
+ divs;
5
6
  load(data) {
6
7
  super.load(data);
7
8
  if (isNull(data)) {
@@ -1,5 +1,11 @@
1
1
  import { OptionsColor, executeOnSingleOrMultiple, isArray, isNull, } from "@tsparticles/engine";
2
2
  export class BubbleBase {
3
+ color;
4
+ distance;
5
+ duration;
6
+ mix;
7
+ opacity;
8
+ size;
3
9
  constructor() {
4
10
  this.distance = 200;
5
11
  this.duration = 0.4;
@@ -1,6 +1,7 @@
1
1
  import { isNull } from "@tsparticles/engine";
2
2
  import { BubbleBase } from "./BubbleBase.js";
3
3
  export class BubbleDiv extends BubbleBase {
4
+ selectors;
4
5
  constructor() {
5
6
  super();
6
7
  this.selectors = [];
package/esm/index.js CHANGED
@@ -1,11 +1,11 @@
1
1
  export async function loadExternalBubbleInteraction(engine) {
2
- engine.checkVersion("4.0.0-alpha.8");
3
- await engine.register(async (e) => {
4
- const { loadInteractivityPlugin } = await import("@tsparticles/plugin-interactivity");
5
- await loadInteractivityPlugin(e);
6
- e.addInteractor?.("externalBubble", async (container) => {
2
+ engine.checkVersion("4.0.0-beta.1");
3
+ await engine.pluginManager.register(async (e) => {
4
+ const { ensureInteractivityPluginLoaded } = await import("@tsparticles/plugin-interactivity");
5
+ ensureInteractivityPluginLoaded(e);
6
+ e.pluginManager.addInteractor?.("externalBubble", async (container) => {
7
7
  const { Bubbler } = await import("./Bubbler.js");
8
- return new Bubbler(e, container);
8
+ return new Bubbler(e.pluginManager, container);
9
9
  });
10
10
  });
11
11
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tsparticles/interaction-external-bubble",
3
- "version": "4.0.0-alpha.8",
3
+ "version": "4.0.0-beta.1",
4
4
  "description": "tsParticles bubble external interaction",
5
5
  "homepage": "https://particles.js.org",
6
6
  "repository": {
@@ -87,8 +87,8 @@
87
87
  "./package.json": "./package.json"
88
88
  },
89
89
  "dependencies": {
90
- "@tsparticles/engine": "4.0.0-alpha.8",
91
- "@tsparticles/plugin-interactivity": "4.0.0-alpha.8"
90
+ "@tsparticles/engine": "4.0.0-beta.1",
91
+ "@tsparticles/plugin-interactivity": "4.0.0-beta.1"
92
92
  },
93
93
  "publishConfig": {
94
94
  "access": "public"