Skip to content

Commit b548da3

Browse files
committed
fix: fixed links issues
1 parent 7815ebc commit b548da3

1 file changed

Lines changed: 99 additions & 160 deletions

File tree

interactions/particles/links/src/LinkInstance.ts

Lines changed: 99 additions & 160 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import {
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";
1312
import 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";
1514
import { setLinkFrequency } from "./Utils.js";
1615

1716
const 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

3322
export 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

Comments
 (0)