@tsparticles/interaction-particles-links 4.0.0-alpha.26 → 4.0.0-alpha.28

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/13.min.js ADDED
@@ -0,0 +1 @@
1
+ "use strict";(this.webpackChunk_tsparticles_interaction_particles_links=this.webpackChunk_tsparticles_interaction_particles_links||[]).push([[13],{13(n,i,t){t.r(i),t.d(i,{LinksPlugin:()=>e});class e{id="links";_engine;constructor(n){this._engine=n}async getPlugin(n){let{LinkInstance:i}=await t.e(823).then(t.bind(t,823));return new i(n,this._engine)}loadOptions(){}needsPlugin(){return!0}}}}]);
@@ -1 +1 @@
1
- "use strict";(this.webpackChunk_tsparticles_interaction_particles_links=this.webpackChunk_tsparticles_interaction_particles_links||[]).push([[520],{520(i,t,n){n.d(t,{Linker:()=>a});var s=n(303);class e extends s.Circle{canvasSize;constructor(i,t,n,s){super(i,t,n),this.canvasSize=s}contains(i){if(super.contains(i))return!0;let{width:t,height:n}=this.canvasSize,{x:s,y:e}=i;return super.contains({x:s-t,y:e})||super.contains({x:s+t,y:e})||super.contains({x:s,y:e-n})||super.contains({x:s,y:e+n})||super.contains({x:s-t,y:e-n})||super.contains({x:s+t,y:e+n})||super.contains({x:s-t,y:e+n})||super.contains({x:s+t,y:e-n})}intersects(i){if(super.intersects(i))return!0;let{width:t,height:n}=this.canvasSize,e=i.position;for(let r of[{x:-t,y:0},{x:t,y:0},{x:0,y:-n},{x:0,y:n},{x:-t,y:-n},{x:t,y:n},{x:-t,y:n},{x:t,y:-n}]){let t,n={x:e.x+r.x,y:e.y+r.y};if(t=i instanceof s.Circle?new s.Circle(n.x,n.y,i.radius):new s.Rectangle(n.x,n.y,i.size.width,i.size.height),super.intersects(t))return!0}return!1}}var r=n(925),o=n(702);class a extends o.ParticlesInteractorBase{_engine;_maxDistance;constructor(i,t){super(i),this._engine=t,this._maxDistance=0}get maxDistance(){return this._maxDistance}clear(){}init(){this.container.particles.linksColor=void 0,this.container.particles.linksColors=new Map}interact(i){if(!i.options.links)return;i.links=[],i.linksDistance&&i.linksDistance>this._maxDistance&&(this._maxDistance=i.linksDistance);let t=i.getPosition(),n=this.container,r=n.canvas.size;if(t.x<s.originPoint.x||t.y<s.originPoint.y||t.x>r.width||t.y>r.height)return;let o=i.options.links,a=o.opacity,l=i.retina.linksDistance??0,c=o.warp,p=c?new e(t.x,t.y,l,r):new s.Circle(t.x,t.y,l);for(let e of n.particles.grid.query(p)){let n=e.options.links;if(i===e||!n?.enable||o.id!==n.id||e.spawning||e.destroyed||!e.links||i.links.some(i=>i.destination===e)||e.links.some(t=>t.destination===i))continue;let p=e.getPosition();if(p.x<s.originPoint.x||p.y<s.originPoint.y||p.x>r.width||p.y>r.height)continue;let h=(0,s.getDistances)(t,p).distance,x=c&&n.warp?function(i,t,n){let{dx:e,dy:r}=(0,s.getDistances)(i,t),o={x:Math.abs(e),y:Math.abs(r)},a={x:Math.min(o.x,n.width-o.x),y:Math.min(o.y,n.height-o.y)};return Math.hypot(a.x,a.y)}(t,p,r):h,k=Math.min(h,x);if(k>l)continue;let u=(1-k/l)*a;this._setColor(i),i.links.push({destination:e,opacity:u,color:this._getLinkColor(i,e),isWarped:x<h})}}isEnabled(i){return!!i.options.links?.enable}loadParticlesOptions(i,...t){for(let n of(i.links??=new r.q,t))i.links.load(n?.links)}reset(){}_getLinkColor(i,t){let n=this.container,e=i.options.links;if(!e)return;let r=void 0!==e.id?n.particles.linksColors.get(e.id):n.particles.linksColor;return(0,s.getLinkColor)(i,t,r)}_setColor(i){if(!i.options.links)return;let t=this.container,n=i.options.links,e=void 0===n.id?t.particles.linksColor:t.particles.linksColors.get(n.id);e||(e=(0,s.getLinkRandomColor)(this._engine,n.color,n.blink,n.consent),void 0===n.id?t.particles.linksColor=e:t.particles.linksColors.set(n.id,e))}}}}]);
1
+ "use strict";(this.webpackChunk_tsparticles_interaction_particles_links=this.webpackChunk_tsparticles_interaction_particles_links||[]).push([[342],{342(i,t,n){n.d(t,{Linker:()=>a});var s=n(303);class e extends s.Circle{canvasSize;constructor(i,t,n,s){super(i,t,n),this.canvasSize=s}contains(i){if(super.contains(i))return!0;let{width:t,height:n}=this.canvasSize,{x:s,y:e}=i;return super.contains({x:s-t,y:e})||super.contains({x:s+t,y:e})||super.contains({x:s,y:e-n})||super.contains({x:s,y:e+n})||super.contains({x:s-t,y:e-n})||super.contains({x:s+t,y:e+n})||super.contains({x:s-t,y:e+n})||super.contains({x:s+t,y:e-n})}intersects(i){if(super.intersects(i))return!0;let{width:t,height:n}=this.canvasSize,e=i.position;for(let r of[{x:-t,y:0},{x:t,y:0},{x:0,y:-n},{x:0,y:n},{x:-t,y:-n},{x:t,y:n},{x:-t,y:n},{x:t,y:-n}]){let t,n={x:e.x+r.x,y:e.y+r.y};if(t=i instanceof s.Circle?new s.Circle(n.x,n.y,i.radius):new s.Rectangle(n.x,n.y,i.size.width,i.size.height),super.intersects(t))return!0}return!1}}var r=n(348),o=n(702);class a extends o.ParticlesInteractorBase{_engine;_maxDistance;constructor(i,t){super(i),this._engine=t,this._maxDistance=0}get maxDistance(){return this._maxDistance}clear(){}init(){this.container.particles.linksColor=void 0,this.container.particles.linksColors=new Map}interact(i){if(!i.options.links)return;i.links=[],i.linksDistance&&i.linksDistance>this._maxDistance&&(this._maxDistance=i.linksDistance);let t=i.getPosition(),n=this.container,r=n.canvas.size;if(t.x<s.originPoint.x||t.y<s.originPoint.y||t.x>r.width||t.y>r.height)return;let o=i.options.links,a=o.opacity,l=i.retina.linksDistance??0,c=o.warp,p=c?new e(t.x,t.y,l,r):new s.Circle(t.x,t.y,l);for(let e of n.particles.grid.query(p)){let n=e.options.links;if(i===e||!n?.enable||o.id!==n.id||e.spawning||e.destroyed||!e.links||i.links.some(i=>i.destination===e)||e.links.some(t=>t.destination===i))continue;let p=e.getPosition();if(p.x<s.originPoint.x||p.y<s.originPoint.y||p.x>r.width||p.y>r.height)continue;let h=(0,s.getDistances)(t,p).distance,x=c&&n.warp?function(i,t,n){let{dx:e,dy:r}=(0,s.getDistances)(i,t),o={x:Math.abs(e),y:Math.abs(r)},a={x:Math.min(o.x,n.width-o.x),y:Math.min(o.y,n.height-o.y)};return Math.hypot(a.x,a.y)}(t,p,r):h,k=Math.min(h,x);if(k>l)continue;let u=(1-k/l)*a;this._setColor(i),i.links.push({destination:e,opacity:u,color:this._getLinkColor(i,e),isWarped:x<h})}}isEnabled(i){return!!i.options.links?.enable}loadParticlesOptions(i,...t){for(let n of(i.links??=new r.q,t))i.links.load(n?.links)}reset(){}_getLinkColor(i,t){let n=this.container,e=i.options.links;if(!e)return;let r=void 0!==e.id?n.particles.linksColors.get(e.id):n.particles.linksColor;return(0,s.getLinkColor)(i,t,r)}_setColor(i){if(!i.options.links)return;let t=this.container,n=i.options.links,e=void 0===n.id?t.particles.linksColor:t.particles.linksColors.get(n.id);e||(e=(0,s.getLinkRandomColor)(this._engine,n.color,n.blink,n.consent),void 0===n.id?t.particles.linksColor=e:t.particles.linksColors.set(n.id,e))}}}}]);
package/823.min.js ADDED
@@ -0,0 +1 @@
1
+ "use strict";(this.webpackChunk_tsparticles_interaction_particles_links=this.webpackChunk_tsparticles_interaction_particles_links||[]).push([[823],{823(e,i,t){t.d(i,{LinkInstance:()=>s});var n=t(303);function l(e,i){let t=[...e.map(e=>e.id)].sort((e,i)=>e-i).join("_"),l=i.get(t);return void 0===l&&(l=(0,n.getRandom)(),i.set(t,l)),l}class s{_colorCache=new Map;_container;_engine;_freqs;constructor(e,i){this._container=e,this._engine=i,this._freqs={links:new Map,triangles:new Map}}drawParticle(e,i){let{links:t,options:l}=i;if(!t?.length||!l.links)return;let s=l.links,o=i.retina.linksWidth??0,a=i.getPosition(),r=i.options.twinkle?.links,c=s.triangles.enable,h=c?new Set(t.map(e=>e.destination.id)):null,g=e.globalAlpha,d="",_=-1,k=-1,p=!1,y=()=>{p&&(e.stroke(),p=!1)};for(let g of t){if(s.frequency<1&&this._getLinkFrequency(i,g.destination)>s.frequency)continue;let t=g.destination.getPosition();if(c&&!g.isWarped&&h&&(y(),this._drawTriangles(l,i,g,h,a,t,e)),g.opacity<=0||o<=0||!s.enable)continue;let f=g.opacity,u=g.color,b=r?.enable&&(0,n.getRandom)()<r.frequency?(0,n.rangeColorToRgb)(this._engine,r.color):void 0;if(r&&b&&(u=b,f=(0,n.getRangeValue)(r.opacity)),!u){let e=void 0!==s.id?this._container.particles.linksColors.get(s.id):this._container.particles.linksColor;u=(0,n.getLinkColor)(i,g.destination,e)}if(!u)continue;let q=this._getCachedStyle(u);if((q!==d||o!==_||f!==k)&&(y(),e.strokeStyle=q,e.lineWidth=o,e.globalAlpha=f,d=q,_=o,k=f,e.beginPath(),p=!0),g.isWarped){let i=this._container.canvas.size,l=t.x-a.x,s=t.y-a.y,o=n.originPoint.x,r=n.originPoint.y;Math.abs(l)>i.width*n.half&&(o=l>0?-i.width:i.width),Math.abs(s)>i.height*n.half&&(r=s>0?-i.height:i.height),e.moveTo(a.x,a.y),e.lineTo(t.x+o,t.y+r),e.moveTo(a.x-o,a.y-r),e.lineTo(t.x,t.y)}else e.moveTo(a.x,a.y),e.lineTo(t.x,t.y)}y(),e.globalAlpha=g}init(){return this._freqs.links.clear(),this._freqs.triangles.clear(),this._colorCache.clear(),Promise.resolve()}particleCreated(e){if(e.links=[],!e.options.links)return;e.linksDistance=e.options.links.distance,e.linksWidth=e.options.links.width;let i=this._container.retina.pixelRatio;e.retina.linksDistance=e.linksDistance*i,e.retina.linksWidth=e.linksWidth*i}particleDestroyed(e){e.links=[]}_drawTriangles(e,i,t,l,s,o,a){let r=t.destination,c=e.links?.triangles;if(!c?.enable||!r.options.links?.triangles.enable)return;let h=r.links;if(h?.length)for(let g of h){if(g.isWarped||this._getLinkFrequency(r,g.destination)>r.options.links.frequency||!l.has(g.destination.id))continue;let h=g.destination;if(this._getTriangleFrequency(i,r,h)>(e.links?.triangles.frequency??0))continue;let d=c.opacity??(t.opacity+g.opacity)*n.half,_=(0,n.rangeColorToRgb)(this._engine,c.color)??t.color;if(!_||d<=0)continue;let k=h.getPosition();a.save(),a.fillStyle=this._getCachedStyle(_),a.globalAlpha=d,a.beginPath(),a.moveTo(s.x,s.y),a.lineTo(o.x,o.y),a.lineTo(k.x,k.y),a.closePath(),a.fill(),a.restore()}}_getCachedStyle(e){let i=`${e.r},${e.g},${e.b}`,t=this._colorCache.get(i);return t||(t=(0,n.getStyleFromRgb)(e,this._container.hdr),this._colorCache.set(i,t)),t}_getLinkFrequency(e,i){return l([e,i],this._freqs.links)}_getTriangleFrequency(e,i,t){return l([e,i,t],this._freqs.triangles)}}}}]);
@@ -1,75 +1,50 @@
1
1
  import { getLinkColor as engineGetLinkColor, getRandom, getRangeValue, getStyleFromRgb, half, originPoint, rangeColorToRgb, } from "@tsparticles/engine";
2
2
  import { setLinkFrequency } from "./Utils.js";
3
- const minOpacity = 0, minDistance = 0, minWidth = 0, maxFrequency = 1, defaultFrequency = 0, opacitySteps = 10, defaultWidth = 0, triangleCoordsCount = 6, lineCoordsCount = 4, x1Offset = 0, y1Offset = 1, x2Offset = 2, y2Offset = 3, x3Offset = 4, y3Offset = 5;
3
+ const minOpacity = 0, minWidth = 0, minDistance = 0, maxFrequency = 1, defaultFrequency = 0;
4
4
  export class LinkInstance {
5
5
  _colorCache = new Map();
6
6
  _container;
7
7
  _engine;
8
8
  _freqs;
9
- _lineBatches = new Map();
10
- _triangleBatches = new Map();
11
9
  constructor(container, engine) {
12
10
  this._container = container;
13
11
  this._engine = engine;
14
12
  this._freqs = { links: new Map(), triangles: new Map() };
15
13
  }
16
- draw(context) {
17
- for (const [, batch] of this._triangleBatches) {
18
- context.save();
19
- context.fillStyle = batch.colorStyle;
20
- context.globalAlpha = batch.opacity;
21
- context.beginPath();
22
- for (let i = 0; i < batch.coords.length; i += triangleCoordsCount) {
23
- const x1 = batch.coords[i + x1Offset] ?? originPoint.x, y1 = batch.coords[i + y1Offset] ?? originPoint.y, x2 = batch.coords[i + x2Offset] ?? originPoint.x, y2 = batch.coords[i + y2Offset] ?? originPoint.y, x3 = batch.coords[i + x3Offset] ?? originPoint.x, y3 = batch.coords[i + y3Offset] ?? originPoint.y;
24
- context.moveTo(x1, y1);
25
- context.lineTo(x2, y2);
26
- context.lineTo(x3, y3);
27
- }
28
- context.fill();
29
- context.restore();
30
- }
31
- for (const [, batch] of this._lineBatches) {
32
- context.save();
33
- context.strokeStyle = batch.colorStyle;
34
- context.lineWidth = batch.width;
35
- context.globalAlpha = batch.opacity;
36
- context.beginPath();
37
- for (let i = 0; i < batch.coords.length; i += lineCoordsCount) {
38
- const x1 = batch.coords[i + x1Offset] ?? originPoint.x, y1 = batch.coords[i + y1Offset] ?? originPoint.y, x2 = batch.coords[i + x2Offset] ?? originPoint.x, y2 = batch.coords[i + y2Offset] ?? originPoint.y;
39
- context.moveTo(x1, y1);
40
- context.lineTo(x2, y2);
41
- }
42
- context.stroke();
43
- context.restore();
44
- }
45
- this._lineBatches.clear();
46
- this._triangleBatches.clear();
47
- }
48
- drawParticle(_context, particle) {
14
+ drawParticle(context, particle) {
49
15
  const { links, options } = particle;
50
16
  if (!links?.length || !options.links) {
51
17
  return;
52
18
  }
53
- const canvasSize = this._container.canvas.size, pos1 = particle.getPosition(), linkOpts = options.links;
19
+ const linkOpts = options.links, width = particle.retina.linksWidth ?? minWidth, pos1 = particle.getPosition(), twinkle = particle.options["twinkle"]?.links, trianglesEnabled = linkOpts.triangles.enable, p1Destinations = trianglesEnabled ? new Set(links.map(l => l.destination.id)) : null, originalAlpha = context.globalAlpha;
20
+ let currentColorStyle = "", currentWidth = -1, currentAlpha = -1, pathOpen = false;
21
+ const flushLines = () => {
22
+ if (pathOpen) {
23
+ context.stroke();
24
+ pathOpen = false;
25
+ }
26
+ };
54
27
  for (const link of links) {
55
28
  if (linkOpts.frequency < maxFrequency &&
56
29
  this._getLinkFrequency(particle, link.destination) > linkOpts.frequency) {
57
30
  continue;
58
31
  }
59
- if (!link.isWarped) {
60
- this._collectTriangles(options, particle, link, links, pos1);
32
+ const pos2 = link.destination.getPosition();
33
+ if (trianglesEnabled && !link.isWarped && p1Destinations) {
34
+ flushLines();
35
+ this._drawTriangles(options, particle, link, p1Destinations, pos1, pos2, context);
36
+ }
37
+ if (link.opacity <= minOpacity || width <= minWidth) {
38
+ continue;
61
39
  }
62
- if (link.opacity <= minOpacity || (particle.retina.linksWidth ?? minWidth) <= minWidth) {
40
+ if (!linkOpts.enable) {
63
41
  continue;
64
42
  }
65
43
  let opacity = link.opacity, colorLine = link.color;
66
- const twinkle = particle.options["twinkle"]?.lines;
67
- if (twinkle?.enable && getRandom() < twinkle.frequency) {
68
- const twinkleRgb = rangeColorToRgb(this._engine, twinkle.color);
69
- if (twinkleRgb) {
70
- colorLine = twinkleRgb;
71
- opacity = getRangeValue(twinkle.opacity);
72
- }
44
+ const twinkleRgb = twinkle?.enable && getRandom() < twinkle.frequency ? rangeColorToRgb(this._engine, twinkle.color) : undefined;
45
+ if (twinkle && twinkleRgb) {
46
+ colorLine = twinkleRgb;
47
+ opacity = getRangeValue(twinkle.opacity);
73
48
  }
74
49
  if (!colorLine) {
75
50
  const linkColor = linkOpts.id !== undefined
@@ -80,15 +55,20 @@ export class LinkInstance {
80
55
  if (!colorLine) {
81
56
  continue;
82
57
  }
83
- const qOpacity = Math.ceil(opacity * opacitySteps) / opacitySteps, colorStyle = this._getCachedStyle(colorLine), width = particle.retina.linksWidth ?? defaultWidth, key = `${colorStyle}_${qOpacity}_${width}`;
84
- let batch = this._lineBatches.get(key);
85
- if (!batch) {
86
- batch = { colorStyle, opacity: qOpacity, width, coords: [] };
87
- this._lineBatches.set(key, batch);
58
+ const colorStyle = this._getCachedStyle(colorLine);
59
+ if (colorStyle !== currentColorStyle || width !== currentWidth || opacity !== currentAlpha) {
60
+ flushLines();
61
+ context.strokeStyle = colorStyle;
62
+ context.lineWidth = width;
63
+ context.globalAlpha = opacity;
64
+ currentColorStyle = colorStyle;
65
+ currentWidth = width;
66
+ currentAlpha = opacity;
67
+ context.beginPath();
68
+ pathOpen = true;
88
69
  }
89
- const pos2 = link.destination.getPosition();
90
70
  if (link.isWarped) {
91
- const dx = pos2.x - pos1.x, dy = pos2.y - pos1.y;
71
+ const canvasSize = this._container.canvas.size, dx = pos2.x - pos1.x, dy = pos2.y - pos1.y;
92
72
  let sx = originPoint.x, sy = originPoint.y;
93
73
  if (Math.abs(dx) > canvasSize.width * half) {
94
74
  sx = dx > minDistance ? -canvasSize.width : canvasSize.width;
@@ -96,13 +76,18 @@ export class LinkInstance {
96
76
  if (Math.abs(dy) > canvasSize.height * half) {
97
77
  sy = dy > minDistance ? -canvasSize.height : canvasSize.height;
98
78
  }
99
- const v2 = { x: pos2.x + sx, y: pos2.y + sy }, v1 = { x: pos1.x - sx, y: pos1.y - sy };
100
- batch.coords.push(pos1.x, pos1.y, v2.x, v2.y, v1.x, v1.y, pos2.x, pos2.y);
79
+ context.moveTo(pos1.x, pos1.y);
80
+ context.lineTo(pos2.x + sx, pos2.y + sy);
81
+ context.moveTo(pos1.x - sx, pos1.y - sy);
82
+ context.lineTo(pos2.x, pos2.y);
101
83
  }
102
84
  else {
103
- batch.coords.push(pos1.x, pos1.y, pos2.x, pos2.y);
85
+ context.moveTo(pos1.x, pos1.y);
86
+ context.lineTo(pos2.x, pos2.y);
104
87
  }
105
88
  }
89
+ flushLines();
90
+ context.globalAlpha = originalAlpha;
106
91
  }
107
92
  init() {
108
93
  this._freqs.links.clear();
@@ -124,16 +109,15 @@ export class LinkInstance {
124
109
  particleDestroyed(particle) {
125
110
  particle.links = [];
126
111
  }
127
- _collectTriangles(options, p1, link, p1Links, pos1) {
112
+ _drawTriangles(options, p1, link, p1Destinations, pos1, pos2, context) {
128
113
  const p2 = link.destination, triangleOptions = options.links?.triangles;
129
114
  if (!triangleOptions?.enable || !p2.options.links?.triangles.enable) {
130
115
  return;
131
116
  }
132
- const p1Destinations = new Set(p1Links.map(l => l.destination.id)), p2Links = p2.links;
117
+ const p2Links = p2.links;
133
118
  if (!p2Links?.length) {
134
119
  return;
135
120
  }
136
- const pos2 = p2.getPosition();
137
121
  for (const vertex of p2Links) {
138
122
  if (vertex.isWarped ||
139
123
  this._getLinkFrequency(p2, vertex.destination) > p2.options.links.frequency ||
@@ -144,18 +128,21 @@ export class LinkInstance {
144
128
  if (this._getTriangleFrequency(p1, p2, p3) > (options.links?.triangles.frequency ?? defaultFrequency)) {
145
129
  continue;
146
130
  }
147
- const opacityTriangle = Math.ceil((link.opacity + vertex.opacity) * half * opacitySteps) / opacitySteps, colorTriangle = rangeColorToRgb(this._engine, triangleOptions.color) ?? link.color;
148
- if (!colorTriangle) {
131
+ const opacityTriangle = triangleOptions.opacity ?? (link.opacity + vertex.opacity) * half, colorTriangle = rangeColorToRgb(this._engine, triangleOptions.color) ?? link.color;
132
+ if (!colorTriangle || opacityTriangle <= minOpacity) {
149
133
  continue;
150
134
  }
151
- const colorStyle = this._getCachedStyle(colorTriangle), key = `${colorStyle}_${opacityTriangle}`;
152
- let batch = this._triangleBatches.get(key);
153
- if (!batch) {
154
- batch = { colorStyle, opacity: opacityTriangle, coords: [] };
155
- this._triangleBatches.set(key, batch);
156
- }
157
135
  const pos3 = p3.getPosition();
158
- batch.coords.push(pos1.x, pos1.y, pos2.x, pos2.y, pos3.x, pos3.y);
136
+ context.save();
137
+ context.fillStyle = this._getCachedStyle(colorTriangle);
138
+ context.globalAlpha = opacityTriangle;
139
+ context.beginPath();
140
+ context.moveTo(pos1.x, pos1.y);
141
+ context.lineTo(pos2.x, pos2.y);
142
+ context.lineTo(pos3.x, pos3.y);
143
+ context.closePath();
144
+ context.fill();
145
+ context.restore();
159
146
  }
160
147
  }
161
148
  _getCachedStyle(rgb) {
package/browser/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  export async function loadParticlesLinksInteraction(engine) {
2
- engine.checkVersion("4.0.0-alpha.26");
2
+ engine.checkVersion("4.0.0-alpha.28");
3
3
  await engine.register(async (e) => {
4
4
  const [{ ensureInteractivityPluginLoaded }, { LinksPlugin },] = await Promise.all([
5
5
  import("@tsparticles/plugin-interactivity"),
@@ -1,75 +1,50 @@
1
1
  import { getLinkColor as engineGetLinkColor, getRandom, getRangeValue, getStyleFromRgb, half, originPoint, rangeColorToRgb, } from "@tsparticles/engine";
2
2
  import { setLinkFrequency } from "./Utils.js";
3
- const minOpacity = 0, minDistance = 0, minWidth = 0, maxFrequency = 1, defaultFrequency = 0, opacitySteps = 10, defaultWidth = 0, triangleCoordsCount = 6, lineCoordsCount = 4, x1Offset = 0, y1Offset = 1, x2Offset = 2, y2Offset = 3, x3Offset = 4, y3Offset = 5;
3
+ const minOpacity = 0, minWidth = 0, minDistance = 0, maxFrequency = 1, defaultFrequency = 0;
4
4
  export class LinkInstance {
5
5
  _colorCache = new Map();
6
6
  _container;
7
7
  _engine;
8
8
  _freqs;
9
- _lineBatches = new Map();
10
- _triangleBatches = new Map();
11
9
  constructor(container, engine) {
12
10
  this._container = container;
13
11
  this._engine = engine;
14
12
  this._freqs = { links: new Map(), triangles: new Map() };
15
13
  }
16
- draw(context) {
17
- for (const [, batch] of this._triangleBatches) {
18
- context.save();
19
- context.fillStyle = batch.colorStyle;
20
- context.globalAlpha = batch.opacity;
21
- context.beginPath();
22
- for (let i = 0; i < batch.coords.length; i += triangleCoordsCount) {
23
- const x1 = batch.coords[i + x1Offset] ?? originPoint.x, y1 = batch.coords[i + y1Offset] ?? originPoint.y, x2 = batch.coords[i + x2Offset] ?? originPoint.x, y2 = batch.coords[i + y2Offset] ?? originPoint.y, x3 = batch.coords[i + x3Offset] ?? originPoint.x, y3 = batch.coords[i + y3Offset] ?? originPoint.y;
24
- context.moveTo(x1, y1);
25
- context.lineTo(x2, y2);
26
- context.lineTo(x3, y3);
27
- }
28
- context.fill();
29
- context.restore();
30
- }
31
- for (const [, batch] of this._lineBatches) {
32
- context.save();
33
- context.strokeStyle = batch.colorStyle;
34
- context.lineWidth = batch.width;
35
- context.globalAlpha = batch.opacity;
36
- context.beginPath();
37
- for (let i = 0; i < batch.coords.length; i += lineCoordsCount) {
38
- const x1 = batch.coords[i + x1Offset] ?? originPoint.x, y1 = batch.coords[i + y1Offset] ?? originPoint.y, x2 = batch.coords[i + x2Offset] ?? originPoint.x, y2 = batch.coords[i + y2Offset] ?? originPoint.y;
39
- context.moveTo(x1, y1);
40
- context.lineTo(x2, y2);
41
- }
42
- context.stroke();
43
- context.restore();
44
- }
45
- this._lineBatches.clear();
46
- this._triangleBatches.clear();
47
- }
48
- drawParticle(_context, particle) {
14
+ drawParticle(context, particle) {
49
15
  const { links, options } = particle;
50
16
  if (!links?.length || !options.links) {
51
17
  return;
52
18
  }
53
- const canvasSize = this._container.canvas.size, pos1 = particle.getPosition(), linkOpts = options.links;
19
+ const linkOpts = options.links, width = particle.retina.linksWidth ?? minWidth, pos1 = particle.getPosition(), twinkle = particle.options["twinkle"]?.links, trianglesEnabled = linkOpts.triangles.enable, p1Destinations = trianglesEnabled ? new Set(links.map(l => l.destination.id)) : null, originalAlpha = context.globalAlpha;
20
+ let currentColorStyle = "", currentWidth = -1, currentAlpha = -1, pathOpen = false;
21
+ const flushLines = () => {
22
+ if (pathOpen) {
23
+ context.stroke();
24
+ pathOpen = false;
25
+ }
26
+ };
54
27
  for (const link of links) {
55
28
  if (linkOpts.frequency < maxFrequency &&
56
29
  this._getLinkFrequency(particle, link.destination) > linkOpts.frequency) {
57
30
  continue;
58
31
  }
59
- if (!link.isWarped) {
60
- this._collectTriangles(options, particle, link, links, pos1);
32
+ const pos2 = link.destination.getPosition();
33
+ if (trianglesEnabled && !link.isWarped && p1Destinations) {
34
+ flushLines();
35
+ this._drawTriangles(options, particle, link, p1Destinations, pos1, pos2, context);
36
+ }
37
+ if (link.opacity <= minOpacity || width <= minWidth) {
38
+ continue;
61
39
  }
62
- if (link.opacity <= minOpacity || (particle.retina.linksWidth ?? minWidth) <= minWidth) {
40
+ if (!linkOpts.enable) {
63
41
  continue;
64
42
  }
65
43
  let opacity = link.opacity, colorLine = link.color;
66
- const twinkle = particle.options["twinkle"]?.lines;
67
- if (twinkle?.enable && getRandom() < twinkle.frequency) {
68
- const twinkleRgb = rangeColorToRgb(this._engine, twinkle.color);
69
- if (twinkleRgb) {
70
- colorLine = twinkleRgb;
71
- opacity = getRangeValue(twinkle.opacity);
72
- }
44
+ const twinkleRgb = twinkle?.enable && getRandom() < twinkle.frequency ? rangeColorToRgb(this._engine, twinkle.color) : undefined;
45
+ if (twinkle && twinkleRgb) {
46
+ colorLine = twinkleRgb;
47
+ opacity = getRangeValue(twinkle.opacity);
73
48
  }
74
49
  if (!colorLine) {
75
50
  const linkColor = linkOpts.id !== undefined
@@ -80,15 +55,20 @@ export class LinkInstance {
80
55
  if (!colorLine) {
81
56
  continue;
82
57
  }
83
- const qOpacity = Math.ceil(opacity * opacitySteps) / opacitySteps, colorStyle = this._getCachedStyle(colorLine), width = particle.retina.linksWidth ?? defaultWidth, key = `${colorStyle}_${qOpacity}_${width}`;
84
- let batch = this._lineBatches.get(key);
85
- if (!batch) {
86
- batch = { colorStyle, opacity: qOpacity, width, coords: [] };
87
- this._lineBatches.set(key, batch);
58
+ const colorStyle = this._getCachedStyle(colorLine);
59
+ if (colorStyle !== currentColorStyle || width !== currentWidth || opacity !== currentAlpha) {
60
+ flushLines();
61
+ context.strokeStyle = colorStyle;
62
+ context.lineWidth = width;
63
+ context.globalAlpha = opacity;
64
+ currentColorStyle = colorStyle;
65
+ currentWidth = width;
66
+ currentAlpha = opacity;
67
+ context.beginPath();
68
+ pathOpen = true;
88
69
  }
89
- const pos2 = link.destination.getPosition();
90
70
  if (link.isWarped) {
91
- const dx = pos2.x - pos1.x, dy = pos2.y - pos1.y;
71
+ const canvasSize = this._container.canvas.size, dx = pos2.x - pos1.x, dy = pos2.y - pos1.y;
92
72
  let sx = originPoint.x, sy = originPoint.y;
93
73
  if (Math.abs(dx) > canvasSize.width * half) {
94
74
  sx = dx > minDistance ? -canvasSize.width : canvasSize.width;
@@ -96,13 +76,18 @@ export class LinkInstance {
96
76
  if (Math.abs(dy) > canvasSize.height * half) {
97
77
  sy = dy > minDistance ? -canvasSize.height : canvasSize.height;
98
78
  }
99
- const v2 = { x: pos2.x + sx, y: pos2.y + sy }, v1 = { x: pos1.x - sx, y: pos1.y - sy };
100
- batch.coords.push(pos1.x, pos1.y, v2.x, v2.y, v1.x, v1.y, pos2.x, pos2.y);
79
+ context.moveTo(pos1.x, pos1.y);
80
+ context.lineTo(pos2.x + sx, pos2.y + sy);
81
+ context.moveTo(pos1.x - sx, pos1.y - sy);
82
+ context.lineTo(pos2.x, pos2.y);
101
83
  }
102
84
  else {
103
- batch.coords.push(pos1.x, pos1.y, pos2.x, pos2.y);
85
+ context.moveTo(pos1.x, pos1.y);
86
+ context.lineTo(pos2.x, pos2.y);
104
87
  }
105
88
  }
89
+ flushLines();
90
+ context.globalAlpha = originalAlpha;
106
91
  }
107
92
  init() {
108
93
  this._freqs.links.clear();
@@ -124,16 +109,15 @@ export class LinkInstance {
124
109
  particleDestroyed(particle) {
125
110
  particle.links = [];
126
111
  }
127
- _collectTriangles(options, p1, link, p1Links, pos1) {
112
+ _drawTriangles(options, p1, link, p1Destinations, pos1, pos2, context) {
128
113
  const p2 = link.destination, triangleOptions = options.links?.triangles;
129
114
  if (!triangleOptions?.enable || !p2.options.links?.triangles.enable) {
130
115
  return;
131
116
  }
132
- const p1Destinations = new Set(p1Links.map(l => l.destination.id)), p2Links = p2.links;
117
+ const p2Links = p2.links;
133
118
  if (!p2Links?.length) {
134
119
  return;
135
120
  }
136
- const pos2 = p2.getPosition();
137
121
  for (const vertex of p2Links) {
138
122
  if (vertex.isWarped ||
139
123
  this._getLinkFrequency(p2, vertex.destination) > p2.options.links.frequency ||
@@ -144,18 +128,21 @@ export class LinkInstance {
144
128
  if (this._getTriangleFrequency(p1, p2, p3) > (options.links?.triangles.frequency ?? defaultFrequency)) {
145
129
  continue;
146
130
  }
147
- const opacityTriangle = Math.ceil((link.opacity + vertex.opacity) * half * opacitySteps) / opacitySteps, colorTriangle = rangeColorToRgb(this._engine, triangleOptions.color) ?? link.color;
148
- if (!colorTriangle) {
131
+ const opacityTriangle = triangleOptions.opacity ?? (link.opacity + vertex.opacity) * half, colorTriangle = rangeColorToRgb(this._engine, triangleOptions.color) ?? link.color;
132
+ if (!colorTriangle || opacityTriangle <= minOpacity) {
149
133
  continue;
150
134
  }
151
- const colorStyle = this._getCachedStyle(colorTriangle), key = `${colorStyle}_${opacityTriangle}`;
152
- let batch = this._triangleBatches.get(key);
153
- if (!batch) {
154
- batch = { colorStyle, opacity: opacityTriangle, coords: [] };
155
- this._triangleBatches.set(key, batch);
156
- }
157
135
  const pos3 = p3.getPosition();
158
- batch.coords.push(pos1.x, pos1.y, pos2.x, pos2.y, pos3.x, pos3.y);
136
+ context.save();
137
+ context.fillStyle = this._getCachedStyle(colorTriangle);
138
+ context.globalAlpha = opacityTriangle;
139
+ context.beginPath();
140
+ context.moveTo(pos1.x, pos1.y);
141
+ context.lineTo(pos2.x, pos2.y);
142
+ context.lineTo(pos3.x, pos3.y);
143
+ context.closePath();
144
+ context.fill();
145
+ context.restore();
159
146
  }
160
147
  }
161
148
  _getCachedStyle(rgb) {
package/cjs/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  export async function loadParticlesLinksInteraction(engine) {
2
- engine.checkVersion("4.0.0-alpha.26");
2
+ engine.checkVersion("4.0.0-alpha.28");
3
3
  await engine.register(async (e) => {
4
4
  const [{ ensureInteractivityPluginLoaded }, { LinksPlugin },] = await Promise.all([
5
5
  import("@tsparticles/plugin-interactivity"),
@@ -4,7 +4,7 @@
4
4
  * Demo / Generator : https://particles.js.org/
5
5
  * GitHub : https://www.github.com/matteobruni/tsparticles
6
6
  * How to use? : Check the GitHub README
7
- * v4.0.0-alpha.26
7
+ * v4.0.0-alpha.28
8
8
  */
9
9
  "use strict";
10
10
  /*
@@ -23,7 +23,7 @@
23
23
  \**************************************/
24
24
  (__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) {
25
25
 
26
- eval("{__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ LinkInstance: () => (/* binding */ LinkInstance)\n/* harmony export */ });\n/* harmony import */ var _tsparticles_engine__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @tsparticles/engine */ \"@tsparticles/engine\");\n/* harmony import */ var _Utils_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./Utils.js */ \"./dist/browser/Utils.js\");\n\n\nconst minOpacity = 0, minDistance = 0, minWidth = 0, maxFrequency = 1, defaultFrequency = 0, opacitySteps = 10, defaultWidth = 0, triangleCoordsCount = 6, lineCoordsCount = 4, x1Offset = 0, y1Offset = 1, x2Offset = 2, y2Offset = 3, x3Offset = 4, y3Offset = 5;\nclass LinkInstance {\n _colorCache = new Map();\n _container;\n _engine;\n _freqs;\n _lineBatches = new Map();\n _triangleBatches = new Map();\n constructor(container, engine){\n this._container = container;\n this._engine = engine;\n this._freqs = {\n links: new Map(),\n triangles: new Map()\n };\n }\n draw(context) {\n for (const [, batch] of this._triangleBatches){\n context.save();\n context.fillStyle = batch.colorStyle;\n context.globalAlpha = batch.opacity;\n context.beginPath();\n for(let i = 0; i < batch.coords.length; i += triangleCoordsCount){\n const x1 = batch.coords[i + x1Offset] ?? _tsparticles_engine__WEBPACK_IMPORTED_MODULE_0__.originPoint.x, y1 = batch.coords[i + y1Offset] ?? _tsparticles_engine__WEBPACK_IMPORTED_MODULE_0__.originPoint.y, x2 = batch.coords[i + x2Offset] ?? _tsparticles_engine__WEBPACK_IMPORTED_MODULE_0__.originPoint.x, y2 = batch.coords[i + y2Offset] ?? _tsparticles_engine__WEBPACK_IMPORTED_MODULE_0__.originPoint.y, x3 = batch.coords[i + x3Offset] ?? _tsparticles_engine__WEBPACK_IMPORTED_MODULE_0__.originPoint.x, y3 = batch.coords[i + y3Offset] ?? _tsparticles_engine__WEBPACK_IMPORTED_MODULE_0__.originPoint.y;\n context.moveTo(x1, y1);\n context.lineTo(x2, y2);\n context.lineTo(x3, y3);\n }\n context.fill();\n context.restore();\n }\n for (const [, batch] of this._lineBatches){\n context.save();\n context.strokeStyle = batch.colorStyle;\n context.lineWidth = batch.width;\n context.globalAlpha = batch.opacity;\n context.beginPath();\n for(let i = 0; i < batch.coords.length; i += lineCoordsCount){\n const x1 = batch.coords[i + x1Offset] ?? _tsparticles_engine__WEBPACK_IMPORTED_MODULE_0__.originPoint.x, y1 = batch.coords[i + y1Offset] ?? _tsparticles_engine__WEBPACK_IMPORTED_MODULE_0__.originPoint.y, x2 = batch.coords[i + x2Offset] ?? _tsparticles_engine__WEBPACK_IMPORTED_MODULE_0__.originPoint.x, y2 = batch.coords[i + y2Offset] ?? _tsparticles_engine__WEBPACK_IMPORTED_MODULE_0__.originPoint.y;\n context.moveTo(x1, y1);\n context.lineTo(x2, y2);\n }\n context.stroke();\n context.restore();\n }\n this._lineBatches.clear();\n this._triangleBatches.clear();\n }\n drawParticle(_context, particle) {\n const { links, options } = particle;\n if (!links?.length || !options.links) {\n return;\n }\n const canvasSize = this._container.canvas.size, pos1 = particle.getPosition(), linkOpts = options.links;\n for (const link of links){\n if (linkOpts.frequency < maxFrequency && this._getLinkFrequency(particle, link.destination) > linkOpts.frequency) {\n continue;\n }\n if (!link.isWarped) {\n this._collectTriangles(options, particle, link, links, pos1);\n }\n if (link.opacity <= minOpacity || (particle.retina.linksWidth ?? minWidth) <= minWidth) {\n continue;\n }\n let opacity = link.opacity, colorLine = link.color;\n const twinkle = particle.options[\"twinkle\"]?.lines;\n if (twinkle?.enable && (0,_tsparticles_engine__WEBPACK_IMPORTED_MODULE_0__.getRandom)() < twinkle.frequency) {\n const twinkleRgb = (0,_tsparticles_engine__WEBPACK_IMPORTED_MODULE_0__.rangeColorToRgb)(this._engine, twinkle.color);\n if (twinkleRgb) {\n colorLine = twinkleRgb;\n opacity = (0,_tsparticles_engine__WEBPACK_IMPORTED_MODULE_0__.getRangeValue)(twinkle.opacity);\n }\n }\n if (!colorLine) {\n const linkColor = linkOpts.id !== undefined ? this._container.particles.linksColors.get(linkOpts.id) : this._container.particles.linksColor;\n colorLine = (0,_tsparticles_engine__WEBPACK_IMPORTED_MODULE_0__.getLinkColor)(particle, link.destination, linkColor);\n }\n if (!colorLine) {\n continue;\n }\n const qOpacity = Math.ceil(opacity * opacitySteps) / opacitySteps, colorStyle = this._getCachedStyle(colorLine), width = particle.retina.linksWidth ?? defaultWidth, key = `${colorStyle}_${qOpacity}_${width}`;\n let batch = this._lineBatches.get(key);\n if (!batch) {\n batch = {\n colorStyle,\n opacity: qOpacity,\n width,\n coords: []\n };\n this._lineBatches.set(key, batch);\n }\n const pos2 = link.destination.getPosition();\n if (link.isWarped) {\n const dx = pos2.x - pos1.x, dy = pos2.y - pos1.y;\n let sx = _tsparticles_engine__WEBPACK_IMPORTED_MODULE_0__.originPoint.x, sy = _tsparticles_engine__WEBPACK_IMPORTED_MODULE_0__.originPoint.y;\n if (Math.abs(dx) > canvasSize.width * _tsparticles_engine__WEBPACK_IMPORTED_MODULE_0__.half) {\n sx = dx > minDistance ? -canvasSize.width : canvasSize.width;\n }\n if (Math.abs(dy) > canvasSize.height * _tsparticles_engine__WEBPACK_IMPORTED_MODULE_0__.half) {\n sy = dy > minDistance ? -canvasSize.height : canvasSize.height;\n }\n const v2 = {\n x: pos2.x + sx,\n y: pos2.y + sy\n }, v1 = {\n x: pos1.x - sx,\n y: pos1.y - sy\n };\n batch.coords.push(pos1.x, pos1.y, v2.x, v2.y, v1.x, v1.y, pos2.x, pos2.y);\n } else {\n batch.coords.push(pos1.x, pos1.y, pos2.x, pos2.y);\n }\n }\n }\n init() {\n this._freqs.links.clear();\n this._freqs.triangles.clear();\n this._colorCache.clear();\n return Promise.resolve();\n }\n particleCreated(particle) {\n particle.links = [];\n if (!particle.options.links) {\n return;\n }\n particle.linksDistance = particle.options.links.distance;\n particle.linksWidth = particle.options.links.width;\n const ratio = this._container.retina.pixelRatio;\n particle.retina.linksDistance = particle.linksDistance * ratio;\n particle.retina.linksWidth = particle.linksWidth * ratio;\n }\n particleDestroyed(particle) {\n particle.links = [];\n }\n _collectTriangles(options, p1, link, p1Links, pos1) {\n const p2 = link.destination, triangleOptions = options.links?.triangles;\n if (!triangleOptions?.enable || !p2.options.links?.triangles.enable) {\n return;\n }\n const p1Destinations = new Set(p1Links.map((l)=>l.destination.id)), p2Links = p2.links;\n if (!p2Links?.length) {\n return;\n }\n const pos2 = p2.getPosition();\n for (const vertex of p2Links){\n if (vertex.isWarped || this._getLinkFrequency(p2, vertex.destination) > p2.options.links.frequency || !p1Destinations.has(vertex.destination.id)) {\n continue;\n }\n const p3 = vertex.destination;\n if (this._getTriangleFrequency(p1, p2, p3) > (options.links?.triangles.frequency ?? defaultFrequency)) {\n continue;\n }\n const opacityTriangle = Math.ceil((link.opacity + vertex.opacity) * _tsparticles_engine__WEBPACK_IMPORTED_MODULE_0__.half * opacitySteps) / opacitySteps, colorTriangle = (0,_tsparticles_engine__WEBPACK_IMPORTED_MODULE_0__.rangeColorToRgb)(this._engine, triangleOptions.color) ?? link.color;\n if (!colorTriangle) {\n continue;\n }\n const colorStyle = this._getCachedStyle(colorTriangle), key = `${colorStyle}_${opacityTriangle}`;\n let batch = this._triangleBatches.get(key);\n if (!batch) {\n batch = {\n colorStyle,\n opacity: opacityTriangle,\n coords: []\n };\n this._triangleBatches.set(key, batch);\n }\n const pos3 = p3.getPosition();\n batch.coords.push(pos1.x, pos1.y, pos2.x, pos2.y, pos3.x, pos3.y);\n }\n }\n _getCachedStyle(rgb) {\n const key = `${rgb.r},${rgb.g},${rgb.b}`;\n let style = this._colorCache.get(key);\n if (!style) {\n style = (0,_tsparticles_engine__WEBPACK_IMPORTED_MODULE_0__.getStyleFromRgb)(rgb, this._container.hdr);\n this._colorCache.set(key, style);\n }\n return style;\n }\n _getLinkFrequency(p1, p2) {\n return (0,_Utils_js__WEBPACK_IMPORTED_MODULE_1__.setLinkFrequency)([\n p1,\n p2\n ], this._freqs.links);\n }\n _getTriangleFrequency(p1, p2, p3) {\n return (0,_Utils_js__WEBPACK_IMPORTED_MODULE_1__.setLinkFrequency)([\n p1,\n p2,\n p3\n ], this._freqs.triangles);\n }\n}\n\n\n//# sourceURL=webpack://@tsparticles/interaction-particles-links/./dist/browser/LinkInstance.js?\n}");
26
+ eval("{__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ LinkInstance: () => (/* binding */ LinkInstance)\n/* harmony export */ });\n/* harmony import */ var _tsparticles_engine__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @tsparticles/engine */ \"@tsparticles/engine\");\n/* harmony import */ var _Utils_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./Utils.js */ \"./dist/browser/Utils.js\");\n\n\nconst minOpacity = 0, minWidth = 0, minDistance = 0, maxFrequency = 1, defaultFrequency = 0;\nclass LinkInstance {\n _colorCache = new Map();\n _container;\n _engine;\n _freqs;\n constructor(container, engine){\n this._container = container;\n this._engine = engine;\n this._freqs = {\n links: new Map(),\n triangles: new Map()\n };\n }\n drawParticle(context, particle) {\n const { links, options } = particle;\n if (!links?.length || !options.links) {\n return;\n }\n const linkOpts = options.links, width = particle.retina.linksWidth ?? minWidth, pos1 = particle.getPosition(), twinkle = particle.options[\"twinkle\"]?.links, trianglesEnabled = linkOpts.triangles.enable, p1Destinations = trianglesEnabled ? new Set(links.map((l)=>l.destination.id)) : null, originalAlpha = context.globalAlpha;\n let currentColorStyle = \"\", currentWidth = -1, currentAlpha = -1, pathOpen = false;\n const flushLines = ()=>{\n if (pathOpen) {\n context.stroke();\n pathOpen = false;\n }\n };\n for (const link of links){\n if (linkOpts.frequency < maxFrequency && this._getLinkFrequency(particle, link.destination) > linkOpts.frequency) {\n continue;\n }\n const pos2 = link.destination.getPosition();\n if (trianglesEnabled && !link.isWarped && p1Destinations) {\n flushLines();\n this._drawTriangles(options, particle, link, p1Destinations, pos1, pos2, context);\n }\n if (link.opacity <= minOpacity || width <= minWidth) {\n continue;\n }\n if (!linkOpts.enable) {\n continue;\n }\n let opacity = link.opacity, colorLine = link.color;\n const twinkleRgb = twinkle?.enable && (0,_tsparticles_engine__WEBPACK_IMPORTED_MODULE_0__.getRandom)() < twinkle.frequency ? (0,_tsparticles_engine__WEBPACK_IMPORTED_MODULE_0__.rangeColorToRgb)(this._engine, twinkle.color) : undefined;\n if (twinkle && twinkleRgb) {\n colorLine = twinkleRgb;\n opacity = (0,_tsparticles_engine__WEBPACK_IMPORTED_MODULE_0__.getRangeValue)(twinkle.opacity);\n }\n if (!colorLine) {\n const linkColor = linkOpts.id !== undefined ? this._container.particles.linksColors.get(linkOpts.id) : this._container.particles.linksColor;\n colorLine = (0,_tsparticles_engine__WEBPACK_IMPORTED_MODULE_0__.getLinkColor)(particle, link.destination, linkColor);\n }\n if (!colorLine) {\n continue;\n }\n const colorStyle = this._getCachedStyle(colorLine);\n if (colorStyle !== currentColorStyle || width !== currentWidth || opacity !== currentAlpha) {\n flushLines();\n context.strokeStyle = colorStyle;\n context.lineWidth = width;\n context.globalAlpha = opacity;\n currentColorStyle = colorStyle;\n currentWidth = width;\n currentAlpha = opacity;\n context.beginPath();\n pathOpen = true;\n }\n if (link.isWarped) {\n const canvasSize = this._container.canvas.size, dx = pos2.x - pos1.x, dy = pos2.y - pos1.y;\n let sx = _tsparticles_engine__WEBPACK_IMPORTED_MODULE_0__.originPoint.x, sy = _tsparticles_engine__WEBPACK_IMPORTED_MODULE_0__.originPoint.y;\n if (Math.abs(dx) > canvasSize.width * _tsparticles_engine__WEBPACK_IMPORTED_MODULE_0__.half) {\n sx = dx > minDistance ? -canvasSize.width : canvasSize.width;\n }\n if (Math.abs(dy) > canvasSize.height * _tsparticles_engine__WEBPACK_IMPORTED_MODULE_0__.half) {\n sy = dy > minDistance ? -canvasSize.height : canvasSize.height;\n }\n context.moveTo(pos1.x, pos1.y);\n context.lineTo(pos2.x + sx, pos2.y + sy);\n context.moveTo(pos1.x - sx, pos1.y - sy);\n context.lineTo(pos2.x, pos2.y);\n } else {\n context.moveTo(pos1.x, pos1.y);\n context.lineTo(pos2.x, pos2.y);\n }\n }\n flushLines();\n context.globalAlpha = originalAlpha;\n }\n init() {\n this._freqs.links.clear();\n this._freqs.triangles.clear();\n this._colorCache.clear();\n return Promise.resolve();\n }\n particleCreated(particle) {\n particle.links = [];\n if (!particle.options.links) {\n return;\n }\n particle.linksDistance = particle.options.links.distance;\n particle.linksWidth = particle.options.links.width;\n const ratio = this._container.retina.pixelRatio;\n particle.retina.linksDistance = particle.linksDistance * ratio;\n particle.retina.linksWidth = particle.linksWidth * ratio;\n }\n particleDestroyed(particle) {\n particle.links = [];\n }\n _drawTriangles(options, p1, link, p1Destinations, pos1, pos2, context) {\n const p2 = link.destination, triangleOptions = options.links?.triangles;\n if (!triangleOptions?.enable || !p2.options.links?.triangles.enable) {\n return;\n }\n const p2Links = p2.links;\n if (!p2Links?.length) {\n return;\n }\n for (const vertex of p2Links){\n if (vertex.isWarped || this._getLinkFrequency(p2, vertex.destination) > p2.options.links.frequency || !p1Destinations.has(vertex.destination.id)) {\n continue;\n }\n const p3 = vertex.destination;\n if (this._getTriangleFrequency(p1, p2, p3) > (options.links?.triangles.frequency ?? defaultFrequency)) {\n continue;\n }\n const opacityTriangle = triangleOptions.opacity ?? (link.opacity + vertex.opacity) * _tsparticles_engine__WEBPACK_IMPORTED_MODULE_0__.half, colorTriangle = (0,_tsparticles_engine__WEBPACK_IMPORTED_MODULE_0__.rangeColorToRgb)(this._engine, triangleOptions.color) ?? link.color;\n if (!colorTriangle || opacityTriangle <= minOpacity) {\n continue;\n }\n const pos3 = p3.getPosition();\n context.save();\n context.fillStyle = this._getCachedStyle(colorTriangle);\n context.globalAlpha = opacityTriangle;\n context.beginPath();\n context.moveTo(pos1.x, pos1.y);\n context.lineTo(pos2.x, pos2.y);\n context.lineTo(pos3.x, pos3.y);\n context.closePath();\n context.fill();\n context.restore();\n }\n }\n _getCachedStyle(rgb) {\n const key = `${rgb.r},${rgb.g},${rgb.b}`;\n let style = this._colorCache.get(key);\n if (!style) {\n style = (0,_tsparticles_engine__WEBPACK_IMPORTED_MODULE_0__.getStyleFromRgb)(rgb, this._container.hdr);\n this._colorCache.set(key, style);\n }\n return style;\n }\n _getLinkFrequency(p1, p2) {\n return (0,_Utils_js__WEBPACK_IMPORTED_MODULE_1__.setLinkFrequency)([\n p1,\n p2\n ], this._freqs.links);\n }\n _getTriangleFrequency(p1, p2, p3) {\n return (0,_Utils_js__WEBPACK_IMPORTED_MODULE_1__.setLinkFrequency)([\n p1,\n p2,\n p3\n ], this._freqs.triangles);\n }\n}\n\n\n//# sourceURL=webpack://@tsparticles/interaction-particles-links/./dist/browser/LinkInstance.js?\n}");
27
27
 
28
28
  /***/ },
29
29
 
@@ -4,7 +4,7 @@
4
4
  * Demo / Generator : https://particles.js.org/
5
5
  * GitHub : https://www.github.com/matteobruni/tsparticles
6
6
  * How to use? : Check the GitHub README
7
- * v4.0.0-alpha.26
7
+ * v4.0.0-alpha.28
8
8
  */
9
9
  "use strict";
10
10
  /*
@@ -4,7 +4,7 @@
4
4
  * Demo / Generator : https://particles.js.org/
5
5
  * GitHub : https://www.github.com/matteobruni/tsparticles
6
6
  * How to use? : Check the GitHub README
7
- * v4.0.0-alpha.26
7
+ * v4.0.0-alpha.28
8
8
  */
9
9
  "use strict";
10
10
  /*
@@ -1,75 +1,50 @@
1
1
  import { getLinkColor as engineGetLinkColor, getRandom, getRangeValue, getStyleFromRgb, half, originPoint, rangeColorToRgb, } from "@tsparticles/engine";
2
2
  import { setLinkFrequency } from "./Utils.js";
3
- const minOpacity = 0, minDistance = 0, minWidth = 0, maxFrequency = 1, defaultFrequency = 0, opacitySteps = 10, defaultWidth = 0, triangleCoordsCount = 6, lineCoordsCount = 4, x1Offset = 0, y1Offset = 1, x2Offset = 2, y2Offset = 3, x3Offset = 4, y3Offset = 5;
3
+ const minOpacity = 0, minWidth = 0, minDistance = 0, maxFrequency = 1, defaultFrequency = 0;
4
4
  export class LinkInstance {
5
5
  _colorCache = new Map();
6
6
  _container;
7
7
  _engine;
8
8
  _freqs;
9
- _lineBatches = new Map();
10
- _triangleBatches = new Map();
11
9
  constructor(container, engine) {
12
10
  this._container = container;
13
11
  this._engine = engine;
14
12
  this._freqs = { links: new Map(), triangles: new Map() };
15
13
  }
16
- draw(context) {
17
- for (const [, batch] of this._triangleBatches) {
18
- context.save();
19
- context.fillStyle = batch.colorStyle;
20
- context.globalAlpha = batch.opacity;
21
- context.beginPath();
22
- for (let i = 0; i < batch.coords.length; i += triangleCoordsCount) {
23
- const x1 = batch.coords[i + x1Offset] ?? originPoint.x, y1 = batch.coords[i + y1Offset] ?? originPoint.y, x2 = batch.coords[i + x2Offset] ?? originPoint.x, y2 = batch.coords[i + y2Offset] ?? originPoint.y, x3 = batch.coords[i + x3Offset] ?? originPoint.x, y3 = batch.coords[i + y3Offset] ?? originPoint.y;
24
- context.moveTo(x1, y1);
25
- context.lineTo(x2, y2);
26
- context.lineTo(x3, y3);
27
- }
28
- context.fill();
29
- context.restore();
30
- }
31
- for (const [, batch] of this._lineBatches) {
32
- context.save();
33
- context.strokeStyle = batch.colorStyle;
34
- context.lineWidth = batch.width;
35
- context.globalAlpha = batch.opacity;
36
- context.beginPath();
37
- for (let i = 0; i < batch.coords.length; i += lineCoordsCount) {
38
- const x1 = batch.coords[i + x1Offset] ?? originPoint.x, y1 = batch.coords[i + y1Offset] ?? originPoint.y, x2 = batch.coords[i + x2Offset] ?? originPoint.x, y2 = batch.coords[i + y2Offset] ?? originPoint.y;
39
- context.moveTo(x1, y1);
40
- context.lineTo(x2, y2);
41
- }
42
- context.stroke();
43
- context.restore();
44
- }
45
- this._lineBatches.clear();
46
- this._triangleBatches.clear();
47
- }
48
- drawParticle(_context, particle) {
14
+ drawParticle(context, particle) {
49
15
  const { links, options } = particle;
50
16
  if (!links?.length || !options.links) {
51
17
  return;
52
18
  }
53
- const canvasSize = this._container.canvas.size, pos1 = particle.getPosition(), linkOpts = options.links;
19
+ const linkOpts = options.links, width = particle.retina.linksWidth ?? minWidth, pos1 = particle.getPosition(), twinkle = particle.options["twinkle"]?.links, trianglesEnabled = linkOpts.triangles.enable, p1Destinations = trianglesEnabled ? new Set(links.map(l => l.destination.id)) : null, originalAlpha = context.globalAlpha;
20
+ let currentColorStyle = "", currentWidth = -1, currentAlpha = -1, pathOpen = false;
21
+ const flushLines = () => {
22
+ if (pathOpen) {
23
+ context.stroke();
24
+ pathOpen = false;
25
+ }
26
+ };
54
27
  for (const link of links) {
55
28
  if (linkOpts.frequency < maxFrequency &&
56
29
  this._getLinkFrequency(particle, link.destination) > linkOpts.frequency) {
57
30
  continue;
58
31
  }
59
- if (!link.isWarped) {
60
- this._collectTriangles(options, particle, link, links, pos1);
32
+ const pos2 = link.destination.getPosition();
33
+ if (trianglesEnabled && !link.isWarped && p1Destinations) {
34
+ flushLines();
35
+ this._drawTriangles(options, particle, link, p1Destinations, pos1, pos2, context);
36
+ }
37
+ if (link.opacity <= minOpacity || width <= minWidth) {
38
+ continue;
61
39
  }
62
- if (link.opacity <= minOpacity || (particle.retina.linksWidth ?? minWidth) <= minWidth) {
40
+ if (!linkOpts.enable) {
63
41
  continue;
64
42
  }
65
43
  let opacity = link.opacity, colorLine = link.color;
66
- const twinkle = particle.options["twinkle"]?.lines;
67
- if (twinkle?.enable && getRandom() < twinkle.frequency) {
68
- const twinkleRgb = rangeColorToRgb(this._engine, twinkle.color);
69
- if (twinkleRgb) {
70
- colorLine = twinkleRgb;
71
- opacity = getRangeValue(twinkle.opacity);
72
- }
44
+ const twinkleRgb = twinkle?.enable && getRandom() < twinkle.frequency ? rangeColorToRgb(this._engine, twinkle.color) : undefined;
45
+ if (twinkle && twinkleRgb) {
46
+ colorLine = twinkleRgb;
47
+ opacity = getRangeValue(twinkle.opacity);
73
48
  }
74
49
  if (!colorLine) {
75
50
  const linkColor = linkOpts.id !== undefined
@@ -80,15 +55,20 @@ export class LinkInstance {
80
55
  if (!colorLine) {
81
56
  continue;
82
57
  }
83
- const qOpacity = Math.ceil(opacity * opacitySteps) / opacitySteps, colorStyle = this._getCachedStyle(colorLine), width = particle.retina.linksWidth ?? defaultWidth, key = `${colorStyle}_${qOpacity}_${width}`;
84
- let batch = this._lineBatches.get(key);
85
- if (!batch) {
86
- batch = { colorStyle, opacity: qOpacity, width, coords: [] };
87
- this._lineBatches.set(key, batch);
58
+ const colorStyle = this._getCachedStyle(colorLine);
59
+ if (colorStyle !== currentColorStyle || width !== currentWidth || opacity !== currentAlpha) {
60
+ flushLines();
61
+ context.strokeStyle = colorStyle;
62
+ context.lineWidth = width;
63
+ context.globalAlpha = opacity;
64
+ currentColorStyle = colorStyle;
65
+ currentWidth = width;
66
+ currentAlpha = opacity;
67
+ context.beginPath();
68
+ pathOpen = true;
88
69
  }
89
- const pos2 = link.destination.getPosition();
90
70
  if (link.isWarped) {
91
- const dx = pos2.x - pos1.x, dy = pos2.y - pos1.y;
71
+ const canvasSize = this._container.canvas.size, dx = pos2.x - pos1.x, dy = pos2.y - pos1.y;
92
72
  let sx = originPoint.x, sy = originPoint.y;
93
73
  if (Math.abs(dx) > canvasSize.width * half) {
94
74
  sx = dx > minDistance ? -canvasSize.width : canvasSize.width;
@@ -96,13 +76,18 @@ export class LinkInstance {
96
76
  if (Math.abs(dy) > canvasSize.height * half) {
97
77
  sy = dy > minDistance ? -canvasSize.height : canvasSize.height;
98
78
  }
99
- const v2 = { x: pos2.x + sx, y: pos2.y + sy }, v1 = { x: pos1.x - sx, y: pos1.y - sy };
100
- batch.coords.push(pos1.x, pos1.y, v2.x, v2.y, v1.x, v1.y, pos2.x, pos2.y);
79
+ context.moveTo(pos1.x, pos1.y);
80
+ context.lineTo(pos2.x + sx, pos2.y + sy);
81
+ context.moveTo(pos1.x - sx, pos1.y - sy);
82
+ context.lineTo(pos2.x, pos2.y);
101
83
  }
102
84
  else {
103
- batch.coords.push(pos1.x, pos1.y, pos2.x, pos2.y);
85
+ context.moveTo(pos1.x, pos1.y);
86
+ context.lineTo(pos2.x, pos2.y);
104
87
  }
105
88
  }
89
+ flushLines();
90
+ context.globalAlpha = originalAlpha;
106
91
  }
107
92
  init() {
108
93
  this._freqs.links.clear();
@@ -124,16 +109,15 @@ export class LinkInstance {
124
109
  particleDestroyed(particle) {
125
110
  particle.links = [];
126
111
  }
127
- _collectTriangles(options, p1, link, p1Links, pos1) {
112
+ _drawTriangles(options, p1, link, p1Destinations, pos1, pos2, context) {
128
113
  const p2 = link.destination, triangleOptions = options.links?.triangles;
129
114
  if (!triangleOptions?.enable || !p2.options.links?.triangles.enable) {
130
115
  return;
131
116
  }
132
- const p1Destinations = new Set(p1Links.map(l => l.destination.id)), p2Links = p2.links;
117
+ const p2Links = p2.links;
133
118
  if (!p2Links?.length) {
134
119
  return;
135
120
  }
136
- const pos2 = p2.getPosition();
137
121
  for (const vertex of p2Links) {
138
122
  if (vertex.isWarped ||
139
123
  this._getLinkFrequency(p2, vertex.destination) > p2.options.links.frequency ||
@@ -144,18 +128,21 @@ export class LinkInstance {
144
128
  if (this._getTriangleFrequency(p1, p2, p3) > (options.links?.triangles.frequency ?? defaultFrequency)) {
145
129
  continue;
146
130
  }
147
- const opacityTriangle = Math.ceil((link.opacity + vertex.opacity) * half * opacitySteps) / opacitySteps, colorTriangle = rangeColorToRgb(this._engine, triangleOptions.color) ?? link.color;
148
- if (!colorTriangle) {
131
+ const opacityTriangle = triangleOptions.opacity ?? (link.opacity + vertex.opacity) * half, colorTriangle = rangeColorToRgb(this._engine, triangleOptions.color) ?? link.color;
132
+ if (!colorTriangle || opacityTriangle <= minOpacity) {
149
133
  continue;
150
134
  }
151
- const colorStyle = this._getCachedStyle(colorTriangle), key = `${colorStyle}_${opacityTriangle}`;
152
- let batch = this._triangleBatches.get(key);
153
- if (!batch) {
154
- batch = { colorStyle, opacity: opacityTriangle, coords: [] };
155
- this._triangleBatches.set(key, batch);
156
- }
157
135
  const pos3 = p3.getPosition();
158
- batch.coords.push(pos1.x, pos1.y, pos2.x, pos2.y, pos3.x, pos3.y);
136
+ context.save();
137
+ context.fillStyle = this._getCachedStyle(colorTriangle);
138
+ context.globalAlpha = opacityTriangle;
139
+ context.beginPath();
140
+ context.moveTo(pos1.x, pos1.y);
141
+ context.lineTo(pos2.x, pos2.y);
142
+ context.lineTo(pos3.x, pos3.y);
143
+ context.closePath();
144
+ context.fill();
145
+ context.restore();
159
146
  }
160
147
  }
161
148
  _getCachedStyle(rgb) {
package/esm/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  export async function loadParticlesLinksInteraction(engine) {
2
- engine.checkVersion("4.0.0-alpha.26");
2
+ engine.checkVersion("4.0.0-alpha.28");
3
3
  await engine.register(async (e) => {
4
4
  const [{ ensureInteractivityPluginLoaded }, { LinksPlugin },] = await Promise.all([
5
5
  import("@tsparticles/plugin-interactivity"),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tsparticles/interaction-particles-links",
3
- "version": "4.0.0-alpha.26",
3
+ "version": "4.0.0-alpha.28",
4
4
  "description": "tsParticles links particles interaction",
5
5
  "homepage": "https://particles.js.org",
6
6
  "repository": {
@@ -87,9 +87,9 @@
87
87
  "./package.json": "./package.json"
88
88
  },
89
89
  "dependencies": {
90
- "@tsparticles/canvas-utils": "4.0.0-alpha.26",
91
- "@tsparticles/engine": "4.0.0-alpha.26",
92
- "@tsparticles/plugin-interactivity": "4.0.0-alpha.26"
90
+ "@tsparticles/canvas-utils": "4.0.0-alpha.28",
91
+ "@tsparticles/engine": "4.0.0-alpha.28",
92
+ "@tsparticles/plugin-interactivity": "4.0.0-alpha.28"
93
93
  },
94
94
  "publishConfig": {
95
95
  "access": "public"
package/report.html CHANGED
@@ -3,7 +3,7 @@
3
3
  <head>
4
4
  <meta charset="UTF-8"/>
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1"/>
6
- <title>@tsparticles/interaction-particles-links [26 Feb 2026 at 18:10]</title>
6
+ <title>@tsparticles/interaction-particles-links [15 Mar 2026 at 13:48]</title>
7
7
  <link rel="shortcut icon" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAMAAACdt4HsAAABrVBMVEUAAAD///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////+O1foceMD///+J0/qK1Pr7/v8Xdr/9///W8P4UdL7L7P0Scr2r4Pyj3vwad8D5/f/2/f+55f3E6f34+/2H0/ojfMKpzOd0rNgQcb3F3O/j9f7c8v6g3Pz0/P/w+v/q+P7n9v6T1/uQ1vuE0vqLut/y+v+Z2fvt+f+15Pzv9fuc2/vR7v2V2Pvd6/bg9P7I6/285/2y4/yp3/zp8vk8i8kqgMT7/P31+fyv4vxGkcz6/P6/6P3j7vfS5PNnpNUxhcbO7f7F6v3O4vHK3/DA2u631Ouy0eqXweKJud5wqthfoNMMbLvY8f73+v2dxeR8sNtTmdDx9/zX6PSjyeaCtd1YnNGX2PuQveCGt95Nls42h8dLlM3F4vBtAAAAM3RSTlMAAyOx0/sKBvik8opWGBMOAe3l1snDm2E9LSb06eHcu5JpHbarfHZCN9CBb08zzkdNS0kYaptYAAAFV0lEQVRYw92X51/aYBDHHS2O2qqttVbrqNq9m+TJIAYIShBkWwqIiCgoWvfeq7Z2/s29hyQNyUcR7LveGwVyXy6XH8/9rqxglLfUPLxVduUor3h0rfp2TYvpivk37929TkG037hffoX0+peVtZQc1589rigVUdXS/ABSAyEmGIO/1XfvldSK8vs3OqB6u3m0nxmIrvgB0dj7rr7Y9IbuF68hnfFaiHA/sxqm0wciIG43P60qKv9WXWc1RXGh/mFESFABTSBi0sNAKzqet17eCtOb3kZIDwxEEU0oAIJGYxNBDhBND29e0rtXXbcpuPmED9IhEAAQ/AXEaF8EPmnrrKsv0LvWR3fg5sWDNAFZOgAgaKvZDogHNU9MFwnnYROkc56RD5CjAbQX9Ow4g7upCsvYu55aSI/Nj0H1akgKQEUM94dwK65hYRmFU9MIcH/fqJYOZYcnuJSU/waKDgTOEVaVKhwrTRP5XzgSpAITYzom7UvkhFX5VutmxeNnWDjjswTKTyfgluNDGbUpWissXhF3s7mlSml+czWkg3D0l1nNjGNjz3myOQOa1KM/jOS6ebdbAVTCi4gljHSFrviza7tOgRWcS0MOUX9zdNgag5w7rRqA44Lzw0hr1WqES36dFliSJFlh2rXIae3FFcDDgKdxrUIDePr8jGcSClV1u7A9xeN0ModY/pHMxmR1EzRh8TJiwqsHmKW0l4FCEZI+jHio+JdPPE9qwQtTRxku2D8sIeRL2LnxWSllANCQGOIiqVHAz2ye2JR0DcH+HoxDkaADLjgxjKQ+AwCX/g0+DNgdG0ukYCONAe+dbc2IAc6fwt1ARoDSezNHxV2Cmzwv3O6lDMV55edBGwGK9n1+x2F8EDfAGCxug8MhpsMEcTEAWf3rx2vZhe/LAmtIn/6apE6PN0ULKgywD9mmdxbmFl3OvD5AS5fW5zLbv/YHmcsBTjf/afDz3MaZTVCfAP9z6/Bw6ycv8EUBWJIn9zYcoAWWlW9+OzO3vkTy8H+RANLmdrpOuYWdZYEXpo+TlCJrW5EARb7fF+bWdqf3hhyZI1nWJQHgznErZhbjoEsWqi8dQNoE294aldzFurwSABL2XXMf9+H1VQGke9exw5P/AnA5Pv5ngMul7LOvO922iwACu8WkCwLCafvM4CeWPxfA8lNHcWZSoi8EwMAIciKX2Z4SWCMAa3snCZ/G4EA8D6CMLNFsGQhkkz/gQNEBbPCbWsxGUpYVu3z8IyNAknwJkfPMEhLyrdi5RTyUVACkw4GSFRNWJNEW+fgPGwHD8/JxnRuLabN4CGNRkAE23na2+VmEAUmrYymSGjMAYqH84YUIyzgzs3XC7gNgH36Vcc4zKY9o9fgPBXUAiHHwVboBHGLiX6Zcjp1f2wu4tvzZKo0ecPnDtQYDQvJXaBeNzce45Fp28ZQLrEZVuFqgBwOalArKXnW1UzlnSusQKJqKYNuz4tOnI6sZG4zanpemv+7ySU2jbA9h6uhcgpfy6G2PahirDZ6zvq6zDduMVFTKvzw8wgyEdelwY9in3XkEPs3osJuwRQ4qTkfzifndg9Gfc4pdsu82+tTnHZTBa2EAMrqr2t43pguc8tNm7JQVQ2S0ukj2d22dhXYP0/veWtwKrCkNoNimAN5+Xr/oLrxswKbVJjteWrX7eR63o4j9q0GxnaBdWgGA5VStpanIjQmEhV0/nVt5VOFUvix6awJhPcAaTEShgrG+iGyvb5a0Ndb1YGHFPEwoqAinoaykaID1o1pdPNu7XsnCKQ3R+hwWIIhGvORcJUBYXe3Xa3vq/mF/N9V13ugufMkfXn+KHsRD0B8AAAAASUVORK5CYII=" type="image/x-icon" />
8
8
 
9
9
  <script>
@@ -4,7 +4,7 @@
4
4
  * Demo / Generator : https://particles.js.org/
5
5
  * GitHub : https://www.github.com/matteobruni/tsparticles
6
6
  * How to use? : Check the GitHub README
7
- * v4.0.0-alpha.26
7
+ * v4.0.0-alpha.28
8
8
  */
9
9
  /*
10
10
  * ATTENTION: The "eval" devtool has been used (maybe by default in mode: "development").
@@ -84,7 +84,7 @@ eval("{__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpa
84
84
  \*******************************/
85
85
  (__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) {
86
86
 
87
- eval("{__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ Links: () => (/* reexport safe */ _Options_Classes_Links_js__WEBPACK_IMPORTED_MODULE_0__.Links),\n/* harmony export */ LinksShadow: () => (/* reexport safe */ _Options_Classes_LinksShadow_js__WEBPACK_IMPORTED_MODULE_1__.LinksShadow),\n/* harmony export */ LinksTriangle: () => (/* reexport safe */ _Options_Classes_LinksTriangle_js__WEBPACK_IMPORTED_MODULE_2__.LinksTriangle),\n/* harmony export */ loadParticlesLinksInteraction: () => (/* binding */ loadParticlesLinksInteraction)\n/* harmony export */ });\n/* harmony import */ var _Options_Classes_Links_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./Options/Classes/Links.js */ \"./dist/browser/Options/Classes/Links.js\");\n/* harmony import */ var _Options_Classes_LinksShadow_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./Options/Classes/LinksShadow.js */ \"./dist/browser/Options/Classes/LinksShadow.js\");\n/* harmony import */ var _Options_Classes_LinksTriangle_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./Options/Classes/LinksTriangle.js */ \"./dist/browser/Options/Classes/LinksTriangle.js\");\nasync function loadParticlesLinksInteraction(engine) {\n engine.checkVersion(\"4.0.0-alpha.26\");\n await engine.register(async (e)=>{\n const [{ ensureInteractivityPluginLoaded }, { LinksPlugin }] = await Promise.all([\n Promise.resolve(/*! import() */).then(__webpack_require__.t.bind(__webpack_require__, /*! @tsparticles/plugin-interactivity */ \"@tsparticles/plugin-interactivity\", 19)),\n __webpack_require__.e(/*! import() */ \"dist_browser_LinksPlugin_js\").then(__webpack_require__.bind(__webpack_require__, /*! ./LinksPlugin.js */ \"./dist/browser/LinksPlugin.js\"))\n ]);\n ensureInteractivityPluginLoaded(e);\n e.addPlugin(new LinksPlugin(e));\n e.addInteractor?.(\"particlesLinks\", async (container)=>{\n const { Linker } = await __webpack_require__.e(/*! import() */ \"dist_browser_Linker_js\").then(__webpack_require__.bind(__webpack_require__, /*! ./Linker.js */ \"./dist/browser/Linker.js\"));\n return new Linker(container, e);\n });\n });\n}\n\n\n\n\n\n//# sourceURL=webpack://@tsparticles/interaction-particles-links/./dist/browser/index.js?\n}");
87
+ eval("{__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ Links: () => (/* reexport safe */ _Options_Classes_Links_js__WEBPACK_IMPORTED_MODULE_0__.Links),\n/* harmony export */ LinksShadow: () => (/* reexport safe */ _Options_Classes_LinksShadow_js__WEBPACK_IMPORTED_MODULE_1__.LinksShadow),\n/* harmony export */ LinksTriangle: () => (/* reexport safe */ _Options_Classes_LinksTriangle_js__WEBPACK_IMPORTED_MODULE_2__.LinksTriangle),\n/* harmony export */ loadParticlesLinksInteraction: () => (/* binding */ loadParticlesLinksInteraction)\n/* harmony export */ });\n/* harmony import */ var _Options_Classes_Links_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./Options/Classes/Links.js */ \"./dist/browser/Options/Classes/Links.js\");\n/* harmony import */ var _Options_Classes_LinksShadow_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./Options/Classes/LinksShadow.js */ \"./dist/browser/Options/Classes/LinksShadow.js\");\n/* harmony import */ var _Options_Classes_LinksTriangle_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./Options/Classes/LinksTriangle.js */ \"./dist/browser/Options/Classes/LinksTriangle.js\");\nasync function loadParticlesLinksInteraction(engine) {\n engine.checkVersion(\"4.0.0-alpha.28\");\n await engine.register(async (e)=>{\n const [{ ensureInteractivityPluginLoaded }, { LinksPlugin }] = await Promise.all([\n Promise.resolve(/*! import() */).then(__webpack_require__.t.bind(__webpack_require__, /*! @tsparticles/plugin-interactivity */ \"@tsparticles/plugin-interactivity\", 19)),\n __webpack_require__.e(/*! import() */ \"dist_browser_LinksPlugin_js\").then(__webpack_require__.bind(__webpack_require__, /*! ./LinksPlugin.js */ \"./dist/browser/LinksPlugin.js\"))\n ]);\n ensureInteractivityPluginLoaded(e);\n e.addPlugin(new LinksPlugin(e));\n e.addInteractor?.(\"particlesLinks\", async (container)=>{\n const { Linker } = await __webpack_require__.e(/*! import() */ \"dist_browser_Linker_js\").then(__webpack_require__.bind(__webpack_require__, /*! ./Linker.js */ \"./dist/browser/Linker.js\"));\n return new Linker(container, e);\n });\n });\n}\n\n\n\n\n\n//# sourceURL=webpack://@tsparticles/interaction-particles-links/./dist/browser/index.js?\n}");
88
88
 
89
89
  /***/ }
90
90
 
@@ -100,12 +100,6 @@ eval("{__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpa
100
100
  /******/ if (cachedModule !== undefined) {
101
101
  /******/ return cachedModule.exports;
102
102
  /******/ }
103
- /******/ // Check if module exists (development only)
104
- /******/ if (__webpack_modules__[moduleId] === undefined) {
105
- /******/ var e = new Error("Cannot find module '" + moduleId + "'");
106
- /******/ e.code = 'MODULE_NOT_FOUND';
107
- /******/ throw e;
108
- /******/ }
109
103
  /******/ // Create a new module (and put it into the cache)
110
104
  /******/ var module = __webpack_module_cache__[moduleId] = {
111
105
  /******/ // no module.id needed
@@ -114,6 +108,12 @@ eval("{__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpa
114
108
  /******/ };
115
109
  /******/
116
110
  /******/ // Execute the module function
111
+ /******/ if (!(moduleId in __webpack_modules__)) {
112
+ /******/ delete __webpack_module_cache__[moduleId];
113
+ /******/ var e = new Error("Cannot find module '" + moduleId + "'");
114
+ /******/ e.code = 'MODULE_NOT_FOUND';
115
+ /******/ throw e;
116
+ /******/ }
117
117
  /******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__);
118
118
  /******/
119
119
  /******/ // Return the exports of the module
@@ -1,2 +1,2 @@
1
- !function(e,t){if("object"==typeof exports&&"object"==typeof module)module.exports=t(require("@tsparticles/engine"),require("@tsparticles/plugin-interactivity"));else if("function"==typeof define&&define.amd)define(["@tsparticles/engine","@tsparticles/plugin-interactivity"],t);else{var r="object"==typeof exports?t(require("@tsparticles/engine"),require("@tsparticles/plugin-interactivity")):t(e.window,e.window);for(var i in r)("object"==typeof exports?exports:e)[i]=r[i]}}(this,(e,t)=>(()=>{"use strict";var r,i,o,n={303(t){t.exports=e},702(e){e.exports=t},925(e,t,r){r.d(t,{q:()=>s});var i=r(303),o=r(101),n=r(869);class s{blink;color;consent;distance;enable;frequency;id;opacity;shadow;triangles;warp;width;constructor(){this.blink=!1,this.color=new i.OptionsColor,this.color.value="#fff",this.consent=!1,this.distance=100,this.enable=!1,this.frequency=1,this.opacity=1,this.shadow=new o.s,this.triangles=new n.G,this.width=1,this.warp=!1}load(e){(0,i.isNull)(e)||(void 0!==e.id&&(this.id=e.id),void 0!==e.blink&&(this.blink=e.blink),this.color=i.OptionsColor.create(this.color,e.color),void 0!==e.consent&&(this.consent=e.consent),void 0!==e.distance&&(this.distance=e.distance),void 0!==e.enable&&(this.enable=e.enable),void 0!==e.frequency&&(this.frequency=e.frequency),void 0!==e.opacity&&(this.opacity=e.opacity),this.shadow.load(e.shadow),this.triangles.load(e.triangles),void 0!==e.width&&(this.width=e.width),void 0!==e.warp&&(this.warp=e.warp))}}},101(e,t,r){r.d(t,{s:()=>o});var i=r(303);class o{blur;color;enable;constructor(){this.blur=5,this.color=new i.OptionsColor,this.color.value="#000",this.enable=!1}load(e){(0,i.isNull)(e)||(void 0!==e.blur&&(this.blur=e.blur),this.color=i.OptionsColor.create(this.color,e.color),void 0!==e.enable&&(this.enable=e.enable))}}},869(e,t,r){r.d(t,{G:()=>o});var i=r(303);class o{color;enable;frequency;opacity;constructor(){this.enable=!1,this.frequency=1}load(e){(0,i.isNull)(e)||(void 0!==e.color&&(this.color=i.OptionsColor.create(this.color,e.color)),void 0!==e.enable&&(this.enable=e.enable),void 0!==e.frequency&&(this.frequency=e.frequency),void 0!==e.opacity&&(this.opacity=e.opacity))}}}},s={};function a(e){var t=s[e];if(void 0!==t)return t.exports;var r=s[e]={exports:{}};return n[e](r,r.exports,a),r.exports}a.m=n,c=Object.getPrototypeOf?e=>Object.getPrototypeOf(e):e=>e.__proto__,a.t=function(e,t){if(1&t&&(e=this(e)),8&t||"object"==typeof e&&e&&(4&t&&e.__esModule||16&t&&"function"==typeof e.then))return e;var r=Object.create(null);a.r(r);var i={};l=l||[null,c({}),c([]),c(c)];for(var o=2&t&&e;("object"==typeof o||"function"==typeof o)&&!~l.indexOf(o);o=c(o))Object.getOwnPropertyNames(o).forEach(t=>i[t]=()=>e[t]);return i.default=()=>e,a.d(r,i),r},a.d=(e,t)=>{for(var r in t)a.o(t,r)&&!a.o(e,r)&&Object.defineProperty(e,r,{enumerable:!0,get:t[r]})},a.f={},a.e=e=>Promise.all(Object.keys(a.f).reduce((t,r)=>(a.f[r](e,t),t),[])),a.u=e=>""+e+".min.js",a.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||Function("return this")()}catch(e){if("object"==typeof window)return window}}(),a.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),p={},a.l=(e,t,r,i)=>{if(p[e])return void p[e].push(t);if(void 0!==r)for(var o,n,s=document.getElementsByTagName("script"),l=0;l<s.length;l++){var c=s[l];if(c.getAttribute("src")==e||c.getAttribute("data-webpack")=="@tsparticles/interaction-particles-links:"+r){o=c;break}}o||(n=!0,(o=document.createElement("script")).charset="utf-8",a.nc&&o.setAttribute("nonce",a.nc),o.setAttribute("data-webpack","@tsparticles/interaction-particles-links:"+r),o.src=e),p[e]=[t];var d=(t,r)=>{o.onerror=o.onload=null,clearTimeout(u);var i=p[e];if(delete p[e],o.parentNode&&o.parentNode.removeChild(o),i&&i.forEach(e=>e(r)),t)return t(r)},u=setTimeout(d.bind(null,void 0,{type:"timeout",target:o}),12e4);o.onerror=d.bind(null,o.onerror),o.onload=d.bind(null,o.onload),n&&document.head.appendChild(o)},a.r=e=>{"u">typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},a.g.importScripts&&(d=a.g.location+"");var l,c,p,d,u=a.g.document;if(!d&&u&&(u.currentScript&&"SCRIPT"===u.currentScript.tagName.toUpperCase()&&(d=u.currentScript.src),!d)){var h=u.getElementsByTagName("script");if(h.length)for(var f=h.length-1;f>-1&&(!d||!/^http(s?):/.test(d));)d=h[f--].src}if(!d)throw Error("Automatic publicPath is not supported in this browser");a.p=d=d.replace(/^blob:/,"").replace(/#.*$/,"").replace(/\?.*$/,"").replace(/\/[^\/]+$/,"/"),r={203:0},a.f.j=(e,t)=>{var i=a.o(r,e)?r[e]:void 0;if(0!==i)if(i)t.push(i[2]);else{var o=new Promise((t,o)=>i=r[e]=[t,o]);t.push(i[2]=o);var n=a.p+a.u(e),s=Error();a.l(n,t=>{if(a.o(r,e)&&(0!==(i=r[e])&&(r[e]=void 0),i)){var o=t&&("load"===t.type?"missing":t.type),n=t&&t.target&&t.target.src;s.message="Loading chunk "+e+` failed.
2
- (`+o+": "+n+")",s.name="ChunkLoadError",s.type=o,s.request=n,i[1](s)}},"chunk-"+e,e)}},i=(e,t)=>{var i,o,[n,s,l]=t,c=0;if(n.some(e=>0!==r[e])){for(i in s)a.o(s,i)&&(a.m[i]=s[i]);l&&l(a)}for(e&&e(t);c<n.length;c++)o=n[c],a.o(r,o)&&r[o]&&r[o][0](),r[o]=0},(o=this.webpackChunk_tsparticles_interaction_particles_links=this.webpackChunk_tsparticles_interaction_particles_links||[]).forEach(i.bind(null,0)),o.push=i.bind(null,o.push.bind(o));var b={};a.r(b),a.d(b,{Links:()=>v.q,LinksShadow:()=>y.s,LinksTriangle:()=>g.G,loadParticlesLinksInteraction:()=>w});var v=a(925),y=a(101),g=a(869);async function w(e){e.checkVersion("4.0.0-alpha.26"),await e.register(async e=>{let[{ensureInteractivityPluginLoaded:t},{LinksPlugin:r}]=await Promise.all([Promise.resolve().then(a.t.bind(a,702,19)),a.e(628).then(a.bind(a,628))]);t(e),e.addPlugin(new r(e)),e.addInteractor?.("particlesLinks",async t=>{let{Linker:r}=await a.e(520).then(a.bind(a,520));return new r(t,e)})})}return b})());
1
+ !function(e,t){if("object"==typeof exports&&"object"==typeof module)module.exports=t(require("@tsparticles/engine"),require("@tsparticles/plugin-interactivity"));else if("function"==typeof define&&define.amd)define(["@tsparticles/engine","@tsparticles/plugin-interactivity"],t);else{var r="object"==typeof exports?t(require("@tsparticles/engine"),require("@tsparticles/plugin-interactivity")):t(e.window,e.window);for(var i in r)("object"==typeof exports?exports:e)[i]=r[i]}}(this,(e,t)=>(()=>{"use strict";var r,i,o,n={303(t){t.exports=e},702(e){e.exports=t},348(e,t,r){r.d(t,{q:()=>s});var i=r(303),o=r(724),n=r(200);class s{blink;color;consent;distance;enable;frequency;id;opacity;shadow;triangles;warp;width;constructor(){this.blink=!1,this.color=new i.OptionsColor,this.color.value="#fff",this.consent=!1,this.distance=100,this.enable=!1,this.frequency=1,this.opacity=1,this.shadow=new o.s,this.triangles=new n.G,this.width=1,this.warp=!1}load(e){(0,i.isNull)(e)||(void 0!==e.id&&(this.id=e.id),void 0!==e.blink&&(this.blink=e.blink),this.color=i.OptionsColor.create(this.color,e.color),void 0!==e.consent&&(this.consent=e.consent),void 0!==e.distance&&(this.distance=e.distance),void 0!==e.enable&&(this.enable=e.enable),void 0!==e.frequency&&(this.frequency=e.frequency),void 0!==e.opacity&&(this.opacity=e.opacity),this.shadow.load(e.shadow),this.triangles.load(e.triangles),void 0!==e.width&&(this.width=e.width),void 0!==e.warp&&(this.warp=e.warp))}}},724(e,t,r){r.d(t,{s:()=>o});var i=r(303);class o{blur;color;enable;constructor(){this.blur=5,this.color=new i.OptionsColor,this.color.value="#000",this.enable=!1}load(e){(0,i.isNull)(e)||(void 0!==e.blur&&(this.blur=e.blur),this.color=i.OptionsColor.create(this.color,e.color),void 0!==e.enable&&(this.enable=e.enable))}}},200(e,t,r){r.d(t,{G:()=>o});var i=r(303);class o{color;enable;frequency;opacity;constructor(){this.enable=!1,this.frequency=1}load(e){(0,i.isNull)(e)||(void 0!==e.color&&(this.color=i.OptionsColor.create(this.color,e.color)),void 0!==e.enable&&(this.enable=e.enable),void 0!==e.frequency&&(this.frequency=e.frequency),void 0!==e.opacity&&(this.opacity=e.opacity))}}}},s={};function a(e){var t=s[e];if(void 0!==t)return t.exports;var r=s[e]={exports:{}};return n[e](r,r.exports,a),r.exports}a.m=n,c=Object.getPrototypeOf?e=>Object.getPrototypeOf(e):e=>e.__proto__,a.t=function(e,t){if(1&t&&(e=this(e)),8&t||"object"==typeof e&&e&&(4&t&&e.__esModule||16&t&&"function"==typeof e.then))return e;var r=Object.create(null);a.r(r);var i={};l=l||[null,c({}),c([]),c(c)];for(var o=2&t&&e;("object"==typeof o||"function"==typeof o)&&!~l.indexOf(o);o=c(o))Object.getOwnPropertyNames(o).forEach(t=>i[t]=()=>e[t]);return i.default=()=>e,a.d(r,i),r},a.d=(e,t)=>{for(var r in t)a.o(t,r)&&!a.o(e,r)&&Object.defineProperty(e,r,{enumerable:!0,get:t[r]})},a.f={},a.e=e=>Promise.all(Object.keys(a.f).reduce((t,r)=>(a.f[r](e,t),t),[])),a.u=e=>""+e+".min.js",a.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||Function("return this")()}catch(e){if("object"==typeof window)return window}}(),a.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),p={},a.l=(e,t,r,i)=>{if(p[e])return void p[e].push(t);if(void 0!==r)for(var o,n,s=document.getElementsByTagName("script"),l=0;l<s.length;l++){var c=s[l];if(c.getAttribute("src")==e||c.getAttribute("data-webpack")=="@tsparticles/interaction-particles-links:"+r){o=c;break}}o||(n=!0,(o=document.createElement("script")).charset="utf-8",a.nc&&o.setAttribute("nonce",a.nc),o.setAttribute("data-webpack","@tsparticles/interaction-particles-links:"+r),o.src=e),p[e]=[t];var d=(t,r)=>{o.onerror=o.onload=null,clearTimeout(u);var i=p[e];if(delete p[e],o.parentNode&&o.parentNode.removeChild(o),i&&i.forEach(e=>e(r)),t)return t(r)},u=setTimeout(d.bind(null,void 0,{type:"timeout",target:o}),12e4);o.onerror=d.bind(null,o.onerror),o.onload=d.bind(null,o.onload),n&&document.head.appendChild(o)},a.r=e=>{"u">typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},a.g.importScripts&&(d=a.g.location+"");var l,c,p,d,u=a.g.document;if(!d&&u&&(u.currentScript&&"SCRIPT"===u.currentScript.tagName.toUpperCase()&&(d=u.currentScript.src),!d)){var h=u.getElementsByTagName("script");if(h.length)for(var f=h.length-1;f>-1&&(!d||!/^http(s?):/.test(d));)d=h[f--].src}if(!d)throw Error("Automatic publicPath is not supported in this browser");a.p=d=d.replace(/^blob:/,"").replace(/#.*$/,"").replace(/\?.*$/,"").replace(/\/[^\/]+$/,"/"),r={203:0},a.f.j=(e,t)=>{var i=a.o(r,e)?r[e]:void 0;if(0!==i)if(i)t.push(i[2]);else{var o=new Promise((t,o)=>i=r[e]=[t,o]);t.push(i[2]=o);var n=a.p+a.u(e),s=Error();a.l(n,t=>{if(a.o(r,e)&&(0!==(i=r[e])&&(r[e]=void 0),i)){var o=t&&("load"===t.type?"missing":t.type),n=t&&t.target&&t.target.src;s.message="Loading chunk "+e+` failed.
2
+ (`+o+": "+n+")",s.name="ChunkLoadError",s.type=o,s.request=n,i[1](s)}},"chunk-"+e,e)}},i=(e,t)=>{var i,o,[n,s,l]=t,c=0;if(n.some(e=>0!==r[e])){for(i in s)a.o(s,i)&&(a.m[i]=s[i]);l&&l(a)}for(e&&e(t);c<n.length;c++)o=n[c],a.o(r,o)&&r[o]&&r[o][0](),r[o]=0},(o=this.webpackChunk_tsparticles_interaction_particles_links=this.webpackChunk_tsparticles_interaction_particles_links||[]).forEach(i.bind(null,0)),o.push=i.bind(null,o.push.bind(o));var b={};a.r(b),a.d(b,{Links:()=>v.q,LinksShadow:()=>y.s,LinksTriangle:()=>g.G,loadParticlesLinksInteraction:()=>w});var v=a(348),y=a(724),g=a(200);async function w(e){e.checkVersion("4.0.0-alpha.28"),await e.register(async e=>{let[{ensureInteractivityPluginLoaded:t},{LinksPlugin:r}]=await Promise.all([Promise.resolve().then(a.t.bind(a,702,19)),a.e(13).then(a.bind(a,13))]);t(e),e.addPlugin(new r(e)),e.addInteractor?.("particlesLinks",async t=>{let{Linker:r}=await a.e(342).then(a.bind(a,342));return new r(t,e)})})}return b})());
@@ -15,7 +15,7 @@ export interface IParticlesFrequencies {
15
15
  triangles: Map<string, number>;
16
16
  }
17
17
  export interface ITwinkle {
18
- lines: {
18
+ links: {
19
19
  color: IRangeColor;
20
20
  enable: boolean;
21
21
  frequency: number;
@@ -5,15 +5,12 @@ export declare class LinkInstance implements IContainerPlugin {
5
5
  private readonly _container;
6
6
  private readonly _engine;
7
7
  private readonly _freqs;
8
- private readonly _lineBatches;
9
- private readonly _triangleBatches;
10
8
  constructor(container: LinkContainer, engine: Engine);
11
- draw(context: CanvasRenderingContext2D): void;
12
- drawParticle(_context: CanvasRenderingContext2D, particle: LinkParticle): void;
9
+ drawParticle(context: CanvasRenderingContext2D, particle: LinkParticle): void;
13
10
  init(): Promise<void>;
14
11
  particleCreated(particle: LinkParticle): void;
15
12
  particleDestroyed(particle: LinkParticle): void;
16
- private _collectTriangles;
13
+ private _drawTriangles;
17
14
  private _getCachedStyle;
18
15
  private _getLinkFrequency;
19
16
  private _getTriangleFrequency;
@@ -12,76 +12,51 @@
12
12
  exports.LinkInstance = void 0;
13
13
  const engine_1 = require("@tsparticles/engine");
14
14
  const Utils_js_1 = require("./Utils.js");
15
- const minOpacity = 0, minDistance = 0, minWidth = 0, maxFrequency = 1, defaultFrequency = 0, opacitySteps = 10, defaultWidth = 0, triangleCoordsCount = 6, lineCoordsCount = 4, x1Offset = 0, y1Offset = 1, x2Offset = 2, y2Offset = 3, x3Offset = 4, y3Offset = 5;
15
+ const minOpacity = 0, minWidth = 0, minDistance = 0, maxFrequency = 1, defaultFrequency = 0;
16
16
  class LinkInstance {
17
17
  _colorCache = new Map();
18
18
  _container;
19
19
  _engine;
20
20
  _freqs;
21
- _lineBatches = new Map();
22
- _triangleBatches = new Map();
23
21
  constructor(container, engine) {
24
22
  this._container = container;
25
23
  this._engine = engine;
26
24
  this._freqs = { links: new Map(), triangles: new Map() };
27
25
  }
28
- draw(context) {
29
- for (const [, batch] of this._triangleBatches) {
30
- context.save();
31
- context.fillStyle = batch.colorStyle;
32
- context.globalAlpha = batch.opacity;
33
- context.beginPath();
34
- for (let i = 0; i < batch.coords.length; i += triangleCoordsCount) {
35
- const x1 = batch.coords[i + x1Offset] ?? engine_1.originPoint.x, y1 = batch.coords[i + y1Offset] ?? engine_1.originPoint.y, x2 = batch.coords[i + x2Offset] ?? engine_1.originPoint.x, y2 = batch.coords[i + y2Offset] ?? engine_1.originPoint.y, x3 = batch.coords[i + x3Offset] ?? engine_1.originPoint.x, y3 = batch.coords[i + y3Offset] ?? engine_1.originPoint.y;
36
- context.moveTo(x1, y1);
37
- context.lineTo(x2, y2);
38
- context.lineTo(x3, y3);
39
- }
40
- context.fill();
41
- context.restore();
42
- }
43
- for (const [, batch] of this._lineBatches) {
44
- context.save();
45
- context.strokeStyle = batch.colorStyle;
46
- context.lineWidth = batch.width;
47
- context.globalAlpha = batch.opacity;
48
- context.beginPath();
49
- for (let i = 0; i < batch.coords.length; i += lineCoordsCount) {
50
- const x1 = batch.coords[i + x1Offset] ?? engine_1.originPoint.x, y1 = batch.coords[i + y1Offset] ?? engine_1.originPoint.y, x2 = batch.coords[i + x2Offset] ?? engine_1.originPoint.x, y2 = batch.coords[i + y2Offset] ?? engine_1.originPoint.y;
51
- context.moveTo(x1, y1);
52
- context.lineTo(x2, y2);
53
- }
54
- context.stroke();
55
- context.restore();
56
- }
57
- this._lineBatches.clear();
58
- this._triangleBatches.clear();
59
- }
60
- drawParticle(_context, particle) {
26
+ drawParticle(context, particle) {
61
27
  const { links, options } = particle;
62
28
  if (!links?.length || !options.links) {
63
29
  return;
64
30
  }
65
- const canvasSize = this._container.canvas.size, pos1 = particle.getPosition(), linkOpts = options.links;
31
+ const linkOpts = options.links, width = particle.retina.linksWidth ?? minWidth, pos1 = particle.getPosition(), twinkle = particle.options["twinkle"]?.links, trianglesEnabled = linkOpts.triangles.enable, p1Destinations = trianglesEnabled ? new Set(links.map(l => l.destination.id)) : null, originalAlpha = context.globalAlpha;
32
+ let currentColorStyle = "", currentWidth = -1, currentAlpha = -1, pathOpen = false;
33
+ const flushLines = () => {
34
+ if (pathOpen) {
35
+ context.stroke();
36
+ pathOpen = false;
37
+ }
38
+ };
66
39
  for (const link of links) {
67
40
  if (linkOpts.frequency < maxFrequency &&
68
41
  this._getLinkFrequency(particle, link.destination) > linkOpts.frequency) {
69
42
  continue;
70
43
  }
71
- if (!link.isWarped) {
72
- this._collectTriangles(options, particle, link, links, pos1);
44
+ const pos2 = link.destination.getPosition();
45
+ if (trianglesEnabled && !link.isWarped && p1Destinations) {
46
+ flushLines();
47
+ this._drawTriangles(options, particle, link, p1Destinations, pos1, pos2, context);
48
+ }
49
+ if (link.opacity <= minOpacity || width <= minWidth) {
50
+ continue;
73
51
  }
74
- if (link.opacity <= minOpacity || (particle.retina.linksWidth ?? minWidth) <= minWidth) {
52
+ if (!linkOpts.enable) {
75
53
  continue;
76
54
  }
77
55
  let opacity = link.opacity, colorLine = link.color;
78
- const twinkle = particle.options["twinkle"]?.lines;
79
- if (twinkle?.enable && (0, engine_1.getRandom)() < twinkle.frequency) {
80
- const twinkleRgb = (0, engine_1.rangeColorToRgb)(this._engine, twinkle.color);
81
- if (twinkleRgb) {
82
- colorLine = twinkleRgb;
83
- opacity = (0, engine_1.getRangeValue)(twinkle.opacity);
84
- }
56
+ const twinkleRgb = twinkle?.enable && (0, engine_1.getRandom)() < twinkle.frequency ? (0, engine_1.rangeColorToRgb)(this._engine, twinkle.color) : undefined;
57
+ if (twinkle && twinkleRgb) {
58
+ colorLine = twinkleRgb;
59
+ opacity = (0, engine_1.getRangeValue)(twinkle.opacity);
85
60
  }
86
61
  if (!colorLine) {
87
62
  const linkColor = linkOpts.id !== undefined
@@ -92,15 +67,20 @@
92
67
  if (!colorLine) {
93
68
  continue;
94
69
  }
95
- const qOpacity = Math.ceil(opacity * opacitySteps) / opacitySteps, colorStyle = this._getCachedStyle(colorLine), width = particle.retina.linksWidth ?? defaultWidth, key = `${colorStyle}_${qOpacity}_${width}`;
96
- let batch = this._lineBatches.get(key);
97
- if (!batch) {
98
- batch = { colorStyle, opacity: qOpacity, width, coords: [] };
99
- this._lineBatches.set(key, batch);
70
+ const colorStyle = this._getCachedStyle(colorLine);
71
+ if (colorStyle !== currentColorStyle || width !== currentWidth || opacity !== currentAlpha) {
72
+ flushLines();
73
+ context.strokeStyle = colorStyle;
74
+ context.lineWidth = width;
75
+ context.globalAlpha = opacity;
76
+ currentColorStyle = colorStyle;
77
+ currentWidth = width;
78
+ currentAlpha = opacity;
79
+ context.beginPath();
80
+ pathOpen = true;
100
81
  }
101
- const pos2 = link.destination.getPosition();
102
82
  if (link.isWarped) {
103
- const dx = pos2.x - pos1.x, dy = pos2.y - pos1.y;
83
+ const canvasSize = this._container.canvas.size, dx = pos2.x - pos1.x, dy = pos2.y - pos1.y;
104
84
  let sx = engine_1.originPoint.x, sy = engine_1.originPoint.y;
105
85
  if (Math.abs(dx) > canvasSize.width * engine_1.half) {
106
86
  sx = dx > minDistance ? -canvasSize.width : canvasSize.width;
@@ -108,13 +88,18 @@
108
88
  if (Math.abs(dy) > canvasSize.height * engine_1.half) {
109
89
  sy = dy > minDistance ? -canvasSize.height : canvasSize.height;
110
90
  }
111
- const v2 = { x: pos2.x + sx, y: pos2.y + sy }, v1 = { x: pos1.x - sx, y: pos1.y - sy };
112
- batch.coords.push(pos1.x, pos1.y, v2.x, v2.y, v1.x, v1.y, pos2.x, pos2.y);
91
+ context.moveTo(pos1.x, pos1.y);
92
+ context.lineTo(pos2.x + sx, pos2.y + sy);
93
+ context.moveTo(pos1.x - sx, pos1.y - sy);
94
+ context.lineTo(pos2.x, pos2.y);
113
95
  }
114
96
  else {
115
- batch.coords.push(pos1.x, pos1.y, pos2.x, pos2.y);
97
+ context.moveTo(pos1.x, pos1.y);
98
+ context.lineTo(pos2.x, pos2.y);
116
99
  }
117
100
  }
101
+ flushLines();
102
+ context.globalAlpha = originalAlpha;
118
103
  }
119
104
  init() {
120
105
  this._freqs.links.clear();
@@ -136,16 +121,15 @@
136
121
  particleDestroyed(particle) {
137
122
  particle.links = [];
138
123
  }
139
- _collectTriangles(options, p1, link, p1Links, pos1) {
124
+ _drawTriangles(options, p1, link, p1Destinations, pos1, pos2, context) {
140
125
  const p2 = link.destination, triangleOptions = options.links?.triangles;
141
126
  if (!triangleOptions?.enable || !p2.options.links?.triangles.enable) {
142
127
  return;
143
128
  }
144
- const p1Destinations = new Set(p1Links.map(l => l.destination.id)), p2Links = p2.links;
129
+ const p2Links = p2.links;
145
130
  if (!p2Links?.length) {
146
131
  return;
147
132
  }
148
- const pos2 = p2.getPosition();
149
133
  for (const vertex of p2Links) {
150
134
  if (vertex.isWarped ||
151
135
  this._getLinkFrequency(p2, vertex.destination) > p2.options.links.frequency ||
@@ -156,18 +140,21 @@
156
140
  if (this._getTriangleFrequency(p1, p2, p3) > (options.links?.triangles.frequency ?? defaultFrequency)) {
157
141
  continue;
158
142
  }
159
- const opacityTriangle = Math.ceil((link.opacity + vertex.opacity) * engine_1.half * opacitySteps) / opacitySteps, colorTriangle = (0, engine_1.rangeColorToRgb)(this._engine, triangleOptions.color) ?? link.color;
160
- if (!colorTriangle) {
143
+ const opacityTriangle = triangleOptions.opacity ?? (link.opacity + vertex.opacity) * engine_1.half, colorTriangle = (0, engine_1.rangeColorToRgb)(this._engine, triangleOptions.color) ?? link.color;
144
+ if (!colorTriangle || opacityTriangle <= minOpacity) {
161
145
  continue;
162
146
  }
163
- const colorStyle = this._getCachedStyle(colorTriangle), key = `${colorStyle}_${opacityTriangle}`;
164
- let batch = this._triangleBatches.get(key);
165
- if (!batch) {
166
- batch = { colorStyle, opacity: opacityTriangle, coords: [] };
167
- this._triangleBatches.set(key, batch);
168
- }
169
147
  const pos3 = p3.getPosition();
170
- batch.coords.push(pos1.x, pos1.y, pos2.x, pos2.y, pos3.x, pos3.y);
148
+ context.save();
149
+ context.fillStyle = this._getCachedStyle(colorTriangle);
150
+ context.globalAlpha = opacityTriangle;
151
+ context.beginPath();
152
+ context.moveTo(pos1.x, pos1.y);
153
+ context.lineTo(pos2.x, pos2.y);
154
+ context.lineTo(pos3.x, pos3.y);
155
+ context.closePath();
156
+ context.fill();
157
+ context.restore();
171
158
  }
172
159
  }
173
160
  _getCachedStyle(rgb) {
package/umd/index.js CHANGED
@@ -48,7 +48,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
48
48
  Object.defineProperty(exports, "__esModule", { value: true });
49
49
  exports.loadParticlesLinksInteraction = loadParticlesLinksInteraction;
50
50
  async function loadParticlesLinksInteraction(engine) {
51
- engine.checkVersion("4.0.0-alpha.26");
51
+ engine.checkVersion("4.0.0-alpha.28");
52
52
  await engine.register(async (e) => {
53
53
  const [{ ensureInteractivityPluginLoaded }, { LinksPlugin },] = await Promise.all([
54
54
  __syncRequire ? Promise.resolve().then(() => __importStar(require("@tsparticles/plugin-interactivity"))) : new Promise((resolve_1, reject_1) => { require(["@tsparticles/plugin-interactivity"], resolve_1, reject_1); }).then(__importStar),
package/463.min.js DELETED
@@ -1 +0,0 @@
1
- "use strict";(this.webpackChunk_tsparticles_interaction_particles_links=this.webpackChunk_tsparticles_interaction_particles_links||[]).push([[463],{463(i,e,t){t.d(e,{LinkInstance:()=>s});var n=t(303);function o(i,e){let t=[...i.map(i=>i.id)].sort((i,e)=>i-e).join("_"),o=e.get(t);return void 0===o&&(o=(0,n.getRandom)(),e.set(t,o)),o}class s{_colorCache=new Map;_container;_engine;_freqs;_lineBatches=new Map;_triangleBatches=new Map;constructor(i,e){this._container=i,this._engine=e,this._freqs={links:new Map,triangles:new Map}}draw(i){for(let[,e]of this._triangleBatches){i.save(),i.fillStyle=e.colorStyle,i.globalAlpha=e.opacity,i.beginPath();for(let t=0;t<e.coords.length;t+=6){let o=e.coords[t+0]??n.originPoint.x,s=e.coords[t+1]??n.originPoint.y,r=e.coords[t+2]??n.originPoint.x,l=e.coords[t+3]??n.originPoint.y,a=e.coords[t+4]??n.originPoint.x,c=e.coords[t+5]??n.originPoint.y;i.moveTo(o,s),i.lineTo(r,l),i.lineTo(a,c)}i.fill(),i.restore()}for(let[,e]of this._lineBatches){i.save(),i.strokeStyle=e.colorStyle,i.lineWidth=e.width,i.globalAlpha=e.opacity,i.beginPath();for(let t=0;t<e.coords.length;t+=4){let o=e.coords[t+0]??n.originPoint.x,s=e.coords[t+1]??n.originPoint.y,r=e.coords[t+2]??n.originPoint.x,l=e.coords[t+3]??n.originPoint.y;i.moveTo(o,s),i.lineTo(r,l)}i.stroke(),i.restore()}this._lineBatches.clear(),this._triangleBatches.clear()}drawParticle(i,e){let{links:t,options:o}=e;if(!t?.length||!o.links)return;let s=this._container.canvas.size,r=e.getPosition(),l=o.links;for(let i of t){if(l.frequency<1&&this._getLinkFrequency(e,i.destination)>l.frequency||(i.isWarped||this._collectTriangles(o,e,i,t,r),i.opacity<=0||(e.retina.linksWidth??0)<=0))continue;let a=i.opacity,c=i.color,h=e.options.twinkle?.lines;if(h?.enable&&(0,n.getRandom)()<h.frequency){let i=(0,n.rangeColorToRgb)(this._engine,h.color);i&&(c=i,a=(0,n.getRangeValue)(h.opacity))}if(!c){let t=void 0!==l.id?this._container.particles.linksColors.get(l.id):this._container.particles.linksColor;c=(0,n.getLinkColor)(e,i.destination,t)}if(!c)continue;let g=Math.ceil(10*a)/10,d=this._getCachedStyle(c),_=e.retina.linksWidth??0,y=`${d}_${g}_${_}`,p=this._lineBatches.get(y);p||(p={colorStyle:d,opacity:g,width:_,coords:[]},this._lineBatches.set(y,p));let f=i.destination.getPosition();if(i.isWarped){let i=f.x-r.x,e=f.y-r.y,t=n.originPoint.x,o=n.originPoint.y;Math.abs(i)>s.width*n.half&&(t=i>0?-s.width:s.width),Math.abs(e)>s.height*n.half&&(o=e>0?-s.height:s.height);let l={x:f.x+t,y:f.y+o},a={x:r.x-t,y:r.y-o};p.coords.push(r.x,r.y,l.x,l.y,a.x,a.y,f.x,f.y)}else p.coords.push(r.x,r.y,f.x,f.y)}}init(){return this._freqs.links.clear(),this._freqs.triangles.clear(),this._colorCache.clear(),Promise.resolve()}particleCreated(i){if(i.links=[],!i.options.links)return;i.linksDistance=i.options.links.distance,i.linksWidth=i.options.links.width;let e=this._container.retina.pixelRatio;i.retina.linksDistance=i.linksDistance*e,i.retina.linksWidth=i.linksWidth*e}particleDestroyed(i){i.links=[]}_collectTriangles(i,e,t,o,s){let r=t.destination,l=i.links?.triangles;if(!l?.enable||!r.options.links?.triangles.enable)return;let a=new Set(o.map(i=>i.destination.id)),c=r.links;if(!c?.length)return;let h=r.getPosition();for(let o of c){if(o.isWarped||this._getLinkFrequency(r,o.destination)>r.options.links.frequency||!a.has(o.destination.id))continue;let c=o.destination;if(this._getTriangleFrequency(e,r,c)>(i.links?.triangles.frequency??0))continue;let g=Math.ceil((t.opacity+o.opacity)*n.half*10)/10,d=(0,n.rangeColorToRgb)(this._engine,l.color)??t.color;if(!d)continue;let _=this._getCachedStyle(d),y=`${_}_${g}`,p=this._triangleBatches.get(y);p||(p={colorStyle:_,opacity:g,coords:[]},this._triangleBatches.set(y,p));let f=c.getPosition();p.coords.push(s.x,s.y,h.x,h.y,f.x,f.y)}}_getCachedStyle(i){let e=`${i.r},${i.g},${i.b}`,t=this._colorCache.get(e);return t||(t=(0,n.getStyleFromRgb)(i,this._container.hdr),this._colorCache.set(e,t)),t}_getLinkFrequency(i,e){return o([i,e],this._freqs.links)}_getTriangleFrequency(i,e,t){return o([i,e,t],this._freqs.triangles)}}}}]);
package/628.min.js DELETED
@@ -1 +0,0 @@
1
- "use strict";(this.webpackChunk_tsparticles_interaction_particles_links=this.webpackChunk_tsparticles_interaction_particles_links||[]).push([[628],{628(n,i,t){t.r(i),t.d(i,{LinksPlugin:()=>e});class e{id="links";_engine;constructor(n){this._engine=n}async getPlugin(n){let{LinkInstance:i}=await t.e(463).then(t.bind(t,463));return new i(n,this._engine)}loadOptions(){}needsPlugin(){return!0}}}}]);