@tsparticles/interaction-external-bubble 4.0.0-alpha.5 → 4.0.0-beta.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.
package/537.min.js ADDED
@@ -0,0 +1 @@
1
+ "use strict";(this.webpackChunk_tsparticles_interaction_external_bubble=this.webpackChunk_tsparticles_interaction_external_bubble||[]).push([[537],{537(e,i,t){t.d(i,{Bubbler:()=>u});var l,b,o=t(303),a=t(702),n=t(767);function r(e,i,t,l){return i>=t?(0,o.clamp)(e+(i-t)*l,e,i):i<t?(0,o.clamp)(e-(t-i)*l,i,e):void 0}(l=b||(b={})).color="color",l.opacity="opacity",l.size="size";let s="bubble";class u extends a.ExternalInteractorBase{handleClickMode;_engine;_maxDistance;constructor(e,i){super(i),this._engine=e,this._maxDistance=0,i.bubble??={},this.handleClickMode=e=>{e===s&&(i.bubble??={},i.bubble.clicking=!0)}}get maxDistance(){return this._maxDistance}clear(e,i,t){(!e.bubble.inRange||t)&&(delete e.bubble.div,delete e.bubble.opacity,delete e.bubble.radius,delete e.bubble.color)}init(){let e=this.container,i=e.actualOptions.interactivity?.modes.bubble;i&&(this._maxDistance=i.distance,e.retina.bubbleModeDistance=i.distance*e.retina.pixelRatio,void 0!==i.size&&(e.retina.bubbleModeSize=i.size*e.retina.pixelRatio))}interact(e,i){let t=this.container.actualOptions,l=t.interactivity?.events;if(!l)return;let b=l.onHover,n=l.onClick,r=b.enable,u=b.mode,c=n.enable,d=n.mode,p=l.onDiv;r&&(0,o.isInArray)(s,u)?this._hoverBubble(e):c&&(0,o.isInArray)(s,d)?this._clickBubble(e):(0,a.divModeExecute)(s,p,(t,l)=>{this._singleSelectorHover(e,i,t,l)})}isEnabled(e,i){let t=this.container.actualOptions,l=e.mouse,b=(i?.interactivity??t.interactivity)?.events;if(!b)return!1;let{onClick:n,onDiv:r,onHover:u}=b,c=(0,a.isDivModeEnabled)(s,r);return!!(c||u.enable&&l.position||n.enable&&l.clickPosition)&&((0,o.isInArray)(s,u.mode)||(0,o.isInArray)(s,n.mode)||c)}loadModeOptions(e,...i){for(let t of(e.bubble??=new n.Z,i))e.bubble.load(t?.bubble)}reset(e,i){i.bubble.inRange=!1}_clickBubble=e=>{let i=this.container,t=i.actualOptions,l=e.mouse.clickPosition,a=t.interactivity?.modes.bubble;if(!a||!l)return;i.bubble??={};let n=i.retina.bubbleModeDistance;if(!n||n<0)return;let r=i.particles.grid.queryCircle(l,n,i=>this.isEnabled(e,i)),{bubble:s}=i;for(let t of r){if(!s.clicking)continue;t.bubble.inRange=!s.durationEnd;let r=t.getPosition(),u=(0,o.getDistance)(r,l),c=(performance.now()-(e.mouse.clickTime??0))/o.millisecondsToSeconds;c>a.duration&&(s.durationEnd=!0),c>a.duration*o.double&&(s.clicking=!1,s.durationEnd=!1);let d={bubbleObj:{optValue:i.retina.bubbleModeSize,value:t.bubble.radius},particlesObj:{optValue:(0,o.getRangeMax)(t.options.size.value)*i.retina.pixelRatio,value:t.size.value},type:b.size};this._process(t,u,c,d);let p={bubbleObj:{optValue:a.opacity,value:t.bubble.opacity},particlesObj:{optValue:(0,o.getRangeMax)(t.options.opacity.value),value:t.opacity?.value??1},type:b.opacity};this._process(t,u,c,p),!s.durationEnd&&u<=n?this._hoverBubbleColor(t,u):delete t.bubble.color}};_hoverBubble=e=>{let i=this.container,t=e.mouse.position,l=i.retina.bubbleModeDistance;if(l&&!(l<0)&&t)for(let b of i.particles.grid.queryCircle(t,l,i=>this.isEnabled(e,i))){b.bubble.inRange=!0;let i=b.getPosition(),n=(0,o.getDistance)(i,t),r=1-n/l;n<=l?r>=0&&e.status===a.mouseMoveEvent&&(this._hoverBubbleSize(b,r),this._hoverBubbleOpacity(b,r),this._hoverBubbleColor(b,r)):this.reset(e,b),e.status===a.mouseLeaveEvent&&this.reset(e,b)}};_hoverBubbleColor=(e,i,t)=>{let l=this.container.actualOptions,b=t??l.interactivity?.modes.bubble;if(b){if(!e.bubble.finalColor){let i=b.color;if(!i)return;let t=(0,o.itemFromSingleOrMultiple)(i);e.bubble.finalColor=(0,o.rangeColorToHsl)(this._engine,t)}if(e.bubble.finalColor)if(b.mix){e.bubble.color=void 0;let t=e.getFillColor();e.bubble.color=t?(0,o.rgbToHsl)((0,o.colorMix)(t,e.bubble.finalColor,1-i,i)):e.bubble.finalColor}else e.bubble.color=e.bubble.finalColor}};_hoverBubbleOpacity=(e,i,t)=>{let l=this.container.actualOptions,b=t?.opacity??l.interactivity?.modes.bubble?.opacity;if(!b)return;let a=e.options.opacity.value,n=r(e.opacity?.value??1,b,(0,o.getRangeMax)(a),i);void 0!==n&&(e.bubble.opacity=n)};_hoverBubbleSize=(e,i,t)=>{let l=this.container,b=t?.size?t.size*l.retina.pixelRatio:l.retina.bubbleModeSize;if(void 0===b)return;let a=(0,o.getRangeMax)(e.options.size.value)*l.retina.pixelRatio,n=r(e.size.value,b,a,i);void 0!==n&&(e.bubble.radius=n)};_process=(e,i,t,l)=>{let o=this.container,a=l.bubbleObj.optValue,n=o.actualOptions,r=n.interactivity?.modes.bubble;if(!r||void 0===a)return;let s=r.duration,u=o.retina.bubbleModeDistance,c=l.particlesObj.optValue,d=l.bubbleObj.value,p=l.particlesObj.value??0,h=l.type;if(u&&!(u<0)&&a!==c)if(o.bubble??={},o.bubble.durationEnd)d&&(h===b.size&&delete e.bubble.radius,h===b.opacity&&delete e.bubble.opacity);else if(i<=u){if((d??p)!==a){let i=p-t*(p-a)/s;h===b.size&&(e.bubble.radius=i),h===b.opacity&&(e.bubble.opacity=i)}}else h===b.size&&delete e.bubble.radius,h===b.opacity&&delete e.bubble.opacity};_singleSelectorHover=(e,i,t,l)=>{let b=this.container,n=(0,o.safeDocument)().querySelectorAll(t),r=b.actualOptions.interactivity?.modes.bubble;r&&n.length&&n.forEach(t=>{let n=b.retina.pixelRatio,s={x:(t.offsetLeft+t.offsetWidth*o.half)*n,y:(t.offsetTop+t.offsetHeight*o.half)*n},u=t.offsetWidth*o.half*n,c=l.type===a.DivType.circle?new o.Circle(s.x,s.y,u):new o.Rectangle(t.offsetLeft*n,t.offsetTop*n,t.offsetWidth*n,t.offsetHeight*n);for(let l of b.particles.grid.query(c,i=>this.isEnabled(e,i))){if(!c.contains(l.getPosition()))continue;l.bubble.inRange=!0;let e=r.divs,b=(0,a.divMode)(e,t);l.bubble.div&&l.bubble.div===t||(this.clear(l,i,!0),l.bubble.div=t),this._hoverBubbleSize(l,1,b),this._hoverBubbleOpacity(l,1,b),this._hoverBubbleColor(l,1,b)}})}}}}]);
package/README.md CHANGED
@@ -28,6 +28,7 @@ Once the scripts are loaded you can set up `tsParticles` and the interaction plu
28
28
 
29
29
  ```javascript
30
30
  (async () => {
31
+ await loadInteractivityPlugin(tsParticles);
31
32
  await loadExternalBubbleInteraction(tsParticles);
32
33
 
33
34
  await tsParticles.load({
@@ -57,9 +58,11 @@ Then you need to import it in the app, like this:
57
58
 
58
59
  ```javascript
59
60
  const { tsParticles } = require("@tsparticles/engine");
61
+ const { loadInteractivityPlugin } = require("@tsparticles/plugin-interactivity");
60
62
  const { loadExternalBubbleInteraction } = require("@tsparticles/interaction-external-bubble");
61
63
 
62
64
  (async () => {
65
+ await loadInteractivityPlugin(tsParticles);
63
66
  await loadExternalBubbleInteraction(tsParticles);
64
67
  })();
65
68
  ```
@@ -68,9 +71,11 @@ or
68
71
 
69
72
  ```javascript
70
73
  import { tsParticles } from "@tsparticles/engine";
74
+ import { loadInteractivityPlugin } from "@tsparticles/plugin-interactivity";
71
75
  import { loadExternalBubbleInteraction } from "@tsparticles/interaction-external-bubble";
72
76
 
73
77
  (async () => {
78
+ await loadInteractivityPlugin(tsParticles);
74
79
  await loadExternalBubbleInteraction(tsParticles);
75
80
  })();
76
81
  ```
@@ -5,208 +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
+ handleClickMode;
9
+ _engine;
10
+ _maxDistance;
8
11
  constructor(engine, 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)) /
27
- millisecondsToSeconds;
28
- if (timeSpent > bubbleOptions.duration) {
29
- bubble.durationEnd = true;
30
- }
31
- if (timeSpent > bubbleOptions.duration * double) {
32
- bubble.clicking = false;
33
- bubble.durationEnd = false;
34
- }
35
- const sizeData = {
36
- bubbleObj: {
37
- optValue: container.retina.bubbleModeSize,
38
- value: particle.bubble.radius,
39
- },
40
- particlesObj: {
41
- optValue: getRangeMax(particle.options.size.value) * container.retina.pixelRatio,
42
- value: particle.size.value,
43
- },
44
- type: ProcessBubbleType.size,
45
- };
46
- this._process(particle, distMouse, timeSpent, sizeData);
47
- const opacityData = {
48
- bubbleObj: {
49
- optValue: bubbleOptions.opacity,
50
- value: particle.bubble.opacity,
51
- },
52
- particlesObj: {
53
- optValue: getRangeMax(particle.options.opacity.value),
54
- value: particle.opacity?.value ?? defaultOpacity,
55
- },
56
- type: ProcessBubbleType.opacity,
57
- };
58
- this._process(particle, distMouse, timeSpent, opacityData);
59
- if (!bubble.durationEnd && distMouse <= distance) {
60
- this._hoverBubbleColor(particle, distMouse);
61
- }
62
- else {
63
- delete particle.bubble.color;
64
- }
65
- }
66
- };
67
- this._hoverBubble = interactivityData => {
68
- const container = this.container, mousePos = interactivityData.mouse.position, distance = container.retina.bubbleModeDistance;
69
- if (!distance || distance < minDistance || !mousePos) {
70
- return;
71
- }
72
- const query = container.particles.quadTree.queryCircle(mousePos, distance, p => this.isEnabled(interactivityData, p));
73
- for (const particle of query) {
74
- particle.bubble.inRange = true;
75
- const pos = particle.getPosition(), pointDistance = getDistance(pos, mousePos), ratio = ratioOffset - pointDistance / distance;
76
- if (pointDistance <= distance) {
77
- if (ratio >= minRatio && interactivityData.status === mouseMoveEvent) {
78
- this._hoverBubbleSize(particle, ratio);
79
- this._hoverBubbleOpacity(particle, ratio);
80
- this._hoverBubbleColor(particle, ratio);
81
- }
82
- }
83
- else {
84
- this.reset(interactivityData, particle);
85
- }
86
- if (interactivityData.status === mouseLeaveEvent) {
87
- this.reset(interactivityData, particle);
88
- }
89
- }
90
- };
91
- this._hoverBubbleColor = (particle, ratio, divBubble) => {
92
- const options = this.container.actualOptions, bubbleOptions = divBubble ?? options.interactivity?.modes.bubble;
93
- if (!bubbleOptions) {
94
- return;
95
- }
96
- if (!particle.bubble.finalColor) {
97
- const modeColor = bubbleOptions.color;
98
- if (!modeColor) {
99
- return;
100
- }
101
- const bubbleColor = itemFromSingleOrMultiple(modeColor);
102
- particle.bubble.finalColor = rangeColorToHsl(this._engine, bubbleColor);
103
- }
104
- if (!particle.bubble.finalColor) {
105
- return;
106
- }
107
- if (bubbleOptions.mix) {
108
- particle.bubble.color = undefined;
109
- const pColor = particle.getFillColor();
110
- particle.bubble.color = pColor
111
- ? rgbToHsl(colorMix(pColor, particle.bubble.finalColor, ratioOffset - ratio, ratio))
112
- : particle.bubble.finalColor;
113
- }
114
- else {
115
- particle.bubble.color = particle.bubble.finalColor;
116
- }
117
- };
118
- this._hoverBubbleOpacity = (particle, ratio, divBubble) => {
119
- const container = this.container, options = container.actualOptions, modeOpacity = divBubble?.opacity ?? options.interactivity?.modes.bubble?.opacity;
120
- if (!modeOpacity) {
121
- return;
122
- }
123
- const optOpacity = particle.options.opacity.value, pOpacity = particle.opacity?.value ?? defaultOpacity, opacity = calculateBubbleValue(pOpacity, modeOpacity, getRangeMax(optOpacity), ratio);
124
- if (opacity !== undefined) {
125
- particle.bubble.opacity = opacity;
126
- }
127
- };
128
- this._hoverBubbleSize = (particle, ratio, divBubble) => {
129
- const container = this.container, modeSize = divBubble?.size ? divBubble.size * container.retina.pixelRatio : container.retina.bubbleModeSize;
130
- if (modeSize === undefined) {
131
- return;
132
- }
133
- const optSize = getRangeMax(particle.options.size.value) * container.retina.pixelRatio, pSize = particle.size.value, size = calculateBubbleValue(pSize, modeSize, optSize, ratio);
134
- if (size !== undefined) {
135
- particle.bubble.radius = size;
136
- }
137
- };
138
- this._process = (particle, distMouse, timeSpent, data) => {
139
- const container = this.container, bubbleParam = data.bubbleObj.optValue, options = container.actualOptions, bubbleOptions = options.interactivity?.modes.bubble;
140
- if (!bubbleOptions || bubbleParam === undefined) {
141
- return;
142
- }
143
- const bubbleDuration = bubbleOptions.duration, bubbleDistance = container.retina.bubbleModeDistance, particlesParam = data.particlesObj.optValue, pObjBubble = data.bubbleObj.value, pObj = data.particlesObj.value ?? defaultBubbleValue, type = data.type;
144
- if (!bubbleDistance || bubbleDistance < minDistance || bubbleParam === particlesParam) {
145
- return;
146
- }
147
- container.bubble ??= {};
148
- if (container.bubble.durationEnd) {
149
- if (pObjBubble) {
150
- if (type === ProcessBubbleType.size) {
151
- delete particle.bubble.radius;
152
- }
153
- if (type === ProcessBubbleType.opacity) {
154
- delete particle.bubble.opacity;
155
- }
156
- }
157
- }
158
- else {
159
- if (distMouse <= bubbleDistance) {
160
- const obj = pObjBubble ?? pObj;
161
- if (obj !== bubbleParam) {
162
- const value = pObj - (timeSpent * (pObj - bubbleParam)) / bubbleDuration;
163
- if (type === ProcessBubbleType.size) {
164
- particle.bubble.radius = value;
165
- }
166
- if (type === ProcessBubbleType.opacity) {
167
- particle.bubble.opacity = value;
168
- }
169
- }
170
- }
171
- else {
172
- if (type === ProcessBubbleType.size) {
173
- delete particle.bubble.radius;
174
- }
175
- if (type === ProcessBubbleType.opacity) {
176
- delete particle.bubble.opacity;
177
- }
178
- }
179
- }
180
- };
181
- this._singleSelectorHover = (interactivityData, delta, selector, div) => {
182
- const container = this.container, selectors = safeDocument().querySelectorAll(selector), bubble = container.actualOptions.interactivity?.modes.bubble;
183
- if (!bubble || !selectors.length) {
184
- return;
185
- }
186
- selectors.forEach(item => {
187
- const elem = item, pxRatio = container.retina.pixelRatio, pos = {
188
- x: (elem.offsetLeft + elem.offsetWidth * half) * pxRatio,
189
- y: (elem.offsetTop + elem.offsetHeight * half) * pxRatio,
190
- }, repulseRadius = elem.offsetWidth * half * pxRatio, area = div.type === DivType.circle
191
- ? new Circle(pos.x, pos.y, repulseRadius)
192
- : 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));
193
- for (const particle of query) {
194
- if (!area.contains(particle.getPosition())) {
195
- continue;
196
- }
197
- particle.bubble.inRange = true;
198
- const divs = bubble.divs, divBubble = divMode(divs, elem);
199
- if (!particle.bubble.div || particle.bubble.div !== elem) {
200
- this.clear(particle, delta, true);
201
- particle.bubble.div = elem;
202
- }
203
- this._hoverBubbleSize(particle, defaultRatio, divBubble);
204
- this._hoverBubbleOpacity(particle, defaultRatio, divBubble);
205
- this._hoverBubbleColor(particle, defaultRatio, divBubble);
206
- }
207
- });
208
- };
209
13
  this._engine = engine;
14
+ this._maxDistance = 0;
210
15
  container.bubble ??= {};
211
16
  this.handleClickMode = (mode) => {
212
17
  if (mode !== bubbleMode) {
@@ -216,6 +21,9 @@ export class Bubbler extends ExternalInteractorBase {
216
21
  container.bubble.clicking = true;
217
22
  };
218
23
  }
24
+ get maxDistance() {
25
+ return this._maxDistance;
26
+ }
219
27
  clear(particle, _delta, force) {
220
28
  if (particle.bubble.inRange && !force) {
221
29
  return;
@@ -230,6 +38,7 @@ export class Bubbler extends ExternalInteractorBase {
230
38
  if (!bubble) {
231
39
  return;
232
40
  }
41
+ this._maxDistance = bubble.distance;
233
42
  container.retina.bubbleModeDistance = bubble.distance * container.retina.pixelRatio;
234
43
  if (bubble.size !== undefined) {
235
44
  container.retina.bubbleModeSize = bubble.size * container.retina.pixelRatio;
@@ -273,4 +82,202 @@ export class Bubbler extends ExternalInteractorBase {
273
82
  reset(_interactivityData, particle) {
274
83
  particle.bubble.inRange = false;
275
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._engine, 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
+ };
276
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/browser/index.js CHANGED
@@ -1,8 +1,8 @@
1
1
  export async function loadExternalBubbleInteraction(engine) {
2
- engine.checkVersion("4.0.0-alpha.5");
2
+ engine.checkVersion("4.0.0-beta.0");
3
3
  await engine.register(async (e) => {
4
- const { loadInteractivityPlugin } = await import("@tsparticles/plugin-interactivity");
5
- await loadInteractivityPlugin(e);
4
+ const { ensureInteractivityPluginLoaded } = await import("@tsparticles/plugin-interactivity");
5
+ ensureInteractivityPluginLoaded(e);
6
6
  e.addInteractor?.("externalBubble", async (container) => {
7
7
  const { Bubbler } = await import("./Bubbler.js");
8
8
  return new Bubbler(e, container);