@tsparticles/interaction-external-bubble 4.0.0-alpha.0 → 4.0.0-alpha.14

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/477.min.js ADDED
@@ -0,0 +1 @@
1
+ "use strict";(this.webpackChunk_tsparticles_interaction_external_bubble=this.webpackChunk_tsparticles_interaction_external_bubble||[]).push([[477],{477(e,i,t){t.d(i,{Bubbler:()=>u});var l,b,o=t(303),a=t(702),n=t(552);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;constructor(e,i){super(i),this._engine=e,i.bubble??={},this.handleClickMode=e=>{e===s&&(i.bubble??={},i.bubble.clicking=!0)}}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&&(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.quadTree.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.quadTree.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,v=l.type;if(u&&!(u<0)&&a!==c)if(o.bubble??={},o.bubble.durationEnd)d&&(v===b.size&&delete e.bubble.radius,v===b.opacity&&delete e.bubble.opacity);else if(i<=u){if((d??p)!==a){let i=p-t*(p-a)/s;v===b.size&&(e.bubble.radius=i),v===b.opacity&&(e.bubble.opacity=i)}}else v===b.size&&delete e.bubble.radius,v===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.quadTree.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)}})}}}}]);
@@ -1,210 +1,14 @@
1
- import { Circle, DivType, ExternalInteractorBase, Rectangle, colorMix, divMode, divModeExecute, getDistance, getRangeMax, isDivModeEnabled, isInArray, itemFromSingleOrMultiple, millisecondsToSeconds, mouseLeaveEvent, mouseMoveEvent, rangeColorToHsl, rgbToHsl, safeDocument, } from "@tsparticles/engine";
1
+ import { Circle, Rectangle, colorMix, double, getDistance, getRangeMax, half, isInArray, itemFromSingleOrMultiple, millisecondsToSeconds, rangeColorToHsl, rgbToHsl, safeDocument, } from "@tsparticles/engine";
2
+ import { DivType, ExternalInteractorBase, divMode, divModeExecute, isDivModeEnabled, mouseLeaveEvent, mouseMoveEvent, } from "@tsparticles/plugin-interactivity";
2
3
  import { Bubble } from "./Options/Classes/Bubble.js";
3
4
  import { ProcessBubbleType } from "./Enums.js";
4
5
  import { calculateBubbleValue } from "./Utils.js";
5
- const bubbleMode = "bubble", minDistance = 0, defaultClickTime = 0, double = 2, defaultOpacity = 1, ratioOffset = 1, defaultBubbleValue = 0, minRatio = 0, half = 0.5, defaultRatio = 1;
6
+ const bubbleMode = "bubble", minDistance = 0, defaultClickTime = 0, defaultOpacity = 1, ratioOffset = 1, defaultBubbleValue = 0, minRatio = 0, defaultRatio = 1;
6
7
  export class Bubbler extends ExternalInteractorBase {
8
+ handleClickMode;
9
+ _engine;
7
10
  constructor(engine, container) {
8
11
  super(container);
9
- this._clickBubble = () => {
10
- const container = this.container, options = container.actualOptions, mouseClickPos = container.interactivity.mouse.clickPosition, bubbleOptions = options.interactivity.modes.bubble;
11
- if (!bubbleOptions || !mouseClickPos) {
12
- return;
13
- }
14
- container.bubble ??= {};
15
- const distance = container.retina.bubbleModeDistance;
16
- if (!distance || distance < minDistance) {
17
- return;
18
- }
19
- const query = container.particles.quadTree.queryCircle(mouseClickPos, distance, p => this.isEnabled(p)), { bubble } = container;
20
- for (const particle of query) {
21
- if (!bubble.clicking) {
22
- continue;
23
- }
24
- particle.bubble.inRange = !bubble.durationEnd;
25
- const pos = particle.getPosition(), distMouse = getDistance(pos, mouseClickPos), timeSpent = (new Date().getTime() - (container.interactivity.mouse.clickTime ?? defaultClickTime)) /
26
- 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 = () => {
67
- const container = this.container, mousePos = container.interactivity.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(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 && container.interactivity.status === mouseMoveEvent) {
77
- this._hoverBubbleSize(particle, ratio);
78
- this._hoverBubbleOpacity(particle, ratio);
79
- this._hoverBubbleColor(particle, ratio);
80
- }
81
- }
82
- else {
83
- this.reset(particle);
84
- }
85
- if (container.interactivity.status === mouseLeaveEvent) {
86
- this.reset(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 = (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(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
12
  this._engine = engine;
209
13
  container.bubble ??= {};
210
14
  this.handleClickMode = (mode) => {
@@ -225,7 +29,7 @@ export class Bubbler extends ExternalInteractorBase {
225
29
  delete particle.bubble.color;
226
30
  }
227
31
  init() {
228
- const container = this.container, bubble = container.actualOptions.interactivity.modes.bubble;
32
+ const container = this.container, bubble = container.actualOptions.interactivity?.modes.bubble;
229
33
  if (!bubble) {
230
34
  return;
231
35
  }
@@ -234,22 +38,30 @@ export class Bubbler extends ExternalInteractorBase {
234
38
  container.retina.bubbleModeSize = bubble.size * container.retina.pixelRatio;
235
39
  }
236
40
  }
237
- interact(delta) {
238
- const options = this.container.actualOptions, events = options.interactivity.events, onHover = events.onHover, onClick = events.onClick, hoverEnabled = onHover.enable, hoverMode = onHover.mode, clickEnabled = onClick.enable, clickMode = onClick.mode, divs = events.onDiv;
41
+ interact(interactivityData, delta) {
42
+ const options = this.container.actualOptions, events = options.interactivity?.events;
43
+ if (!events) {
44
+ return;
45
+ }
46
+ const onHover = events.onHover, onClick = events.onClick, hoverEnabled = onHover.enable, hoverMode = onHover.mode, clickEnabled = onClick.enable, clickMode = onClick.mode, divs = events.onDiv;
239
47
  if (hoverEnabled && isInArray(bubbleMode, hoverMode)) {
240
- this._hoverBubble();
48
+ this._hoverBubble(interactivityData);
241
49
  }
242
50
  else if (clickEnabled && isInArray(bubbleMode, clickMode)) {
243
- this._clickBubble();
51
+ this._clickBubble(interactivityData);
244
52
  }
245
53
  else {
246
54
  divModeExecute(bubbleMode, divs, (selector, div) => {
247
- this._singleSelectorHover(delta, selector, div);
55
+ this._singleSelectorHover(interactivityData, delta, selector, div);
248
56
  });
249
57
  }
250
58
  }
251
- isEnabled(particle) {
252
- const container = this.container, options = container.actualOptions, mouse = container.interactivity.mouse, events = (particle?.interactivity ?? options.interactivity).events, { onClick, onDiv, onHover } = events, divBubble = isDivModeEnabled(bubbleMode, onDiv);
59
+ isEnabled(interactivityData, particle) {
60
+ const container = this.container, options = container.actualOptions, mouse = interactivityData.mouse, events = (particle?.interactivity ?? options.interactivity)?.events;
61
+ if (!events) {
62
+ return false;
63
+ }
64
+ const { onClick, onDiv, onHover } = events, divBubble = isDivModeEnabled(bubbleMode, onDiv);
253
65
  if (!(divBubble || (onHover.enable && !!mouse.position) || (onClick.enable && mouse.clickPosition))) {
254
66
  return false;
255
67
  }
@@ -261,7 +73,205 @@ export class Bubbler extends ExternalInteractorBase {
261
73
  options.bubble.load(source?.bubble);
262
74
  }
263
75
  }
264
- reset(particle) {
76
+ reset(_interactivityData, particle) {
265
77
  particle.bubble.inRange = false;
266
78
  }
79
+ _clickBubble = interactivityData => {
80
+ const container = this.container, options = container.actualOptions, mouseClickPos = interactivityData.mouse.clickPosition, bubbleOptions = options.interactivity?.modes.bubble;
81
+ if (!bubbleOptions || !mouseClickPos) {
82
+ return;
83
+ }
84
+ container.bubble ??= {};
85
+ const distance = container.retina.bubbleModeDistance;
86
+ if (!distance || distance < minDistance) {
87
+ return;
88
+ }
89
+ const query = container.particles.quadTree.queryCircle(mouseClickPos, distance, p => this.isEnabled(interactivityData, p)), { bubble } = container;
90
+ for (const particle of query) {
91
+ if (!bubble.clicking) {
92
+ continue;
93
+ }
94
+ particle.bubble.inRange = !bubble.durationEnd;
95
+ const pos = particle.getPosition(), distMouse = getDistance(pos, mouseClickPos), timeSpent = (performance.now() - (interactivityData.mouse.clickTime ?? defaultClickTime)) / millisecondsToSeconds;
96
+ if (timeSpent > bubbleOptions.duration) {
97
+ bubble.durationEnd = true;
98
+ }
99
+ if (timeSpent > bubbleOptions.duration * double) {
100
+ bubble.clicking = false;
101
+ bubble.durationEnd = false;
102
+ }
103
+ const sizeData = {
104
+ bubbleObj: {
105
+ optValue: container.retina.bubbleModeSize,
106
+ value: particle.bubble.radius,
107
+ },
108
+ particlesObj: {
109
+ optValue: getRangeMax(particle.options.size.value) * container.retina.pixelRatio,
110
+ value: particle.size.value,
111
+ },
112
+ type: ProcessBubbleType.size,
113
+ };
114
+ this._process(particle, distMouse, timeSpent, sizeData);
115
+ const opacityData = {
116
+ bubbleObj: {
117
+ optValue: bubbleOptions.opacity,
118
+ value: particle.bubble.opacity,
119
+ },
120
+ particlesObj: {
121
+ optValue: getRangeMax(particle.options.opacity.value),
122
+ value: particle.opacity?.value ?? defaultOpacity,
123
+ },
124
+ type: ProcessBubbleType.opacity,
125
+ };
126
+ this._process(particle, distMouse, timeSpent, opacityData);
127
+ if (!bubble.durationEnd && distMouse <= distance) {
128
+ this._hoverBubbleColor(particle, distMouse);
129
+ }
130
+ else {
131
+ delete particle.bubble.color;
132
+ }
133
+ }
134
+ };
135
+ _hoverBubble = interactivityData => {
136
+ const container = this.container, mousePos = interactivityData.mouse.position, distance = container.retina.bubbleModeDistance;
137
+ if (!distance || distance < minDistance || !mousePos) {
138
+ return;
139
+ }
140
+ const query = container.particles.quadTree.queryCircle(mousePos, distance, p => this.isEnabled(interactivityData, p));
141
+ for (const particle of query) {
142
+ particle.bubble.inRange = true;
143
+ const pos = particle.getPosition(), pointDistance = getDistance(pos, mousePos), ratio = ratioOffset - pointDistance / distance;
144
+ if (pointDistance <= distance) {
145
+ if (ratio >= minRatio && interactivityData.status === mouseMoveEvent) {
146
+ this._hoverBubbleSize(particle, ratio);
147
+ this._hoverBubbleOpacity(particle, ratio);
148
+ this._hoverBubbleColor(particle, ratio);
149
+ }
150
+ }
151
+ else {
152
+ this.reset(interactivityData, particle);
153
+ }
154
+ if (interactivityData.status === mouseLeaveEvent) {
155
+ this.reset(interactivityData, particle);
156
+ }
157
+ }
158
+ };
159
+ _hoverBubbleColor = (particle, ratio, divBubble) => {
160
+ const options = this.container.actualOptions, bubbleOptions = divBubble ?? options.interactivity?.modes.bubble;
161
+ if (!bubbleOptions) {
162
+ return;
163
+ }
164
+ if (!particle.bubble.finalColor) {
165
+ const modeColor = bubbleOptions.color;
166
+ if (!modeColor) {
167
+ return;
168
+ }
169
+ const bubbleColor = itemFromSingleOrMultiple(modeColor);
170
+ particle.bubble.finalColor = rangeColorToHsl(this._engine, bubbleColor);
171
+ }
172
+ if (!particle.bubble.finalColor) {
173
+ return;
174
+ }
175
+ if (bubbleOptions.mix) {
176
+ particle.bubble.color = undefined;
177
+ const pColor = particle.getFillColor();
178
+ particle.bubble.color = pColor
179
+ ? rgbToHsl(colorMix(pColor, particle.bubble.finalColor, ratioOffset - ratio, ratio))
180
+ : particle.bubble.finalColor;
181
+ }
182
+ else {
183
+ particle.bubble.color = particle.bubble.finalColor;
184
+ }
185
+ };
186
+ _hoverBubbleOpacity = (particle, ratio, divBubble) => {
187
+ const container = this.container, options = container.actualOptions, modeOpacity = divBubble?.opacity ?? options.interactivity?.modes.bubble?.opacity;
188
+ if (!modeOpacity) {
189
+ return;
190
+ }
191
+ const optOpacity = particle.options.opacity.value, pOpacity = particle.opacity?.value ?? defaultOpacity, opacity = calculateBubbleValue(pOpacity, modeOpacity, getRangeMax(optOpacity), ratio);
192
+ if (opacity !== undefined) {
193
+ particle.bubble.opacity = opacity;
194
+ }
195
+ };
196
+ _hoverBubbleSize = (particle, ratio, divBubble) => {
197
+ const container = this.container, modeSize = divBubble?.size ? divBubble.size * container.retina.pixelRatio : container.retina.bubbleModeSize;
198
+ if (modeSize === undefined) {
199
+ return;
200
+ }
201
+ const optSize = getRangeMax(particle.options.size.value) * container.retina.pixelRatio, pSize = particle.size.value, size = calculateBubbleValue(pSize, modeSize, optSize, ratio);
202
+ if (size !== undefined) {
203
+ particle.bubble.radius = size;
204
+ }
205
+ };
206
+ _process = (particle, distMouse, timeSpent, data) => {
207
+ const container = this.container, bubbleParam = data.bubbleObj.optValue, options = container.actualOptions, bubbleOptions = options.interactivity?.modes.bubble;
208
+ if (!bubbleOptions || bubbleParam === undefined) {
209
+ return;
210
+ }
211
+ const bubbleDuration = bubbleOptions.duration, bubbleDistance = container.retina.bubbleModeDistance, particlesParam = data.particlesObj.optValue, pObjBubble = data.bubbleObj.value, pObj = data.particlesObj.value ?? defaultBubbleValue, type = data.type;
212
+ if (!bubbleDistance || bubbleDistance < minDistance || bubbleParam === particlesParam) {
213
+ return;
214
+ }
215
+ container.bubble ??= {};
216
+ if (container.bubble.durationEnd) {
217
+ if (pObjBubble) {
218
+ if (type === ProcessBubbleType.size) {
219
+ delete particle.bubble.radius;
220
+ }
221
+ if (type === ProcessBubbleType.opacity) {
222
+ delete particle.bubble.opacity;
223
+ }
224
+ }
225
+ }
226
+ else {
227
+ if (distMouse <= bubbleDistance) {
228
+ const obj = pObjBubble ?? pObj;
229
+ if (obj !== bubbleParam) {
230
+ const value = pObj - (timeSpent * (pObj - bubbleParam)) / bubbleDuration;
231
+ if (type === ProcessBubbleType.size) {
232
+ particle.bubble.radius = value;
233
+ }
234
+ if (type === ProcessBubbleType.opacity) {
235
+ particle.bubble.opacity = value;
236
+ }
237
+ }
238
+ }
239
+ else {
240
+ if (type === ProcessBubbleType.size) {
241
+ delete particle.bubble.radius;
242
+ }
243
+ if (type === ProcessBubbleType.opacity) {
244
+ delete particle.bubble.opacity;
245
+ }
246
+ }
247
+ }
248
+ };
249
+ _singleSelectorHover = (interactivityData, delta, selector, div) => {
250
+ const container = this.container, selectors = safeDocument().querySelectorAll(selector), bubble = container.actualOptions.interactivity?.modes.bubble;
251
+ if (!bubble || !selectors.length) {
252
+ return;
253
+ }
254
+ selectors.forEach(item => {
255
+ const elem = item, pxRatio = container.retina.pixelRatio, pos = {
256
+ x: (elem.offsetLeft + elem.offsetWidth * half) * pxRatio,
257
+ y: (elem.offsetTop + elem.offsetHeight * half) * pxRatio,
258
+ }, repulseRadius = elem.offsetWidth * half * pxRatio, area = div.type === DivType.circle
259
+ ? new Circle(pos.x, pos.y, repulseRadius)
260
+ : 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));
261
+ for (const particle of query) {
262
+ if (!area.contains(particle.getPosition())) {
263
+ continue;
264
+ }
265
+ particle.bubble.inRange = true;
266
+ const divs = bubble.divs, divBubble = divMode(divs, elem);
267
+ if (!particle.bubble.div || particle.bubble.div !== elem) {
268
+ this.clear(particle, delta, true);
269
+ particle.bubble.div = elem;
270
+ }
271
+ this._hoverBubbleSize(particle, defaultRatio, divBubble);
272
+ this._hoverBubbleOpacity(particle, defaultRatio, divBubble);
273
+ this._hoverBubbleColor(particle, defaultRatio, divBubble);
274
+ }
275
+ });
276
+ };
267
277
  }
@@ -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,7 +1,9 @@
1
- export function loadExternalBubbleInteraction(engine) {
2
- engine.checkVersion("4.0.0-alpha.0");
3
- engine.register(e => {
4
- e.addInteractor("externalBubble", async (container) => {
1
+ export async function loadExternalBubbleInteraction(engine) {
2
+ engine.checkVersion("4.0.0-alpha.14");
3
+ await engine.register(async (e) => {
4
+ const { loadInteractivityPlugin } = await import("@tsparticles/plugin-interactivity");
5
+ await loadInteractivityPlugin(e);
6
+ e.addInteractor?.("externalBubble", async (container) => {
5
7
  const { Bubbler } = await import("./Bubbler.js");
6
8
  return new Bubbler(e, container);
7
9
  });