11import {
22 type Engine ,
33 type IContainerPlugin ,
4- type IRgb ,
54 getLinkColor as engineGetLinkColor ,
65 getRandom ,
76 getRangeValue ,
@@ -11,97 +10,34 @@ import {
1110 rangeColorToRgb ,
1211} from "@tsparticles/engine" ;
1312import type { ILink , IParticlesFrequencies , ITwinkle } from "./Interfaces.js" ;
14- import type { LinkBatch , LinkContainer , LinkParticle , ParticlesLinkOptions , TriangleBatch } from "./Types.js" ;
13+ import type { LinkContainer , LinkParticle , ParticlesLinkOptions } from "./Types.js" ;
1514import { setLinkFrequency } from "./Utils.js" ;
1615
1716const minOpacity = 0 ,
18- minDistance = 0 ,
1917 minWidth = 0 ,
18+ minDistance = 0 ,
2019 maxFrequency = 1 ,
21- defaultFrequency = 0 ,
22- opacitySteps = 10 ,
23- defaultWidth = 0 ,
24- triangleCoordsCount = 6 ,
25- lineCoordsCount = 4 ,
26- x1Offset = 0 ,
27- y1Offset = 1 ,
28- x2Offset = 2 ,
29- y2Offset = 3 ,
30- x3Offset = 4 ,
31- y3Offset = 5 ;
20+ defaultFrequency = 0 ;
3221
3322export class LinkInstance implements IContainerPlugin {
34- private readonly _colorCache = new Map < string , string > ( ) ;
3523 private readonly _container : LinkContainer ;
3624 private readonly _engine : Engine ;
3725 private readonly _freqs : IParticlesFrequencies ;
38- private readonly _lineBatches = new Map < string , LinkBatch > ( ) ;
39- private readonly _triangleBatches = new Map < string , TriangleBatch > ( ) ;
4026
4127 constructor ( container : LinkContainer , engine : Engine ) {
4228 this . _container = container ;
4329 this . _engine = engine ;
4430 this . _freqs = { links : new Map ( ) , triangles : new Map ( ) } ;
4531 }
4632
47- draw ( context : CanvasRenderingContext2D ) : void {
48- for ( const [ , batch ] of this . _triangleBatches ) {
49- context . save ( ) ;
50- context . fillStyle = batch . colorStyle ;
51- context . globalAlpha = batch . opacity ;
52- context . beginPath ( ) ;
53-
54- for ( let i = 0 ; i < batch . coords . length ; i += triangleCoordsCount ) {
55- const x1 = batch . coords [ i + x1Offset ] ?? originPoint . x ,
56- y1 = batch . coords [ i + y1Offset ] ?? originPoint . y ,
57- x2 = batch . coords [ i + x2Offset ] ?? originPoint . x ,
58- y2 = batch . coords [ i + y2Offset ] ?? originPoint . y ,
59- x3 = batch . coords [ i + x3Offset ] ?? originPoint . x ,
60- y3 = batch . coords [ i + y3Offset ] ?? originPoint . y ;
61-
62- context . moveTo ( x1 , y1 ) ;
63- context . lineTo ( x2 , y2 ) ;
64- context . lineTo ( x3 , y3 ) ;
65- }
66-
67- context . fill ( ) ;
68- context . restore ( ) ;
69- }
70-
71- for ( const [ , batch ] of this . _lineBatches ) {
72- context . save ( ) ;
73- context . strokeStyle = batch . colorStyle ;
74- context . lineWidth = batch . width ;
75- context . globalAlpha = batch . opacity ;
76- context . beginPath ( ) ;
77-
78- for ( let i = 0 ; i < batch . coords . length ; i += lineCoordsCount ) {
79- const x1 = batch . coords [ i + x1Offset ] ?? originPoint . x ,
80- y1 = batch . coords [ i + y1Offset ] ?? originPoint . y ,
81- x2 = batch . coords [ i + x2Offset ] ?? originPoint . x ,
82- y2 = batch . coords [ i + y2Offset ] ?? originPoint . y ;
83-
84- context . moveTo ( x1 , y1 ) ;
85- context . lineTo ( x2 , y2 ) ;
86- }
87-
88- context . stroke ( ) ;
89- context . restore ( ) ;
90- }
91-
92- this . _lineBatches . clear ( ) ;
93- this . _triangleBatches . clear ( ) ;
94- }
95-
96- drawParticle ( _context : CanvasRenderingContext2D , particle : LinkParticle ) : void {
33+ drawParticle ( context : CanvasRenderingContext2D , particle : LinkParticle ) : void {
9734 const { links, options } = particle ;
9835
9936 if ( ! links ?. length || ! options . links ) {
10037 return ;
10138 }
10239
103- const canvasSize = this . _container . canvas . size ,
104- pos1 = particle . getPosition ( ) ,
40+ const pos1 = particle . getPosition ( ) ,
10541 linkOpts = options . links ;
10642
10743 for ( const link of links ) {
@@ -113,83 +49,20 @@ export class LinkInstance implements IContainerPlugin {
11349 }
11450
11551 if ( ! link . isWarped ) {
116- this . _collectTriangles ( options , particle , link , links , pos1 ) ;
52+ this . _drawTriangles ( options , particle , link , links , pos1 , context ) ;
11753 }
11854
11955 if ( link . opacity <= minOpacity || ( particle . retina . linksWidth ?? minWidth ) <= minWidth ) {
12056 continue ;
12157 }
12258
123- let opacity = link . opacity ,
124- colorLine = link . color ;
125-
126- const twinkle = ( particle . options [ "twinkle" ] as ITwinkle | undefined ) ?. links ;
127-
128- if ( twinkle ?. enable && getRandom ( ) < twinkle . frequency ) {
129- const twinkleRgb = rangeColorToRgb ( this . _engine , twinkle . color ) ;
130-
131- if ( twinkleRgb ) {
132- colorLine = twinkleRgb ;
133- opacity = getRangeValue ( twinkle . opacity ) ;
134- }
135- }
136-
137- if ( ! colorLine ) {
138- const linkColor =
139- linkOpts . id !== undefined
140- ? this . _container . particles . linksColors . get ( linkOpts . id )
141- : this . _container . particles . linksColor ;
142-
143- colorLine = engineGetLinkColor ( particle , link . destination , linkColor ) ;
144- }
145-
146- if ( ! colorLine ) {
147- continue ;
148- }
149-
150- const qOpacity = Math . ceil ( opacity * opacitySteps ) / opacitySteps ,
151- colorStyle = this . _getCachedStyle ( colorLine ) ,
152- width = particle . retina . linksWidth ?? defaultWidth ,
153- key = `${ colorStyle } _${ qOpacity } _${ width } ` ;
154-
155- let batch = this . _lineBatches . get ( key ) ;
156-
157- if ( ! batch ) {
158- batch = { colorStyle, opacity : qOpacity , width, coords : [ ] } ;
159- this . _lineBatches . set ( key , batch ) ;
160- }
161-
162- const pos2 = link . destination . getPosition ( ) ;
163-
164- if ( link . isWarped ) {
165- const dx = pos2 . x - pos1 . x ,
166- dy = pos2 . y - pos1 . y ;
167-
168- let sx = originPoint . x ,
169- sy = originPoint . y ;
170-
171- if ( Math . abs ( dx ) > canvasSize . width * half ) {
172- sx = dx > minDistance ? - canvasSize . width : canvasSize . width ;
173- }
174-
175- if ( Math . abs ( dy ) > canvasSize . height * half ) {
176- sy = dy > minDistance ? - canvasSize . height : canvasSize . height ;
177- }
178-
179- const v2 = { x : pos2 . x + sx , y : pos2 . y + sy } ,
180- v1 = { x : pos1 . x - sx , y : pos1 . y - sy } ;
181-
182- batch . coords . push ( pos1 . x , pos1 . y , v2 . x , v2 . y , v1 . x , v1 . y , pos2 . x , pos2 . y ) ;
183- } else {
184- batch . coords . push ( pos1 . x , pos1 . y , pos2 . x , pos2 . y ) ;
185- }
59+ this . _drawLinkLine ( context , particle , link , pos1 ) ;
18660 }
18761 }
18862
18963 init ( ) : Promise < void > {
19064 this . _freqs . links . clear ( ) ;
19165 this . _freqs . triangles . clear ( ) ;
192- this . _colorCache . clear ( ) ;
19366 return Promise . resolve ( ) ;
19467 }
19568
@@ -213,12 +86,91 @@ export class LinkInstance implements IContainerPlugin {
21386 particle . links = [ ] ;
21487 }
21588
216- private _collectTriangles (
89+ private _drawLinkLine (
90+ context : CanvasRenderingContext2D ,
91+ p1 : LinkParticle ,
92+ link : ILink ,
93+ pos1 : ReturnType < LinkParticle [ "getPosition" ] > ,
94+ ) : void {
95+ const linkOpts = p1 . options . links ;
96+
97+ if ( ! linkOpts ?. enable ) {
98+ return ;
99+ }
100+
101+ let opacity = link . opacity ,
102+ colorLine = link . color ;
103+
104+ const twinkle = ( p1 . options [ "twinkle" ] as ITwinkle | undefined ) ?. links ;
105+
106+ if ( twinkle ?. enable && getRandom ( ) < twinkle . frequency ) {
107+ const twinkleRgb = rangeColorToRgb ( this . _engine , twinkle . color ) ;
108+
109+ if ( twinkleRgb ) {
110+ colorLine = twinkleRgb ;
111+ opacity = getRangeValue ( twinkle . opacity ) ;
112+ }
113+ }
114+
115+ if ( ! colorLine ) {
116+ const linkColor =
117+ linkOpts . id !== undefined
118+ ? this . _container . particles . linksColors . get ( linkOpts . id )
119+ : this . _container . particles . linksColor ;
120+
121+ colorLine = engineGetLinkColor ( p1 , link . destination , linkColor ) ;
122+ }
123+
124+ if ( ! colorLine ) {
125+ return ;
126+ }
127+
128+ const width = p1 . retina . linksWidth ?? minWidth ,
129+ pos2 = link . destination . getPosition ( ) ,
130+ canvasSize = this . _container . canvas . size ;
131+
132+ context . save ( ) ;
133+ context . lineWidth = width ;
134+ context . strokeStyle = getStyleFromRgb ( colorLine , this . _container . hdr ) ;
135+ context . globalAlpha = opacity ;
136+ context . beginPath ( ) ;
137+
138+ if ( link . isWarped ) {
139+ const dx = pos2 . x - pos1 . x ,
140+ dy = pos2 . y - pos1 . y ;
141+
142+ let sx = originPoint . x ,
143+ sy = originPoint . y ;
144+
145+ if ( Math . abs ( dx ) > canvasSize . width * half ) {
146+ sx = dx > minDistance ? - canvasSize . width : canvasSize . width ;
147+ }
148+
149+ if ( Math . abs ( dy ) > canvasSize . height * half ) {
150+ sy = dy > minDistance ? - canvasSize . height : canvasSize . height ;
151+ }
152+
153+ /* draw the two half-segments that cross the canvas boundary */
154+ context . moveTo ( pos1 . x , pos1 . y ) ;
155+ context . lineTo ( pos2 . x + sx , pos2 . y + sy ) ;
156+ context . moveTo ( pos1 . x - sx , pos1 . y - sy ) ;
157+ context . lineTo ( pos2 . x , pos2 . y ) ;
158+ } else {
159+ context . moveTo ( pos1 . x , pos1 . y ) ;
160+ context . lineTo ( pos2 . x , pos2 . y ) ;
161+ }
162+
163+ context . stroke ( ) ;
164+ context . restore ( ) ;
165+ }
166+
167+ private _drawTriangles (
217168 options : ParticlesLinkOptions ,
218169 p1 : LinkParticle ,
219170 link : ILink ,
220171 p1Links : ILink [ ] ,
221172 pos1 : ReturnType < LinkParticle [ "getPosition" ] > ,
173+ context : CanvasRenderingContext2D ,
222174 ) : void {
223175 const p2 = link . destination ,
224176 triangleOptions = options . links ?. triangles ;
@@ -251,39 +203,26 @@ export class LinkInstance implements IContainerPlugin {
251203 continue ;
252204 }
253205
254- const opacityTriangle = Math . ceil ( ( link . opacity + vertex . opacity ) * half * opacitySteps ) / opacitySteps ,
206+ const opacityTriangle = triangleOptions . opacity ?? ( link . opacity + vertex . opacity ) * half ,
255207 colorTriangle = rangeColorToRgb ( this . _engine , triangleOptions . color ) ?? link . color ;
256208
257- if ( ! colorTriangle ) {
209+ if ( ! colorTriangle || opacityTriangle <= minOpacity ) {
258210 continue ;
259211 }
260212
261- const colorStyle = this . _getCachedStyle ( colorTriangle ) ,
262- key = `${ colorStyle } _${ opacityTriangle } ` ;
263-
264- let batch = this . _triangleBatches . get ( key ) ;
265-
266- if ( ! batch ) {
267- batch = { colorStyle, opacity : opacityTriangle , coords : [ ] } ;
268- this . _triangleBatches . set ( key , batch ) ;
269- }
270-
271213 const pos3 = p3 . getPosition ( ) ;
272214
273- batch . coords . push ( pos1 . x , pos1 . y , pos2 . x , pos2 . y , pos3 . x , pos3 . y ) ;
274- }
275- }
276-
277- private _getCachedStyle ( rgb : IRgb ) : string {
278- const key = `${ rgb . r } ,${ rgb . g } ,${ rgb . b } ` ;
279- let style = this . _colorCache . get ( key ) ;
280-
281- if ( ! style ) {
282- style = getStyleFromRgb ( rgb , this . _container . hdr ) ;
283- this . _colorCache . set ( key , style ) ;
215+ context . save ( ) ;
216+ context . fillStyle = getStyleFromRgb ( colorTriangle , this . _container . hdr ) ;
217+ context . globalAlpha = opacityTriangle ;
218+ context . beginPath ( ) ;
219+ context . moveTo ( pos1 . x , pos1 . y ) ;
220+ context . lineTo ( pos2 . x , pos2 . y ) ;
221+ context . lineTo ( pos3 . x , pos3 . y ) ;
222+ context . closePath ( ) ;
223+ context . fill ( ) ;
224+ context . restore ( ) ;
284225 }
285-
286- return style ;
287226 }
288227
289228 private _getLinkFrequency ( p1 : LinkParticle , p2 : LinkParticle ) : number {
0 commit comments