@tsparticles/interaction-external-bubble 4.0.0-alpha.8 → 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,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
+ 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)) / 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
13
  this._engine = engine;
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._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
+ };
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/browser/index.js CHANGED
@@ -1,8 +1,8 @@
1
1
  export async function loadExternalBubbleInteraction(engine) {
2
- engine.checkVersion("4.0.0-alpha.8");
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);