@tsparticles/interaction-particles-links 4.0.0-alpha.8 → 4.0.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/212.min.js +1 -0
  2. package/513.min.js +1 -0
  3. package/866.min.js +1 -0
  4. package/README.md +5 -0
  5. package/browser/CircleWarp.js +34 -18
  6. package/browser/LinkInstance.js +141 -132
  7. package/browser/Linker.js +49 -44
  8. package/browser/LinksPlugin.js +5 -4
  9. package/browser/Options/Classes/Links.js +12 -0
  10. package/browser/Options/Classes/LinksShadow.js +3 -0
  11. package/browser/Options/Classes/LinksTriangle.js +4 -0
  12. package/browser/Utils.js +2 -83
  13. package/browser/index.js +10 -7
  14. package/cjs/CircleWarp.js +34 -18
  15. package/cjs/LinkInstance.js +141 -132
  16. package/cjs/Linker.js +49 -44
  17. package/cjs/LinksPlugin.js +5 -4
  18. package/cjs/Options/Classes/Links.js +12 -0
  19. package/cjs/Options/Classes/LinksShadow.js +3 -0
  20. package/cjs/Options/Classes/LinksTriangle.js +4 -0
  21. package/cjs/Utils.js +2 -83
  22. package/cjs/index.js +10 -7
  23. package/dist_browser_LinkInstance_js.js +3 -3
  24. package/dist_browser_Linker_js.js +3 -3
  25. package/dist_browser_LinksPlugin_js.js +2 -2
  26. package/esm/CircleWarp.js +34 -18
  27. package/esm/LinkInstance.js +141 -132
  28. package/esm/Linker.js +49 -44
  29. package/esm/LinksPlugin.js +5 -4
  30. package/esm/Options/Classes/Links.js +12 -0
  31. package/esm/Options/Classes/LinksShadow.js +3 -0
  32. package/esm/Options/Classes/LinksTriangle.js +4 -0
  33. package/esm/Utils.js +2 -83
  34. package/esm/index.js +10 -7
  35. package/package.json +4 -3
  36. package/report.html +84 -29
  37. package/tsparticles.interaction.particles.links.js +45 -33
  38. package/tsparticles.interaction.particles.links.min.js +2 -2
  39. package/types/CircleWarp.d.ts +2 -2
  40. package/types/Interfaces.d.ts +4 -2
  41. package/types/LinkInstance.d.ts +9 -9
  42. package/types/Linker.d.ts +7 -4
  43. package/types/LinksPlugin.d.ts +4 -4
  44. package/types/Types.d.ts +11 -20
  45. package/types/Utils.d.ts +1 -5
  46. package/umd/CircleWarp.js +33 -17
  47. package/umd/LinkInstance.js +139 -130
  48. package/umd/Linker.js +48 -43
  49. package/umd/LinksPlugin.js +5 -4
  50. package/umd/Options/Classes/Links.js +12 -0
  51. package/umd/Options/Classes/LinksShadow.js +3 -0
  52. package/umd/Options/Classes/LinksTriangle.js +4 -0
  53. package/umd/Utils.js +1 -85
  54. package/umd/index.js +10 -7
  55. package/161.min.js +0 -2
  56. package/161.min.js.LICENSE.txt +0 -1
  57. package/29.min.js +0 -2
  58. package/29.min.js.LICENSE.txt +0 -1
  59. package/94.min.js +0 -2
  60. package/94.min.js.LICENSE.txt +0 -1
  61. package/tsparticles.interaction.particles.links.min.js.LICENSE.txt +0 -1
package/212.min.js ADDED
@@ -0,0 +1 @@
1
+ "use strict";(this.webpackChunk_tsparticles_interaction_particles_links=this.webpackChunk_tsparticles_interaction_particles_links||[]).push([[212],{212(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;_freqs;_pluginManager;constructor(e,i){this._pluginManager=e,this._container=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,a=i.retina.linksWidth??0,o=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="",p=-1,_=-1,k=!1,u=()=>{k&&(e.stroke(),k=!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&&(u(),this._drawTriangles(l,i,g,h,o,t,e)),g.opacity<=0||a<=0||!s.enable)continue;let y=g.opacity,f=g.color,b=r?.enable&&(0,n.getRandom)()<r.frequency?(0,n.rangeColorToRgb)(this._pluginManager,r.color):void 0;if(r&&b&&(f=b,y=(0,n.getRangeValue)(r.opacity)),!f){let e=void 0!==s.id?this._container.particles.linksColors.get(s.id):this._container.particles.linksColor;f=(0,n.getLinkColor)(i,g.destination,e)}if(!f)continue;let q=this._getCachedStyle(f);if((q!==d||a!==p||y!==_)&&(u(),e.strokeStyle=q,e.lineWidth=a,e.globalAlpha=y,d=q,p=a,_=y,e.beginPath(),k=!0),g.isWarped){let i=this._container.canvas.size,l=t.x-o.x,s=t.y-o.y,a=n.originPoint.x,r=n.originPoint.y;Math.abs(l)>i.width*n.half&&(a=l>0?-i.width:i.width),Math.abs(s)>i.height*n.half&&(r=s>0?-i.height:i.height),e.moveTo(o.x,o.y),e.lineTo(t.x+a,t.y+r),e.moveTo(o.x-a,o.y-r),e.lineTo(t.x,t.y)}else e.moveTo(o.x,o.y),e.lineTo(t.x,t.y)}u(),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,a,o){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,p=(0,n.rangeColorToRgb)(this._pluginManager,c.color)??t.color;if(!p||d<=0)continue;let _=h.getPosition();o.save(),o.fillStyle=this._getCachedStyle(p),o.globalAlpha=d,o.beginPath(),o.moveTo(s.x,s.y),o.lineTo(a.x,a.y),o.lineTo(_.x,_.y),o.closePath(),o.fill(),o.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)}}}}]);
package/513.min.js ADDED
@@ -0,0 +1 @@
1
+ "use strict";(this.webpackChunk_tsparticles_interaction_particles_links=this.webpackChunk_tsparticles_interaction_particles_links||[]).push([[513],{513(n,i,t){t.r(i),t.d(i,{LinksPlugin:()=>s});class s{id="links";_pluginManager;constructor(n){this._pluginManager=n}async getPlugin(n){let{LinkInstance:i}=await t.e(212).then(t.bind(t,212));return new i(this._pluginManager,n)}loadOptions(){}needsPlugin(){return!0}}}}]);
package/866.min.js ADDED
@@ -0,0 +1 @@
1
+ "use strict";(this.webpackChunk_tsparticles_interaction_particles_links=this.webpackChunk_tsparticles_interaction_particles_links||[]).push([[866],{866(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(56),o=n(702);class a extends o.ParticlesInteractorBase{_maxDistance;_pluginManager;constructor(i,t){super(t),this._pluginManager=i,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 u=(0,s.getDistances)(t,p).distance,h=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):u,x=Math.min(u,h);if(x>l)continue;let k=(1-x/l)*a;this._setColor(i),i.links.push({destination:e,opacity:k,color:this._getLinkColor(i,e),isWarped:h<u})}}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._pluginManager,n.color,n.blink,n.consent),void 0===n.id?t.particles.linksColor=e:t.particles.linksColors.set(n.id,e))}}}}]);
package/README.md CHANGED
@@ -27,6 +27,7 @@ Once the scripts are loaded you can set up `tsParticles` and the interaction plu
27
27
 
28
28
  ```javascript
29
29
  (async () => {
30
+ await loadInteractivityPlugin(tsParticles);
30
31
  await loadParticlesLinksInteraction(tsParticles);
31
32
 
32
33
  await tsParticles.load({
@@ -56,9 +57,11 @@ Then you need to import it in the app, like this:
56
57
 
57
58
  ```javascript
58
59
  const { tsParticles } = require("@tsparticles/engine");
60
+ const { loadInteractivityPlugin } = require("@tsparticles/plugin-interactivity");
59
61
  const { loadParticlesLinksInteraction } = require("@tsparticles/interaction-particles-links");
60
62
 
61
63
  (async () => {
64
+ await loadInteractivityPlugin(tsParticles);
62
65
  await loadParticlesLinksInteraction(tsParticles);
63
66
  })();
64
67
  ```
@@ -67,9 +70,11 @@ or
67
70
 
68
71
  ```javascript
69
72
  import { tsParticles } from "@tsparticles/engine";
73
+ import { loadInteractivityPlugin } from "@tsparticles/plugin-interactivity";
70
74
  import { loadParticlesLinksInteraction } from "@tsparticles/interaction-particles-links";
71
75
 
72
76
  (async () => {
77
+ await loadInteractivityPlugin(tsParticles);
73
78
  await loadParticlesLinksInteraction(tsParticles);
74
79
  })();
75
80
  ```
@@ -1,32 +1,48 @@
1
- import { Circle, Rectangle, double } from "@tsparticles/engine";
1
+ import { Circle, Rectangle } from "@tsparticles/engine";
2
2
  export class CircleWarp extends Circle {
3
+ canvasSize;
3
4
  constructor(x, y, radius, canvasSize) {
4
5
  super(x, y, radius);
5
6
  this.canvasSize = canvasSize;
6
- this.canvasSize = { ...canvasSize };
7
7
  }
8
8
  contains(point) {
9
+ if (super.contains(point))
10
+ return true;
9
11
  const { width, height } = this.canvasSize, { x, y } = point;
10
- return (super.contains(point) ||
11
- super.contains({ x: x - width, y }) ||
12
+ return (super.contains({ x: x - width, y }) ||
13
+ super.contains({ x: x + width, y }) ||
14
+ super.contains({ x, y: y - height }) ||
15
+ super.contains({ x, y: y + height }) ||
12
16
  super.contains({ x: x - width, y: y - height }) ||
13
- super.contains({ x, y: y - height }));
17
+ super.contains({ x: x + width, y: y + height }) ||
18
+ super.contains({ x: x - width, y: y + height }) ||
19
+ super.contains({ x: x + width, y: y - height }));
14
20
  }
15
21
  intersects(range) {
16
- if (super.intersects(range)) {
22
+ if (super.intersects(range))
17
23
  return true;
18
- }
19
- const rect = range, circle = range, newPos = {
20
- x: range.position.x - this.canvasSize.width,
21
- y: range.position.y - this.canvasSize.height,
22
- };
23
- if (Object.hasOwn(circle, "radius")) {
24
- const biggerCircle = new Circle(newPos.x, newPos.y, circle.radius * double);
25
- return super.intersects(biggerCircle);
26
- }
27
- else if (Object.hasOwn(rect, "size")) {
28
- const rectSW = new Rectangle(newPos.x, newPos.y, rect.size.width * double, rect.size.height * double);
29
- return super.intersects(rectSW);
24
+ const { width, height } = this.canvasSize, pos = range.position, shifts = [
25
+ { x: -width, y: 0 },
26
+ { x: width, y: 0 },
27
+ { x: 0, y: -height },
28
+ { x: 0, y: height },
29
+ { x: -width, y: -height },
30
+ { x: width, y: height },
31
+ { x: -width, y: height },
32
+ { x: width, y: -height },
33
+ ];
34
+ for (const shift of shifts) {
35
+ const shiftedPos = { x: pos.x + shift.x, y: pos.y + shift.y };
36
+ let shiftedRange;
37
+ if (range instanceof Circle) {
38
+ shiftedRange = new Circle(shiftedPos.x, shiftedPos.y, range.radius);
39
+ }
40
+ else {
41
+ const rect = range;
42
+ shiftedRange = new Rectangle(shiftedPos.x, shiftedPos.y, rect.size.width, rect.size.height);
43
+ }
44
+ if (super.intersects(shiftedRange))
45
+ return true;
30
46
  }
31
47
  return false;
32
48
  }
@@ -1,156 +1,165 @@
1
- import { getDistance, getLinkColor, getRandom, getRangeValue, half, rangeColorToRgb, } from "@tsparticles/engine";
2
- import { drawLinkLine, drawLinkTriangle, setLinkFrequency } from "./Utils.js";
3
- const minOpacity = 0, minWidth = 0, minDistance = 0, maxFrequency = 1;
1
+ import { getLinkColor as engineGetLinkColor, getRandom, getRangeValue, getStyleFromRgb, half, originPoint, rangeColorToRgb, } from "@tsparticles/engine";
2
+ import { setLinkFrequency } from "./Utils.js";
3
+ const minOpacity = 0, minWidth = 0, minDistance = 0, maxFrequency = 1, defaultFrequency = 0;
4
4
  export class LinkInstance {
5
- constructor(container, engine) {
6
- this._drawLinkLine = (p1, link) => {
7
- const p1LinksOptions = p1.options.links;
8
- if (!p1LinksOptions?.enable) {
9
- return;
5
+ _colorCache = new Map();
6
+ _container;
7
+ _freqs;
8
+ _pluginManager;
9
+ constructor(pluginManager, container) {
10
+ this._pluginManager = pluginManager;
11
+ this._container = container;
12
+ this._freqs = { links: new Map(), triangles: new Map() };
13
+ }
14
+ drawParticle(context, particle) {
15
+ const { links, options } = particle;
16
+ if (!links?.length || !options.links) {
17
+ return;
18
+ }
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;
10
25
  }
11
- const container = this._container, p2 = link.destination, pos1 = p1.getPosition(), pos2 = p2.getPosition();
12
- let opacity = link.opacity;
13
- container.canvas.draw(ctx => {
14
- let colorLine;
15
- const twinkle = p1.options["twinkle"]?.lines;
16
- if (twinkle?.enable) {
17
- const twinkleFreq = twinkle.frequency, twinkleRgb = rangeColorToRgb(this._engine, twinkle.color), twinkling = getRandom() < twinkleFreq;
18
- if (twinkling && twinkleRgb) {
19
- colorLine = twinkleRgb;
20
- opacity = getRangeValue(twinkle.opacity);
21
- }
22
- }
23
- if (!colorLine) {
24
- const linkColor = p1LinksOptions.id !== undefined
25
- ? container.particles.linksColors.get(p1LinksOptions.id)
26
- : container.particles.linksColor;
27
- colorLine = getLinkColor(p1, p2, linkColor);
28
- }
29
- if (!colorLine) {
30
- return;
31
- }
32
- const width = p1.retina.linksWidth ?? minWidth, maxDistance = p1.retina.linksDistance ?? minDistance;
33
- drawLinkLine({
34
- context: ctx,
35
- width,
36
- begin: pos1,
37
- end: pos2,
38
- engine: this._engine,
39
- maxDistance,
40
- canvasSize: container.canvas.size,
41
- links: p1LinksOptions,
42
- colorLine,
43
- opacity,
44
- hdr: container.hdr,
45
- });
46
- });
47
26
  };
48
- this._drawLinkTriangle = (p1, link1, link2) => {
49
- const linksOptions = p1.options.links;
50
- if (!linksOptions?.enable) {
51
- return;
27
+ for (const link of links) {
28
+ if (linkOpts.frequency < maxFrequency &&
29
+ this._getLinkFrequency(particle, link.destination) > linkOpts.frequency) {
30
+ continue;
52
31
  }
53
- const triangleOptions = linksOptions.triangles;
54
- if (!triangleOptions.enable) {
55
- return;
32
+ const pos2 = link.destination.getPosition();
33
+ if (trianglesEnabled && !link.isWarped && p1Destinations) {
34
+ flushLines();
35
+ this._drawTriangles(options, particle, link, p1Destinations, pos1, pos2, context);
56
36
  }
57
- const container = this._container, p2 = link1.destination, p3 = link2.destination, opacityTriangle = triangleOptions.opacity ?? (link1.opacity + link2.opacity) * half;
58
- if (opacityTriangle <= minOpacity) {
59
- return;
37
+ if (link.opacity <= minOpacity || width <= minWidth) {
38
+ continue;
60
39
  }
61
- container.canvas.draw(ctx => {
62
- const pos1 = p1.getPosition(), pos2 = p2.getPosition(), pos3 = p3.getPosition(), linksDistance = p1.retina.linksDistance ?? minDistance;
63
- if (getDistance(pos1, pos2) > linksDistance ||
64
- getDistance(pos3, pos2) > linksDistance ||
65
- getDistance(pos3, pos1) > linksDistance) {
66
- return;
67
- }
68
- let colorTriangle = rangeColorToRgb(this._engine, triangleOptions.color);
69
- if (!colorTriangle) {
70
- const linkColor = linksOptions.id !== undefined
71
- ? container.particles.linksColors.get(linksOptions.id)
72
- : container.particles.linksColor;
73
- colorTriangle = getLinkColor(p1, p2, linkColor);
74
- }
75
- if (!colorTriangle) {
76
- return;
77
- }
78
- drawLinkTriangle({
79
- context: ctx,
80
- pos1,
81
- pos2,
82
- pos3,
83
- colorTriangle,
84
- opacityTriangle,
85
- hdr: container.hdr,
86
- });
87
- });
88
- };
89
- this._drawTriangles = (options, p1, link, p1Links) => {
90
- const p2 = link.destination;
91
- if (!(options.links?.triangles.enable && p2.options.links?.triangles.enable)) {
92
- return;
40
+ if (!linkOpts.enable) {
41
+ continue;
42
+ }
43
+ let opacity = link.opacity, colorLine = link.color;
44
+ const twinkleRgb = twinkle?.enable && getRandom() < twinkle.frequency
45
+ ? rangeColorToRgb(this._pluginManager, twinkle.color)
46
+ : undefined;
47
+ if (twinkle && twinkleRgb) {
48
+ colorLine = twinkleRgb;
49
+ opacity = getRangeValue(twinkle.opacity);
50
+ }
51
+ if (!colorLine) {
52
+ const linkColor = linkOpts.id !== undefined
53
+ ? this._container.particles.linksColors.get(linkOpts.id)
54
+ : this._container.particles.linksColor;
55
+ colorLine = engineGetLinkColor(particle, link.destination, linkColor);
56
+ }
57
+ if (!colorLine) {
58
+ continue;
93
59
  }
94
- const vertices = p2.links?.filter(t => {
95
- const linkFreq = this._getLinkFrequency(p2, t.destination), minCount = 0;
96
- return (p2.options.links &&
97
- linkFreq <= p2.options.links.frequency &&
98
- p1Links.findIndex(l => l.destination === t.destination) >= minCount);
99
- });
100
- if (!vertices?.length) {
101
- return;
60
+ const colorStyle = this._getCachedStyle(colorLine);
61
+ if (colorStyle !== currentColorStyle || width !== currentWidth || opacity !== currentAlpha) {
62
+ flushLines();
63
+ context.strokeStyle = colorStyle;
64
+ context.lineWidth = width;
65
+ context.globalAlpha = opacity;
66
+ currentColorStyle = colorStyle;
67
+ currentWidth = width;
68
+ currentAlpha = opacity;
69
+ context.beginPath();
70
+ pathOpen = true;
102
71
  }
103
- for (const vertex of vertices) {
104
- const p3 = vertex.destination, triangleFreq = this._getTriangleFrequency(p1, p2, p3);
105
- if (triangleFreq > options.links.triangles.frequency) {
106
- continue;
72
+ if (link.isWarped) {
73
+ const canvasSize = this._container.canvas.size, dx = pos2.x - pos1.x, dy = pos2.y - pos1.y;
74
+ let sx = originPoint.x, sy = originPoint.y;
75
+ if (Math.abs(dx) > canvasSize.width * half) {
76
+ sx = dx > minDistance ? -canvasSize.width : canvasSize.width;
107
77
  }
108
- this._drawLinkTriangle(p1, link, vertex);
78
+ if (Math.abs(dy) > canvasSize.height * half) {
79
+ sy = dy > minDistance ? -canvasSize.height : canvasSize.height;
80
+ }
81
+ context.moveTo(pos1.x, pos1.y);
82
+ context.lineTo(pos2.x + sx, pos2.y + sy);
83
+ context.moveTo(pos1.x - sx, pos1.y - sy);
84
+ context.lineTo(pos2.x, pos2.y);
109
85
  }
110
- };
111
- this._getLinkFrequency = (p1, p2) => {
112
- return setLinkFrequency([p1, p2], this._freqs.links);
113
- };
114
- this._getTriangleFrequency = (p1, p2, p3) => {
115
- return setLinkFrequency([p1, p2, p3], this._freqs.triangles);
116
- };
117
- this._container = container;
118
- this._engine = engine;
119
- this._freqs = {
120
- links: new Map(),
121
- triangles: new Map(),
122
- };
123
- }
124
- drawParticle(_context, particle) {
125
- const { links, options } = particle;
126
- if (!links?.length) {
127
- return;
128
- }
129
- const p1Links = links.filter(l => options.links &&
130
- (options.links.frequency >= maxFrequency ||
131
- this._getLinkFrequency(particle, l.destination) <= options.links.frequency));
132
- for (const link of p1Links) {
133
- this._drawTriangles(options, particle, link, p1Links);
134
- if (link.opacity > minOpacity && (particle.retina.linksWidth ?? minWidth) > minWidth) {
135
- this._drawLinkLine(particle, link);
86
+ else {
87
+ context.moveTo(pos1.x, pos1.y);
88
+ context.lineTo(pos2.x, pos2.y);
136
89
  }
137
90
  }
91
+ flushLines();
92
+ context.globalAlpha = originalAlpha;
138
93
  }
139
- async init() {
140
- this._freqs.links = new Map();
141
- this._freqs.triangles = new Map();
142
- await Promise.resolve();
94
+ init() {
95
+ this._freqs.links.clear();
96
+ this._freqs.triangles.clear();
97
+ this._colorCache.clear();
98
+ return Promise.resolve();
143
99
  }
144
100
  particleCreated(particle) {
145
101
  particle.links = [];
146
102
  if (!particle.options.links) {
147
103
  return;
148
104
  }
149
- const ratio = this._container.retina.pixelRatio, { retina } = particle, { distance, width } = particle.options.links;
150
- retina.linksDistance = distance * ratio;
151
- retina.linksWidth = width * ratio;
105
+ particle.linksDistance = particle.options.links.distance;
106
+ particle.linksWidth = particle.options.links.width;
107
+ const ratio = this._container.retina.pixelRatio;
108
+ particle.retina.linksDistance = particle.linksDistance * ratio;
109
+ particle.retina.linksWidth = particle.linksWidth * ratio;
152
110
  }
153
111
  particleDestroyed(particle) {
154
112
  particle.links = [];
155
113
  }
114
+ _drawTriangles(options, p1, link, p1Destinations, pos1, pos2, context) {
115
+ const p2 = link.destination, triangleOptions = options.links?.triangles;
116
+ if (!triangleOptions?.enable || !p2.options.links?.triangles.enable) {
117
+ return;
118
+ }
119
+ const p2Links = p2.links;
120
+ if (!p2Links?.length) {
121
+ return;
122
+ }
123
+ for (const vertex of p2Links) {
124
+ if (vertex.isWarped ||
125
+ this._getLinkFrequency(p2, vertex.destination) > p2.options.links.frequency ||
126
+ !p1Destinations.has(vertex.destination.id)) {
127
+ continue;
128
+ }
129
+ const p3 = vertex.destination;
130
+ if (this._getTriangleFrequency(p1, p2, p3) > (options.links?.triangles.frequency ?? defaultFrequency)) {
131
+ continue;
132
+ }
133
+ const opacityTriangle = triangleOptions.opacity ?? (link.opacity + vertex.opacity) * half, colorTriangle = rangeColorToRgb(this._pluginManager, triangleOptions.color) ?? link.color;
134
+ if (!colorTriangle || opacityTriangle <= minOpacity) {
135
+ continue;
136
+ }
137
+ const pos3 = p3.getPosition();
138
+ context.save();
139
+ context.fillStyle = this._getCachedStyle(colorTriangle);
140
+ context.globalAlpha = opacityTriangle;
141
+ context.beginPath();
142
+ context.moveTo(pos1.x, pos1.y);
143
+ context.lineTo(pos2.x, pos2.y);
144
+ context.lineTo(pos3.x, pos3.y);
145
+ context.closePath();
146
+ context.fill();
147
+ context.restore();
148
+ }
149
+ }
150
+ _getCachedStyle(rgb) {
151
+ const key = `${rgb.r},${rgb.g},${rgb.b}`;
152
+ let style = this._colorCache.get(key);
153
+ if (!style) {
154
+ style = getStyleFromRgb(rgb, this._container.hdr);
155
+ this._colorCache.set(key, style);
156
+ }
157
+ return style;
158
+ }
159
+ _getLinkFrequency(p1, p2) {
160
+ return setLinkFrequency([p1, p2], this._freqs.links);
161
+ }
162
+ _getTriangleFrequency(p1, p2, p3) {
163
+ return setLinkFrequency([p1, p2, p3], this._freqs.triangles);
164
+ }
156
165
  }
package/browser/Linker.js CHANGED
@@ -1,46 +1,25 @@
1
- import { Circle, getDistances, getLinkRandomColor, originPoint, } from "@tsparticles/engine";
1
+ import { Circle, getDistances, getLinkColor, getLinkRandomColor, originPoint, } from "@tsparticles/engine";
2
2
  import { CircleWarp } from "./CircleWarp.js";
3
3
  import { Links } from "./Options/Classes/Links.js";
4
4
  import { ParticlesInteractorBase } from "@tsparticles/plugin-interactivity";
5
- const squarePower = 2, opacityOffset = 1, minDistance = 0;
6
- function getLinkDistance(pos1, pos2, optDistance, canvasSize, warp) {
7
- const { dx, dy, distance } = getDistances(pos1, pos2);
8
- if (!warp || distance <= optDistance) {
9
- return distance;
10
- }
11
- const absDiffs = {
12
- x: Math.abs(dx),
13
- y: Math.abs(dy),
14
- }, warpDistances = {
5
+ const opacityOffset = 1, minDistance = 0;
6
+ function getWarpDistance(pos1, pos2, canvasSize) {
7
+ const { dx, dy } = getDistances(pos1, pos2), absDiffs = { x: Math.abs(dx), y: Math.abs(dy) }, warpDistances = {
15
8
  x: Math.min(absDiffs.x, canvasSize.width - absDiffs.x),
16
9
  y: Math.min(absDiffs.y, canvasSize.height - absDiffs.y),
17
10
  };
18
- return Math.sqrt(warpDistances.x ** squarePower + warpDistances.y ** squarePower);
11
+ return Math.hypot(warpDistances.x, warpDistances.y);
19
12
  }
20
13
  export class Linker extends ParticlesInteractorBase {
21
- constructor(container, engine) {
14
+ _maxDistance;
15
+ _pluginManager;
16
+ constructor(pluginManager, container) {
22
17
  super(container);
23
- this._setColor = p1 => {
24
- if (!p1.options.links) {
25
- return;
26
- }
27
- const container = this.container, linksOptions = p1.options.links;
28
- let linkColor = linksOptions.id === undefined
29
- ? container.particles.linksColor
30
- : container.particles.linksColors.get(linksOptions.id);
31
- if (linkColor) {
32
- return;
33
- }
34
- const optColor = linksOptions.color;
35
- linkColor = getLinkRandomColor(this._engine, optColor, linksOptions.blink, linksOptions.consent);
36
- if (linksOptions.id === undefined) {
37
- container.particles.linksColor = linkColor;
38
- }
39
- else {
40
- container.particles.linksColors.set(linksOptions.id, linkColor);
41
- }
42
- };
43
- this._engine = engine;
18
+ this._pluginManager = pluginManager;
19
+ this._maxDistance = 0;
20
+ }
21
+ get maxDistance() {
22
+ return this._maxDistance;
44
23
  }
45
24
  clear() {
46
25
  }
@@ -53,19 +32,14 @@ export class Linker extends ParticlesInteractorBase {
53
32
  return;
54
33
  }
55
34
  p1.links = [];
35
+ if (p1.linksDistance && p1.linksDistance > this._maxDistance) {
36
+ this._maxDistance = p1.linksDistance;
37
+ }
56
38
  const pos1 = p1.getPosition(), container = this.container, canvasSize = container.canvas.size;
57
39
  if (pos1.x < originPoint.x || pos1.y < originPoint.y || pos1.x > canvasSize.width || pos1.y > canvasSize.height) {
58
40
  return;
59
41
  }
60
- const linkOpt1 = p1.options.links, optOpacity = linkOpt1.opacity, optDistance = p1.retina.linksDistance ?? minDistance, warp = linkOpt1.warp;
61
- let range;
62
- if (warp) {
63
- range = new CircleWarp(pos1.x, pos1.y, optDistance, canvasSize);
64
- }
65
- else {
66
- range = new Circle(pos1.x, pos1.y, optDistance);
67
- }
68
- const query = container.particles.quadTree.query(range);
42
+ const linkOpt1 = p1.options.links, optOpacity = linkOpt1.opacity, optDistance = p1.retina.linksDistance ?? minDistance, warp = linkOpt1.warp, range = warp ? new CircleWarp(pos1.x, pos1.y, optDistance, canvasSize) : new Circle(pos1.x, pos1.y, optDistance), query = container.particles.grid.query(range);
69
43
  for (const p2 of query) {
70
44
  const linkOpt2 = p2.options.links;
71
45
  if (p1 === p2 ||
@@ -82,7 +56,7 @@ export class Linker extends ParticlesInteractorBase {
82
56
  if (pos2.x < originPoint.x || pos2.y < originPoint.y || pos2.x > canvasSize.width || pos2.y > canvasSize.height) {
83
57
  continue;
84
58
  }
85
- const distance = getLinkDistance(pos1, pos2, optDistance, canvasSize, warp && linkOpt2.warp);
59
+ const distDirect = getDistances(pos1, pos2).distance, distWarp = warp && linkOpt2.warp ? getWarpDistance(pos1, pos2, canvasSize) : distDirect, distance = Math.min(distDirect, distWarp);
86
60
  if (distance > optDistance) {
87
61
  continue;
88
62
  }
@@ -91,6 +65,8 @@ export class Linker extends ParticlesInteractorBase {
91
65
  p1.links.push({
92
66
  destination: p2,
93
67
  opacity: opacityLine,
68
+ color: this._getLinkColor(p1, p2),
69
+ isWarped: distWarp < distDirect,
94
70
  });
95
71
  }
96
72
  }
@@ -105,4 +81,33 @@ export class Linker extends ParticlesInteractorBase {
105
81
  }
106
82
  reset() {
107
83
  }
84
+ _getLinkColor(p1, p2) {
85
+ const container = this.container, linksOptions = p1.options.links;
86
+ if (!linksOptions) {
87
+ return;
88
+ }
89
+ const linkColor = linksOptions.id !== undefined
90
+ ? container.particles.linksColors.get(linksOptions.id)
91
+ : container.particles.linksColor;
92
+ return getLinkColor(p1, p2, linkColor);
93
+ }
94
+ _setColor(p1) {
95
+ if (!p1.options.links) {
96
+ return;
97
+ }
98
+ const container = this.container, linksOptions = p1.options.links;
99
+ let linkColor = linksOptions.id === undefined
100
+ ? container.particles.linksColor
101
+ : container.particles.linksColors.get(linksOptions.id);
102
+ if (linkColor) {
103
+ return;
104
+ }
105
+ linkColor = getLinkRandomColor(this._pluginManager, linksOptions.color, linksOptions.blink, linksOptions.consent);
106
+ if (linksOptions.id === undefined) {
107
+ container.particles.linksColor = linkColor;
108
+ }
109
+ else {
110
+ container.particles.linksColors.set(linksOptions.id, linkColor);
111
+ }
112
+ }
108
113
  }
@@ -1,11 +1,12 @@
1
1
  export class LinksPlugin {
2
- constructor(engine) {
3
- this.id = "links";
4
- this._engine = engine;
2
+ id = "links";
3
+ _pluginManager;
4
+ constructor(pluginManager) {
5
+ this._pluginManager = pluginManager;
5
6
  }
6
7
  async getPlugin(container) {
7
8
  const { LinkInstance } = await import("./LinkInstance.js");
8
- return new LinkInstance(container, this._engine);
9
+ return new LinkInstance(this._pluginManager, container);
9
10
  }
10
11
  loadOptions() {
11
12
  }
@@ -2,6 +2,18 @@ import { OptionsColor, isNull } from "@tsparticles/engine";
2
2
  import { LinksShadow } from "./LinksShadow.js";
3
3
  import { LinksTriangle } from "./LinksTriangle.js";
4
4
  export class Links {
5
+ blink;
6
+ color;
7
+ consent;
8
+ distance;
9
+ enable;
10
+ frequency;
11
+ id;
12
+ opacity;
13
+ shadow;
14
+ triangles;
15
+ warp;
16
+ width;
5
17
  constructor() {
6
18
  this.blink = false;
7
19
  this.color = new OptionsColor();
@@ -1,5 +1,8 @@
1
1
  import { OptionsColor, isNull } from "@tsparticles/engine";
2
2
  export class LinksShadow {
3
+ blur;
4
+ color;
5
+ enable;
3
6
  constructor() {
4
7
  this.blur = 5;
5
8
  this.color = new OptionsColor();
@@ -1,5 +1,9 @@
1
1
  import { OptionsColor, isNull } from "@tsparticles/engine";
2
2
  export class LinksTriangle {
3
+ color;
4
+ enable;
5
+ frequency;
6
+ opacity;
3
7
  constructor() {
4
8
  this.enable = false;
5
9
  this.frequency = 1;