@tsparticles/interaction-particles-links 4.0.0-alpha.21 → 4.0.0-alpha.23

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/919.min.js CHANGED
@@ -1 +1 @@
1
- "use strict";(this.webpackChunk_tsparticles_interaction_particles_links=this.webpackChunk_tsparticles_interaction_particles_links||[]).push([[919],{919(e,i,t){t.d(i,{LinkInstance:()=>r});var n=t(303),s=t(425);function l(e,i){var t;let s=((t=e.map(e=>e.id)).sort((e,i)=>e-i),t.join("_")),l=i.get(s);return void 0===l&&(l=(0,n.getRandom)(),i.set(s,l)),l}class r{_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:n}=i;if(!t?.length)return;let s=t.filter(e=>n.links&&(n.links.frequency>=1||this._getLinkFrequency(i,e.destination)<=n.links.frequency));for(let e of s)this._drawTriangles(n,i,e,s),e.opacity>0&&(i.retina.linksWidth??0)>0&&this._drawLinkLine(i,e)}async init(){this._freqs.links=new Map,this._freqs.triangles=new Map,await Promise.resolve()}particleCreated(e){if(e.links=[],!e.options.links)return;let i=this._container.retina.pixelRatio,{retina:t}=e,{distance:n,width:s}=e.options.links;t.linksDistance=n*i,t.linksWidth=s*i}particleDestroyed(e){e.links=[]}_drawLinkLine=(e,i)=>{let t=e.options.links;if(!t?.enable)return;let l=this._container,r=i.destination,a=e.getPosition(),o=r.getPosition(),c=i.opacity;l.canvas.draw(i=>{let g,d=e.options.twinkle?.lines;if(d?.enable){let e=d.frequency,i=(0,n.rangeColorToRgb)(this._engine,d.color);(0,n.getRandom)()<e&&i&&(g=i,c=(0,n.getRangeValue)(d.opacity))}if(!g){let i=void 0!==t.id?l.particles.linksColors.get(t.id):l.particles.linksColor;g=(0,n.getLinkColor)(e,r,i)}if(!g)return;let h=e.retina.linksWidth??0,k=e.retina.linksDistance??0;!function(e){let i=!1,{begin:t,end:l,engine:r,maxDistance:a,context:o,canvasSize:c,width:g,colorLine:d,opacity:h,links:k,hdr:y}=e;if((0,n.getDistance)(t,l)<=a)(0,s.drawLine)(o,t,l),i=!0;else if(k.warp){let e,r,g={x:l.x-c.width,y:l.y},d=(0,n.getDistances)(t,g);if(d.distance<=a){let i=t.y-d.dy/d.dx*t.x;e={x:0,y:i},r={x:c.width,y:i}}else{let i={x:l.x,y:l.y-c.height},s=(0,n.getDistances)(t,i);if(s.distance<=a){let i=-(t.y-s.dy/s.dx*t.x)/(s.dy/s.dx);e={x:i,y:0},r={x:i,y:c.height}}else{let i={x:l.x-c.width,y:l.y-c.height},s=(0,n.getDistances)(t,i);if(s.distance<=a){let i=t.y-s.dy/s.dx*t.x;r={x:(e={x:-i/(s.dy/s.dx),y:i}).x+c.width,y:e.y+c.height}}}}e&&r&&((0,s.drawLine)(o,t,e),(0,s.drawLine)(o,l,r),i=!0)}if(!i)return;o.lineWidth=g,o.strokeStyle=(0,n.getStyleFromRgb)(d,y,h);let{shadow:f}=k;if(f.enable){let e=(0,n.rangeColorToRgb)(r,f.color);e&&(o.shadowBlur=f.blur,o.shadowColor=(0,n.getStyleFromRgb)(e,y))}o.stroke()}({context:i,width:h,begin:a,end:o,engine:this._engine,maxDistance:k,canvasSize:l.canvas.size,links:t,colorLine:g,opacity:c,hdr:l.hdr})})};_drawLinkTriangle=(e,i,t)=>{let s=e.options.links;if(!s?.enable)return;let l=s.triangles;if(!l.enable)return;let r=this._container,a=i.destination,o=t.destination,c=l.opacity??(i.opacity+t.opacity)*n.half;c<=0||r.canvas.draw(i=>{let t=e.getPosition(),g=a.getPosition(),d=o.getPosition(),h=e.retina.linksDistance??0;if((0,n.getDistance)(t,g)>h||(0,n.getDistance)(d,g)>h||(0,n.getDistance)(d,t)>h)return;let k=(0,n.rangeColorToRgb)(this._engine,l.color);if(!k){let i=void 0!==s.id?r.particles.linksColors.get(s.id):r.particles.linksColor;k=(0,n.getLinkColor)(e,a,i)}k&&function(e){let{context:i,hdr:t,pos1:s,pos2:l,pos3:r,colorTriangle:a,opacityTriangle:o}=e;i.beginPath(),i.moveTo(s.x,s.y),i.lineTo(l.x,l.y),i.lineTo(r.x,r.y),i.closePath(),i.fillStyle=(0,n.getStyleFromRgb)(a,t,o),i.fill()}({context:i,pos1:t,pos2:g,pos3:d,colorTriangle:k,opacityTriangle:c,hdr:r.hdr})})};_drawTriangles=(e,i,t,n)=>{let s=t.destination;if(!(e.links?.triangles.enable&&s.options.links?.triangles.enable))return;let l=s.links?.filter(e=>{let i=this._getLinkFrequency(s,e.destination);return s.options.links&&i<=s.options.links.frequency&&n.findIndex(i=>i.destination===e.destination)>=0});if(l?.length)for(let n of l){let l=n.destination;this._getTriangleFrequency(i,s,l)>e.links.triangles.frequency||this._drawLinkTriangle(i,t,n)}};_getLinkFrequency=(e,i)=>l([e,i],this._freqs.links);_getTriangleFrequency=(e,i,t)=>l([e,i,t],this._freqs.triangles)}}}]);
1
+ "use strict";(this.webpackChunk_tsparticles_interaction_particles_links=this.webpackChunk_tsparticles_interaction_particles_links||[]).push([[919],{919(i,t,e){e.d(t,{LinkInstance:()=>s});var n=e(303);function o(i,t){var e;let o=((e=i.map(i=>i.id)).sort((i,t)=>i-t),e.join("_")),s=t.get(o);return void 0===s&&(s=(0,n.getRandom)(),t.set(o,s)),s}class s{_colorCache=new Map;_container;_engine;_freqs;_lineBatches=new Map;_triangleBatches=new Map;constructor(i,t){this._container=i,this._engine=t,this._freqs={links:new Map,triangles:new Map}}draw(i){for(let[,t]of this._triangleBatches){i.save(),i.fillStyle=t.colorStyle,i.globalAlpha=t.opacity,i.beginPath();for(let e=0;e<t.coords.length;e+=6){let o=t.coords[e+0]??n.originPoint.x,s=t.coords[e+1]??n.originPoint.y,r=t.coords[e+2]??n.originPoint.x,l=t.coords[e+3]??n.originPoint.y,a=t.coords[e+4]??n.originPoint.x,c=t.coords[e+5]??n.originPoint.y;i.moveTo(o,s),i.lineTo(r,l),i.lineTo(a,c)}i.fill(),i.restore()}for(let[,t]of this._lineBatches){i.save(),i.strokeStyle=t.colorStyle,i.lineWidth=t.width,i.globalAlpha=t.opacity,i.beginPath();for(let e=0;e<t.coords.length;e+=4){let o=t.coords[e+0]??n.originPoint.x,s=t.coords[e+1]??n.originPoint.y,r=t.coords[e+2]??n.originPoint.x,l=t.coords[e+3]??n.originPoint.y;i.moveTo(o,s),i.lineTo(r,l)}i.stroke(),i.restore()}this._lineBatches.clear(),this._triangleBatches.clear()}drawParticle(i,t){let{links:e,options:o}=t;if(!e?.length||!o.links)return;let s=this._container.canvas.size,r=e.filter(i=>o.links&&(o.links.frequency>=1||this._getLinkFrequency(t,i.destination)<=o.links.frequency)),l=t.getPosition();for(let i of r)if(i.isWarped||this._collectTriangles(o,t,i,r),i.opacity>0&&(t.retina.linksWidth??0)>0){let e=i.opacity,r=i.color,a=t.options.twinkle?.lines;if(a?.enable&&(0,n.getRandom)()<a.frequency){let i=(0,n.rangeColorToRgb)(this._engine,a.color);i&&(r=i,e=(0,n.getRangeValue)(a.opacity))}if(!r){let e=void 0!==o.links.id?this._container.particles.linksColors.get(o.links.id):this._container.particles.linksColor;r=(0,n.getLinkColor)(t,i.destination,e)}if(r){let o=Math.ceil(10*e)/10,a=this._getCachedStyle(r),c=t.retina.linksWidth??0,h=`${a}_${o}_${c}`,g=this._lineBatches.get(h);g||(g={colorStyle:a,opacity:o,width:c,coords:[]},this._lineBatches.set(h,g));let d=i.destination.getPosition();if(i.isWarped){let i=d.x-l.x,t=d.y-l.y,e=n.originPoint.x,o=n.originPoint.y;Math.abs(i)>s.width*n.half&&(e=i>0?-s.width:s.width),Math.abs(t)>s.height*n.half&&(o=t>0?-s.height:s.height);let r={x:d.x+e,y:d.y+o},a={x:l.x-e,y:l.y-o};g.coords.push(l.x,l.y,r.x,r.y),g.coords.push(a.x,a.y,d.x,d.y)}else g.coords.push(l.x,l.y,d.x,d.y)}}}async init(){this._freqs.links.clear(),this._freqs.triangles.clear(),this._colorCache.clear(),await Promise.resolve()}particleCreated(i){if(i.links=[],!i.options.links)return;let t=this._container.retina.pixelRatio;i.retina.linksDistance=i.options.links.distance*t,i.retina.linksWidth=i.options.links.width*t}particleDestroyed(i){i.links=[]}_collectTriangles(i,t,e,o){let s=e.destination,r=i.links?.triangles;if(!r?.enable||!s.options.links?.triangles.enable)return;let l=s.links?.filter(i=>!i.isWarped&&s.options.links&&this._getLinkFrequency(s,i.destination)<=s.options.links.frequency&&o.some(t=>t.destination===i.destination));if(l?.length)for(let o of l){let l=o.destination;if(this._getTriangleFrequency(t,s,l)>(i.links?.triangles.frequency??0))continue;let a=Math.ceil((e.opacity+o.opacity)*n.half*10)/10,c=(0,n.rangeColorToRgb)(this._engine,r.color)??e.color;if(!c)continue;let h=this._getCachedStyle(c),g=`${h}_${a}`,d=this._triangleBatches.get(g);d||(d={colorStyle:h,opacity:a,coords:[]},this._triangleBatches.set(g,d));let _=t.getPosition(),y=s.getPosition(),p=l.getPosition();d.coords.push(_.x,_.y,y.x,y.y,p.x,p.y)}}_getCachedStyle(i){let t=`${i.r},${i.g},${i.b}`,e=this._colorCache.get(t);return e||(e=(0,n.getStyleFromRgb)(i,this._container.hdr),this._colorCache.set(t,e)),e}_getLinkFrequency(i,t){return o([i,t],this._freqs.links)}_getTriangleFrequency(i,t,e){return o([i,t,e],this._freqs.triangles)}}}}]);
package/973.min.js CHANGED
@@ -1 +1 @@
1
- "use strict";(this.webpackChunk_tsparticles_interaction_particles_links=this.webpackChunk_tsparticles_interaction_particles_links||[]).push([[973],{973(i,t,e){e.d(t,{Linker:()=>a});var n=e(303);class s extends n.Circle{canvasSize;constructor(i,t,e,n){super(i,t,e),this.canvasSize=n,this.canvasSize={...n}}contains(i){let{width:t,height:e}=this.canvasSize,{x:n,y:s}=i;return super.contains(i)||super.contains({x:n-t,y:s})||super.contains({x:n-t,y:s-e})||super.contains({x:n,y:s-e})}intersects(i){if(super.intersects(i))return!0;let t={x:i.position.x-this.canvasSize.width,y:i.position.y-this.canvasSize.height};if(Object.hasOwn(i,"radius")){let e=new n.Circle(t.x,t.y,i.radius*n.double);return super.intersects(e)}if(Object.hasOwn(i,"size")){let e=new n.Rectangle(t.x,t.y,i.size.width*n.double,i.size.height*n.double);return super.intersects(e)}return!1}}var r=e(153),o=e(702);class a extends o.ParticlesInteractorBase{_engine;constructor(i,t){super(i),this._engine=t}clear(){}init(){this.container.particles.linksColor=void 0,this.container.particles.linksColors=new Map}interact(i){let t;if(!i.options.links)return;i.links=[];let e=i.getPosition(),r=this.container,o=r.canvas.size;if(e.x<n.originPoint.x||e.y<n.originPoint.y||e.x>o.width||e.y>o.height)return;let a=i.options.links,l=a.opacity,c=i.retina.linksDistance??0,h=a.warp;for(let p of(t=h?new s(e.x,e.y,c,o):new n.Circle(e.x,e.y,c),r.particles.quadTree.query(t))){let t=p.options.links;if(i===p||!t?.enable||a.id!==t.id||p.spawning||p.destroyed||!p.links||i.links.some(i=>i.destination===p)||p.links.some(t=>t.destination===i))continue;let s=p.getPosition();if(s.x<n.originPoint.x||s.y<n.originPoint.y||s.x>o.width||s.y>o.height)continue;let r=function(i,t,e,s,r){let{dx:o,dy:a,distance:l}=(0,n.getDistances)(i,t);if(!r||l<=e)return l;let c={x:Math.abs(o),y:Math.abs(a)},h={x:Math.min(c.x,s.width-c.x),y:Math.min(c.y,s.height-c.y)};return Math.sqrt(h.x**2+h.y**2)}(e,s,c,o,h&&t.warp);if(r>c)continue;let u=(1-r/c)*l;this._setColor(i),i.links.push({destination:p,opacity:u})}}isEnabled(i){return!!i.options.links?.enable}loadParticlesOptions(i,...t){for(let e of(i.links??=new r.q,t))i.links.load(e?.links)}reset(){}_setColor=i=>{if(!i.options.links)return;let t=this.container,e=i.options.links,s=void 0===e.id?t.particles.linksColor:t.particles.linksColors.get(e.id);if(s)return;let r=e.color;s=(0,n.getLinkRandomColor)(this._engine,r,e.blink,e.consent),void 0===e.id?t.particles.linksColor=s:t.particles.linksColors.set(e.id,s)}}}}]);
1
+ "use strict";(this.webpackChunk_tsparticles_interaction_particles_links=this.webpackChunk_tsparticles_interaction_particles_links||[]).push([[973],{973(i,t,n){n.d(t,{Linker:()=>l});var e=n(303);class s extends e.Circle{canvasSize;constructor(i,t,n,e){super(i,t,n),this.canvasSize=e}contains(i){if(super.contains(i))return!0;let{width:t,height:n}=this.canvasSize,{x:e,y:s}=i;return super.contains({x:e-t,y:s})||super.contains({x:e+t,y:s})||super.contains({x:e,y:s-n})||super.contains({x:e,y:s+n})||super.contains({x:e-t,y:s-n})||super.contains({x:e+t,y:s+n})||super.contains({x:e-t,y:s+n})||super.contains({x:e+t,y:s-n})}intersects(i){if(super.intersects(i))return!0;let{width:t,height:n}=this.canvasSize,s=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:s.x+r.x,y:s.y+r.y};if(t=i instanceof e.Circle?new e.Circle(n.x,n.y,i.radius):new e.Rectangle(n.x,n.y,i.size.width,i.size.height),super.intersects(t))return!0}return!1}}var r=n(153),o=n(702);class l extends o.ParticlesInteractorBase{_engine;constructor(i,t){super(i),this._engine=t}clear(){}init(){this.container.particles.linksColor=void 0,this.container.particles.linksColors=new Map}interact(i){if(!i.options.links)return;i.links=[];let t=i.getPosition(),n=this.container,r=n.canvas.size;if(t.x<e.originPoint.x||t.y<e.originPoint.y||t.x>r.width||t.y>r.height)return;let o=i.options.links,l=o.opacity,a=i.retina.linksDistance??0,c=o.warp,p=c?new s(t.x,t.y,a,r):new e.Circle(t.x,t.y,a);for(let s of n.particles.quadTree.query(p)){let n=s.options.links;if(i===s||!n?.enable||o.id!==n.id||s.spawning||s.destroyed||!s.links||i.links.some(i=>i.destination===s)||s.links.some(t=>t.destination===i))continue;let p=s.getPosition();if(p.x<e.originPoint.x||p.y<e.originPoint.y||p.x>r.width||p.y>r.height)continue;let u=(0,e.getDistances)(t,p).distance,h=c&&n.warp?function(i,t,n){let{dx:s,dy:r}=(0,e.getDistances)(i,t),o={x:Math.abs(s),y:Math.abs(r)},l={x:Math.min(o.x,n.width-o.x),y:Math.min(o.y,n.height-o.y)};return Math.sqrt(l.x**2+l.y**2)}(t,p,r):u,k=Math.min(u,h);if(k>a)continue;let y=(1-k/a)*l;this._setColor(i),i.links.push({destination:s,opacity:y,color:this._getLinkColor(i,s),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,s=i.options.links;if(!s)return;let r=void 0!==s.id?n.particles.linksColors.get(s.id):n.particles.linksColor;return(0,e.getLinkColor)(i,t,r)}_setColor(i){if(!i.options.links)return;let t=this.container,n=i.options.links,s=void 0===n.id?t.particles.linksColor:t.particles.linksColors.get(n.id);s||(s=(0,e.getLinkRandomColor)(this._engine,n.color,n.blink,n.consent),void 0===n.id?t.particles.linksColor=s:t.particles.linksColors.set(n.id,s))}}}}]);
@@ -1,33 +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
3
  canvasSize;
4
4
  constructor(x, y, radius, canvasSize) {
5
5
  super(x, y, radius);
6
6
  this.canvasSize = canvasSize;
7
- this.canvasSize = { ...canvasSize };
8
7
  }
9
8
  contains(point) {
9
+ if (super.contains(point))
10
+ return true;
10
11
  const { width, height } = this.canvasSize, { x, y } = point;
11
- return (super.contains(point) ||
12
- 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 }) ||
13
16
  super.contains({ x: x - width, y: y - height }) ||
14
- 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 }));
15
20
  }
16
21
  intersects(range) {
17
- if (super.intersects(range)) {
22
+ if (super.intersects(range))
18
23
  return true;
19
- }
20
- const rect = range, circle = range, newPos = {
21
- x: range.position.x - this.canvasSize.width,
22
- y: range.position.y - this.canvasSize.height,
23
- };
24
- if (Object.hasOwn(circle, "radius")) {
25
- const biggerCircle = new Circle(newPos.x, newPos.y, circle.radius * double);
26
- return super.intersects(biggerCircle);
27
- }
28
- else if (Object.hasOwn(rect, "size")) {
29
- const rectSW = new Rectangle(newPos.x, newPos.y, rect.size.width * double, rect.size.height * double);
30
- 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;
31
46
  }
32
47
  return false;
33
48
  }
@@ -1,36 +1,110 @@
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, 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;
4
4
  export class LinkInstance {
5
+ _colorCache = new Map();
5
6
  _container;
6
7
  _engine;
7
8
  _freqs;
9
+ _lineBatches = new Map();
10
+ _triangleBatches = new Map();
8
11
  constructor(container, engine) {
9
12
  this._container = container;
10
13
  this._engine = engine;
11
- this._freqs = {
12
- links: new Map(),
13
- triangles: new Map(),
14
- };
14
+ this._freqs = { links: new Map(), triangles: new Map() };
15
+ }
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();
15
47
  }
16
48
  drawParticle(_context, particle) {
17
49
  const { links, options } = particle;
18
- if (!links?.length) {
50
+ if (!links?.length || !options.links) {
19
51
  return;
20
52
  }
21
- const p1Links = links.filter(l => options.links &&
53
+ const canvasSize = this._container.canvas.size, p1Links = links.filter(l => options.links &&
22
54
  (options.links.frequency >= maxFrequency ||
23
- this._getLinkFrequency(particle, l.destination) <= options.links.frequency));
55
+ this._getLinkFrequency(particle, l.destination) <= options.links.frequency)), pos1 = particle.getPosition();
24
56
  for (const link of p1Links) {
25
- this._drawTriangles(options, particle, link, p1Links);
57
+ if (!link.isWarped) {
58
+ this._collectTriangles(options, particle, link, p1Links);
59
+ }
26
60
  if (link.opacity > minOpacity && (particle.retina.linksWidth ?? minWidth) > minWidth) {
27
- this._drawLinkLine(particle, link);
61
+ let opacity = link.opacity, colorLine = link.color;
62
+ const twinkle = particle.options["twinkle"]?.lines;
63
+ if (twinkle?.enable && getRandom() < twinkle.frequency) {
64
+ const twinkleRgb = rangeColorToRgb(this._engine, twinkle.color);
65
+ if (twinkleRgb) {
66
+ colorLine = twinkleRgb;
67
+ opacity = getRangeValue(twinkle.opacity);
68
+ }
69
+ }
70
+ if (!colorLine) {
71
+ const linkColor = options.links.id !== undefined
72
+ ? this._container.particles.linksColors.get(options.links.id)
73
+ : this._container.particles.linksColor;
74
+ colorLine = engineGetLinkColor(particle, link.destination, linkColor);
75
+ }
76
+ if (colorLine) {
77
+ const qOpacity = Math.ceil(opacity * opacitySteps) / opacitySteps, colorStyle = this._getCachedStyle(colorLine), width = particle.retina.linksWidth ?? defaultWidth, key = `${colorStyle}_${qOpacity}_${width}`;
78
+ let batch = this._lineBatches.get(key);
79
+ if (!batch) {
80
+ batch = { colorStyle, opacity: qOpacity, width, coords: [] };
81
+ this._lineBatches.set(key, batch);
82
+ }
83
+ const pos2 = link.destination.getPosition();
84
+ if (link.isWarped) {
85
+ const dx = pos2.x - pos1.x, dy = pos2.y - pos1.y;
86
+ let sx = originPoint.x, sy = originPoint.y;
87
+ if (Math.abs(dx) > canvasSize.width * half) {
88
+ sx = dx > minDistance ? -canvasSize.width : canvasSize.width;
89
+ }
90
+ if (Math.abs(dy) > canvasSize.height * half) {
91
+ sy = dy > minDistance ? -canvasSize.height : canvasSize.height;
92
+ }
93
+ const v2 = { x: pos2.x + sx, y: pos2.y + sy }, v1 = { x: pos1.x - sx, y: pos1.y - sy };
94
+ batch.coords.push(pos1.x, pos1.y, v2.x, v2.y);
95
+ batch.coords.push(v1.x, v1.y, pos2.x, pos2.y);
96
+ }
97
+ else {
98
+ batch.coords.push(pos1.x, pos1.y, pos2.x, pos2.y);
99
+ }
100
+ }
28
101
  }
29
102
  }
30
103
  }
31
104
  async init() {
32
- this._freqs.links = new Map();
33
- this._freqs.triangles = new Map();
105
+ this._freqs.links.clear();
106
+ this._freqs.triangles.clear();
107
+ this._colorCache.clear();
34
108
  await Promise.resolve();
35
109
  }
36
110
  particleCreated(particle) {
@@ -38,122 +112,59 @@ export class LinkInstance {
38
112
  if (!particle.options.links) {
39
113
  return;
40
114
  }
41
- const ratio = this._container.retina.pixelRatio, { retina } = particle, { distance, width } = particle.options.links;
42
- retina.linksDistance = distance * ratio;
43
- retina.linksWidth = width * ratio;
115
+ const ratio = this._container.retina.pixelRatio;
116
+ particle.retina.linksDistance = particle.options.links.distance * ratio;
117
+ particle.retina.linksWidth = particle.options.links.width * ratio;
44
118
  }
45
119
  particleDestroyed(particle) {
46
120
  particle.links = [];
47
121
  }
48
- _drawLinkLine = (p1, link) => {
49
- const p1LinksOptions = p1.options.links;
50
- if (!p1LinksOptions?.enable) {
51
- return;
52
- }
53
- const container = this._container, p2 = link.destination, pos1 = p1.getPosition(), pos2 = p2.getPosition();
54
- let opacity = link.opacity;
55
- container.canvas.draw(ctx => {
56
- let colorLine;
57
- const twinkle = p1.options["twinkle"]?.lines;
58
- if (twinkle?.enable) {
59
- const twinkleFreq = twinkle.frequency, twinkleRgb = rangeColorToRgb(this._engine, twinkle.color), twinkling = getRandom() < twinkleFreq;
60
- if (twinkling && twinkleRgb) {
61
- colorLine = twinkleRgb;
62
- opacity = getRangeValue(twinkle.opacity);
63
- }
64
- }
65
- if (!colorLine) {
66
- const linkColor = p1LinksOptions.id !== undefined
67
- ? container.particles.linksColors.get(p1LinksOptions.id)
68
- : container.particles.linksColor;
69
- colorLine = getLinkColor(p1, p2, linkColor);
70
- }
71
- if (!colorLine) {
72
- return;
73
- }
74
- const width = p1.retina.linksWidth ?? minWidth, maxDistance = p1.retina.linksDistance ?? minDistance;
75
- drawLinkLine({
76
- context: ctx,
77
- width,
78
- begin: pos1,
79
- end: pos2,
80
- engine: this._engine,
81
- maxDistance,
82
- canvasSize: container.canvas.size,
83
- links: p1LinksOptions,
84
- colorLine,
85
- opacity,
86
- hdr: container.hdr,
87
- });
88
- });
89
- };
90
- _drawLinkTriangle = (p1, link1, link2) => {
91
- const linksOptions = p1.options.links;
92
- if (!linksOptions?.enable) {
93
- return;
94
- }
95
- const triangleOptions = linksOptions.triangles;
96
- if (!triangleOptions.enable) {
97
- return;
98
- }
99
- const container = this._container, p2 = link1.destination, p3 = link2.destination, opacityTriangle = triangleOptions.opacity ?? (link1.opacity + link2.opacity) * half;
100
- if (opacityTriangle <= minOpacity) {
101
- return;
102
- }
103
- container.canvas.draw(ctx => {
104
- const pos1 = p1.getPosition(), pos2 = p2.getPosition(), pos3 = p3.getPosition(), linksDistance = p1.retina.linksDistance ?? minDistance;
105
- if (getDistance(pos1, pos2) > linksDistance ||
106
- getDistance(pos3, pos2) > linksDistance ||
107
- getDistance(pos3, pos1) > linksDistance) {
108
- return;
109
- }
110
- let colorTriangle = rangeColorToRgb(this._engine, triangleOptions.color);
111
- if (!colorTriangle) {
112
- const linkColor = linksOptions.id !== undefined
113
- ? container.particles.linksColors.get(linksOptions.id)
114
- : container.particles.linksColor;
115
- colorTriangle = getLinkColor(p1, p2, linkColor);
116
- }
117
- if (!colorTriangle) {
118
- return;
119
- }
120
- drawLinkTriangle({
121
- context: ctx,
122
- pos1,
123
- pos2,
124
- pos3,
125
- colorTriangle,
126
- opacityTriangle,
127
- hdr: container.hdr,
128
- });
129
- });
130
- };
131
- _drawTriangles = (options, p1, link, p1Links) => {
132
- const p2 = link.destination;
133
- if (!(options.links?.triangles.enable && p2.options.links?.triangles.enable)) {
122
+ _collectTriangles(options, p1, link, p1Links) {
123
+ const p2 = link.destination, triangleOptions = options.links?.triangles;
124
+ if (!triangleOptions?.enable || !p2.options.links?.triangles.enable) {
134
125
  return;
135
126
  }
136
127
  const vertices = p2.links?.filter(t => {
137
- const linkFreq = this._getLinkFrequency(p2, t.destination), minCount = 0;
138
- return (p2.options.links &&
139
- linkFreq <= p2.options.links.frequency &&
140
- p1Links.findIndex(l => l.destination === t.destination) >= minCount);
128
+ return (!t.isWarped &&
129
+ p2.options.links &&
130
+ this._getLinkFrequency(p2, t.destination) <= p2.options.links.frequency &&
131
+ p1Links.some(l => l.destination === t.destination));
141
132
  });
142
133
  if (!vertices?.length) {
143
134
  return;
144
135
  }
145
136
  for (const vertex of vertices) {
146
- const p3 = vertex.destination, triangleFreq = this._getTriangleFrequency(p1, p2, p3);
147
- if (triangleFreq > options.links.triangles.frequency) {
137
+ const p3 = vertex.destination;
138
+ if (this._getTriangleFrequency(p1, p2, p3) > (options.links?.triangles.frequency ?? defaultFrequency)) {
148
139
  continue;
149
140
  }
150
- this._drawLinkTriangle(p1, link, vertex);
141
+ const opacityTriangle = Math.ceil((link.opacity + vertex.opacity) * half * opacitySteps) / opacitySteps, colorTriangle = rangeColorToRgb(this._engine, triangleOptions.color) ?? link.color;
142
+ if (!colorTriangle) {
143
+ continue;
144
+ }
145
+ const colorStyle = this._getCachedStyle(colorTriangle), key = `${colorStyle}_${opacityTriangle}`;
146
+ let batch = this._triangleBatches.get(key);
147
+ if (!batch) {
148
+ batch = { colorStyle, opacity: opacityTriangle, coords: [] };
149
+ this._triangleBatches.set(key, batch);
150
+ }
151
+ const pos1 = p1.getPosition(), pos2 = p2.getPosition(), pos3 = p3.getPosition();
152
+ batch.coords.push(pos1.x, pos1.y, pos2.x, pos2.y, pos3.x, pos3.y);
151
153
  }
152
- };
153
- _getLinkFrequency = (p1, p2) => {
154
+ }
155
+ _getCachedStyle(rgb) {
156
+ const key = `${rgb.r},${rgb.g},${rgb.b}`;
157
+ let style = this._colorCache.get(key);
158
+ if (!style) {
159
+ style = getStyleFromRgb(rgb, this._container.hdr);
160
+ this._colorCache.set(key, style);
161
+ }
162
+ return style;
163
+ }
164
+ _getLinkFrequency(p1, p2) {
154
165
  return setLinkFrequency([p1, p2], this._freqs.links);
155
- };
156
- _getTriangleFrequency = (p1, p2, p3) => {
166
+ }
167
+ _getTriangleFrequency(p1, p2, p3) {
157
168
  return setLinkFrequency([p1, p2, p3], this._freqs.triangles);
158
- };
169
+ }
159
170
  }
package/browser/Linker.js CHANGED
@@ -1,17 +1,10 @@
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
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 = {
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
  };
@@ -38,15 +31,7 @@ export class Linker extends ParticlesInteractorBase {
38
31
  if (pos1.x < originPoint.x || pos1.y < originPoint.y || pos1.x > canvasSize.width || pos1.y > canvasSize.height) {
39
32
  return;
40
33
  }
41
- const linkOpt1 = p1.options.links, optOpacity = linkOpt1.opacity, optDistance = p1.retina.linksDistance ?? minDistance, warp = linkOpt1.warp;
42
- let range;
43
- if (warp) {
44
- range = new CircleWarp(pos1.x, pos1.y, optDistance, canvasSize);
45
- }
46
- else {
47
- range = new Circle(pos1.x, pos1.y, optDistance);
48
- }
49
- const query = container.particles.quadTree.query(range);
34
+ 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.quadTree.query(range);
50
35
  for (const p2 of query) {
51
36
  const linkOpt2 = p2.options.links;
52
37
  if (p1 === p2 ||
@@ -63,7 +48,7 @@ export class Linker extends ParticlesInteractorBase {
63
48
  if (pos2.x < originPoint.x || pos2.y < originPoint.y || pos2.x > canvasSize.width || pos2.y > canvasSize.height) {
64
49
  continue;
65
50
  }
66
- const distance = getLinkDistance(pos1, pos2, optDistance, canvasSize, warp && linkOpt2.warp);
51
+ const distDirect = getDistances(pos1, pos2).distance, distWarp = warp && linkOpt2.warp ? getWarpDistance(pos1, pos2, canvasSize) : distDirect, distance = Math.min(distDirect, distWarp);
67
52
  if (distance > optDistance) {
68
53
  continue;
69
54
  }
@@ -72,6 +57,8 @@ export class Linker extends ParticlesInteractorBase {
72
57
  p1.links.push({
73
58
  destination: p2,
74
59
  opacity: opacityLine,
60
+ color: this._getLinkColor(p1, p2),
61
+ isWarped: distWarp < distDirect,
75
62
  });
76
63
  }
77
64
  }
@@ -86,7 +73,17 @@ export class Linker extends ParticlesInteractorBase {
86
73
  }
87
74
  reset() {
88
75
  }
89
- _setColor = p1 => {
76
+ _getLinkColor(p1, p2) {
77
+ const container = this.container, linksOptions = p1.options.links;
78
+ if (!linksOptions) {
79
+ return;
80
+ }
81
+ const linkColor = linksOptions.id !== undefined
82
+ ? container.particles.linksColors.get(linksOptions.id)
83
+ : container.particles.linksColor;
84
+ return getLinkColor(p1, p2, linkColor);
85
+ }
86
+ _setColor(p1) {
90
87
  if (!p1.options.links) {
91
88
  return;
92
89
  }
@@ -97,13 +94,12 @@ export class Linker extends ParticlesInteractorBase {
97
94
  if (linkColor) {
98
95
  return;
99
96
  }
100
- const optColor = linksOptions.color;
101
- linkColor = getLinkRandomColor(this._engine, optColor, linksOptions.blink, linksOptions.consent);
97
+ linkColor = getLinkRandomColor(this._engine, linksOptions.color, linksOptions.blink, linksOptions.consent);
102
98
  if (linksOptions.id === undefined) {
103
99
  container.particles.linksColor = linkColor;
104
100
  }
105
101
  else {
106
102
  container.particles.linksColors.set(linksOptions.id, linkColor);
107
103
  }
108
- };
104
+ }
109
105
  }
package/browser/Utils.js CHANGED
@@ -1,79 +1,4 @@
1
- import { getDistance, getDistances, getRandom, getStyleFromRgb, rangeColorToRgb, } from "@tsparticles/engine";
2
- import { drawLine } from "@tsparticles/canvas-utils";
3
- export function drawTriangle(context, p1, p2, p3) {
4
- context.beginPath();
5
- context.moveTo(p1.x, p1.y);
6
- context.lineTo(p2.x, p2.y);
7
- context.lineTo(p3.x, p3.y);
8
- context.closePath();
9
- }
10
- export function drawLinkLine(params) {
11
- let drawn = false;
12
- const { begin, end, engine, maxDistance, context, canvasSize, width, colorLine, opacity, links, hdr } = params;
13
- if (getDistance(begin, end) <= maxDistance) {
14
- drawLine(context, begin, end);
15
- drawn = true;
16
- }
17
- else if (links.warp) {
18
- let pi1, pi2;
19
- const endNE = {
20
- x: end.x - canvasSize.width,
21
- y: end.y,
22
- }, d1 = getDistances(begin, endNE);
23
- if (d1.distance <= maxDistance) {
24
- const yi = begin.y - (d1.dy / d1.dx) * begin.x;
25
- pi1 = { x: 0, y: yi };
26
- pi2 = { x: canvasSize.width, y: yi };
27
- }
28
- else {
29
- const endSW = {
30
- x: end.x,
31
- y: end.y - canvasSize.height,
32
- }, d2 = getDistances(begin, endSW);
33
- if (d2.distance <= maxDistance) {
34
- const yi = begin.y - (d2.dy / d2.dx) * begin.x, xi = -yi / (d2.dy / d2.dx);
35
- pi1 = { x: xi, y: 0 };
36
- pi2 = { x: xi, y: canvasSize.height };
37
- }
38
- else {
39
- const endSE = {
40
- x: end.x - canvasSize.width,
41
- y: end.y - canvasSize.height,
42
- }, d3 = getDistances(begin, endSE);
43
- if (d3.distance <= maxDistance) {
44
- const yi = begin.y - (d3.dy / d3.dx) * begin.x, xi = -yi / (d3.dy / d3.dx);
45
- pi1 = { x: xi, y: yi };
46
- pi2 = { x: pi1.x + canvasSize.width, y: pi1.y + canvasSize.height };
47
- }
48
- }
49
- }
50
- if (pi1 && pi2) {
51
- drawLine(context, begin, pi1);
52
- drawLine(context, end, pi2);
53
- drawn = true;
54
- }
55
- }
56
- if (!drawn) {
57
- return;
58
- }
59
- context.lineWidth = width;
60
- context.strokeStyle = getStyleFromRgb(colorLine, hdr, opacity);
61
- const { shadow } = links;
62
- if (shadow.enable) {
63
- const shadowColor = rangeColorToRgb(engine, shadow.color);
64
- if (shadowColor) {
65
- context.shadowBlur = shadow.blur;
66
- context.shadowColor = getStyleFromRgb(shadowColor, hdr);
67
- }
68
- }
69
- context.stroke();
70
- }
71
- export function drawLinkTriangle(params) {
72
- const { context, hdr, pos1, pos2, pos3, colorTriangle, opacityTriangle } = params;
73
- drawTriangle(context, pos1, pos2, pos3);
74
- context.fillStyle = getStyleFromRgb(colorTriangle, hdr, opacityTriangle);
75
- context.fill();
76
- }
1
+ import { getRandom } from "@tsparticles/engine";
77
2
  export function getLinkKey(ids) {
78
3
  ids.sort((a, b) => a - b);
79
4
  return ids.join("_");
package/browser/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  export async function loadParticlesLinksInteraction(engine) {
2
- engine.checkVersion("4.0.0-alpha.21");
2
+ engine.checkVersion("4.0.0-alpha.23");
3
3
  await engine.register(async (e) => {
4
4
  const [{ ensureInteractivityPluginLoaded }, { LinksPlugin },] = await Promise.all([
5
5
  import("@tsparticles/plugin-interactivity"),
@@ -9,7 +9,7 @@ export async function loadParticlesLinksInteraction(engine) {
9
9
  e.addPlugin(new LinksPlugin(e));
10
10
  e.addInteractor?.("particlesLinks", async (container) => {
11
11
  const { Linker } = await import("./Linker.js");
12
- return new Linker(container, engine);
12
+ return new Linker(container, e);
13
13
  });
14
14
  });
15
15
  }