@tsparticles/plugin-polygon-mask 3.0.2 → 3.1.0
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/browser/PolygonMaskInstance.js +46 -38
- package/browser/utils.js +21 -10
- package/cjs/PolygonMaskInstance.js +45 -37
- package/cjs/utils.js +21 -10
- package/esm/PolygonMaskInstance.js +46 -38
- package/esm/utils.js +21 -10
- package/package.json +2 -2
- package/report.html +2 -2
- package/tsparticles.plugin.polygon-mask.js +83 -46
- package/tsparticles.plugin.polygon-mask.min.js +1 -1
- package/tsparticles.plugin.polygon-mask.min.js.LICENSE.txt +1 -1
- package/types/Options/Classes/PolygonMaskInline.d.ts +1 -2
- package/umd/PolygonMaskInstance.js +45 -37
- package/umd/utils.js +21 -10
|
@@ -1,6 +1,9 @@
|
|
|
1
|
-
import { deepExtend, errorPrefix, getDistance, getDistances, getRandom, isArray, isString, itemFromArray, } from "@tsparticles/engine";
|
|
1
|
+
import { deepExtend, errorPrefix, getDistance, getDistances, getRandom, isArray, isString, itemFromArray, percentDenominator, } from "@tsparticles/engine";
|
|
2
2
|
import { calcClosestPtOnSegment, drawPolygonMask, drawPolygonMaskPath, parsePaths, segmentBounce } from "./utils.js";
|
|
3
|
-
const noPolygonDataLoaded = `${errorPrefix} No polygon data loaded.`, noPolygonFound = `${errorPrefix} No polygon found, you need to specify SVG url in config
|
|
3
|
+
const noPolygonDataLoaded = `${errorPrefix} No polygon data loaded.`, noPolygonFound = `${errorPrefix} No polygon found, you need to specify SVG url in config.`, origin = {
|
|
4
|
+
x: 0,
|
|
5
|
+
y: 0,
|
|
6
|
+
}, half = 0.5, double = 2;
|
|
4
7
|
export class PolygonMaskInstance {
|
|
5
8
|
constructor(container, engine) {
|
|
6
9
|
this._checkInsidePolygon = (position) => {
|
|
@@ -11,19 +14,20 @@ export class PolygonMaskInstance {
|
|
|
11
14
|
if (!this.raw) {
|
|
12
15
|
throw new Error(noPolygonFound);
|
|
13
16
|
}
|
|
14
|
-
const canvasSize = container.canvas.size, x = position?.x ?? getRandom() * canvasSize.width, y = position?.y ?? getRandom() * canvasSize.height;
|
|
17
|
+
const canvasSize = container.canvas.size, x = position?.x ?? getRandom() * canvasSize.width, y = position?.y ?? getRandom() * canvasSize.height, indexOffset = 1;
|
|
15
18
|
let inside = false;
|
|
16
|
-
for (let i = 0, j = this.raw.length -
|
|
19
|
+
for (let i = 0, j = this.raw.length - indexOffset; i < this.raw.length; j = i++) {
|
|
17
20
|
const pi = this.raw[i], pj = this.raw[j], intersect = pi.y > y !== pj.y > y && x < ((pj.x - pi.x) * (y - pi.y)) / (pj.y - pi.y) + pi.x;
|
|
18
21
|
if (intersect) {
|
|
19
22
|
inside = !inside;
|
|
20
23
|
}
|
|
21
24
|
}
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
if (options.type === "inside") {
|
|
26
|
+
return inside;
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
return options.type === "outside" ? !inside : false;
|
|
30
|
+
}
|
|
27
31
|
};
|
|
28
32
|
this._createPath2D = () => {
|
|
29
33
|
const container = this._container, options = container.actualOptions.polygon;
|
|
@@ -45,13 +49,14 @@ export class PolygonMaskInstance {
|
|
|
45
49
|
else {
|
|
46
50
|
delete path.path2d;
|
|
47
51
|
}
|
|
48
|
-
if (path.path2d
|
|
52
|
+
if (path.path2d ?? !this.raw) {
|
|
49
53
|
continue;
|
|
50
54
|
}
|
|
51
55
|
path.path2d = new Path2D();
|
|
52
|
-
|
|
56
|
+
const firstIndex = 0, firstPoint = this.raw[firstIndex];
|
|
57
|
+
path.path2d.moveTo(firstPoint.x, firstPoint.y);
|
|
53
58
|
this.raw.forEach((pos, i) => {
|
|
54
|
-
if (i >
|
|
59
|
+
if (i > firstIndex) {
|
|
55
60
|
path.path2d?.lineTo(pos.x, pos.y);
|
|
56
61
|
}
|
|
57
62
|
});
|
|
@@ -63,7 +68,7 @@ export class PolygonMaskInstance {
|
|
|
63
68
|
if (!options) {
|
|
64
69
|
return;
|
|
65
70
|
}
|
|
66
|
-
const url = svgUrl
|
|
71
|
+
const url = svgUrl ?? options.url, forceDownload = force ?? false;
|
|
67
72
|
if (!url || (this.paths !== undefined && !forceDownload)) {
|
|
68
73
|
return this.raw;
|
|
69
74
|
}
|
|
@@ -89,11 +94,11 @@ export class PolygonMaskInstance {
|
|
|
89
94
|
if (!polygonMaskOptions) {
|
|
90
95
|
return;
|
|
91
96
|
}
|
|
92
|
-
if (!this.raw
|
|
97
|
+
if (!this.raw?.length || !this.paths?.length) {
|
|
93
98
|
throw new Error(noPolygonDataLoaded);
|
|
94
99
|
}
|
|
95
100
|
let offset = 0, point;
|
|
96
|
-
const totalLength = this.paths.reduce((tot, path) => tot + path.length,
|
|
101
|
+
const baseAccumulator = 0, totalLength = this.paths.reduce((tot, path) => tot + path.length, baseAccumulator), distance = totalLength / options.particles.number.value;
|
|
97
102
|
for (const path of this.paths) {
|
|
98
103
|
const pathDistance = distance * index - offset;
|
|
99
104
|
if (pathDistance <= path.length) {
|
|
@@ -106,12 +111,12 @@ export class PolygonMaskInstance {
|
|
|
106
111
|
}
|
|
107
112
|
const scale = this._scale;
|
|
108
113
|
return {
|
|
109
|
-
x: (point?.x ??
|
|
110
|
-
y: (point?.y ??
|
|
114
|
+
x: (point?.x ?? origin.x) * scale + (this.offset?.x ?? origin.x),
|
|
115
|
+
y: (point?.y ?? origin.y) * scale + (this.offset?.y ?? origin.y),
|
|
111
116
|
};
|
|
112
117
|
};
|
|
113
118
|
this._getPointByIndex = (index) => {
|
|
114
|
-
if (!this.raw
|
|
119
|
+
if (!this.raw?.length) {
|
|
115
120
|
throw new Error(noPolygonDataLoaded);
|
|
116
121
|
}
|
|
117
122
|
const coords = this.raw[index % this.raw.length];
|
|
@@ -121,7 +126,7 @@ export class PolygonMaskInstance {
|
|
|
121
126
|
};
|
|
122
127
|
};
|
|
123
128
|
this._getRandomPoint = () => {
|
|
124
|
-
if (!this.raw
|
|
129
|
+
if (!this.raw?.length) {
|
|
125
130
|
throw new Error(noPolygonDataLoaded);
|
|
126
131
|
}
|
|
127
132
|
const coords = itemFromArray(this.raw);
|
|
@@ -135,13 +140,13 @@ export class PolygonMaskInstance {
|
|
|
135
140
|
if (!options) {
|
|
136
141
|
return;
|
|
137
142
|
}
|
|
138
|
-
if (!this.raw
|
|
143
|
+
if (!this.raw?.length || !this.paths?.length) {
|
|
139
144
|
throw new Error(noPolygonDataLoaded);
|
|
140
145
|
}
|
|
141
|
-
const path = itemFromArray(this.paths), distance = Math.floor(getRandom() * path.length) +
|
|
146
|
+
const path = itemFromArray(this.paths), offset = 1, distance = Math.floor(getRandom() * path.length) + offset, point = path.element.getPointAtLength(distance), scale = this._scale;
|
|
142
147
|
return {
|
|
143
|
-
x: point.x * scale + (this.offset?.x
|
|
144
|
-
y: point.y * scale + (this.offset?.y
|
|
148
|
+
x: point.x * scale + (this.offset?.x ?? origin.x),
|
|
149
|
+
y: point.y * scale + (this.offset?.y ?? origin.y),
|
|
145
150
|
};
|
|
146
151
|
};
|
|
147
152
|
this._initRawData = async (force) => {
|
|
@@ -179,7 +184,7 @@ export class PolygonMaskInstance {
|
|
|
179
184
|
if (!options) {
|
|
180
185
|
return;
|
|
181
186
|
}
|
|
182
|
-
const parser = new DOMParser(), doc = parser.parseFromString(xml, "image/svg+xml"), svg = doc.getElementsByTagName("svg")[
|
|
187
|
+
const parser = new DOMParser(), doc = parser.parseFromString(xml, "image/svg+xml"), firstIndex = 0, svg = doc.getElementsByTagName("svg")[firstIndex];
|
|
183
188
|
let svgPaths = svg.getElementsByTagName("path");
|
|
184
189
|
if (!svgPaths.length) {
|
|
185
190
|
svgPaths = doc.getElementsByTagName("path");
|
|
@@ -202,8 +207,8 @@ export class PolygonMaskInstance {
|
|
|
202
207
|
y: 50,
|
|
203
208
|
}, canvasSize = container.canvas.size;
|
|
204
209
|
this.offset = {
|
|
205
|
-
x: (canvasSize.width * position.x) /
|
|
206
|
-
y: (canvasSize.height * position.y) /
|
|
210
|
+
x: (canvasSize.width * position.x) / percentDenominator - this.dimension.width * half,
|
|
211
|
+
y: (canvasSize.height * position.y) / percentDenominator - this.dimension.height * half,
|
|
207
212
|
};
|
|
208
213
|
return parsePaths(this.paths, scale, this.offset);
|
|
209
214
|
};
|
|
@@ -214,8 +219,8 @@ export class PolygonMaskInstance {
|
|
|
214
219
|
}
|
|
215
220
|
if (options.type === "inside" || options.type === "outside") {
|
|
216
221
|
let closest, dx, dy;
|
|
217
|
-
const pos = particle.getPosition(), radius = particle.getRadius();
|
|
218
|
-
for (let i = 0, j = this.raw.length -
|
|
222
|
+
const pos = particle.getPosition(), radius = particle.getRadius(), offset = 1;
|
|
223
|
+
for (let i = 0, j = this.raw.length - offset; i < this.raw.length; j = i++) {
|
|
219
224
|
const pi = this.raw[i], pj = this.raw[j];
|
|
220
225
|
closest = calcClosestPtOnSegment(pi, pj, pos);
|
|
221
226
|
const dist = getDistances(pos, closest);
|
|
@@ -226,7 +231,7 @@ export class PolygonMaskInstance {
|
|
|
226
231
|
}
|
|
227
232
|
}
|
|
228
233
|
if (closest && dx !== undefined && dy !== undefined && !this._checkInsidePolygon(pos)) {
|
|
229
|
-
const factor = { x: 1, y: 1 }, diameter = radius *
|
|
234
|
+
const factor = { x: 1, y: 1 }, diameter = radius * double, inverse = -1;
|
|
230
235
|
if (pos.x >= closest.x) {
|
|
231
236
|
factor.x = -1;
|
|
232
237
|
}
|
|
@@ -235,15 +240,15 @@ export class PolygonMaskInstance {
|
|
|
235
240
|
}
|
|
236
241
|
particle.position.x = closest.x + diameter * factor.x;
|
|
237
242
|
particle.position.y = closest.y + diameter * factor.y;
|
|
238
|
-
particle.velocity.mult(
|
|
243
|
+
particle.velocity.mult(inverse);
|
|
239
244
|
return true;
|
|
240
245
|
}
|
|
241
246
|
}
|
|
242
247
|
else if (options.type === "inline" && particle.initialPosition) {
|
|
243
248
|
const dist = getDistance(particle.initialPosition, particle.getPosition()), { velocity } = particle;
|
|
244
249
|
if (dist > this._moveRadius) {
|
|
245
|
-
velocity.x = velocity.y
|
|
246
|
-
velocity.y = velocity.x
|
|
250
|
+
velocity.x = velocity.y * half - velocity.x;
|
|
251
|
+
velocity.y = velocity.x * half - velocity.y;
|
|
247
252
|
return true;
|
|
248
253
|
}
|
|
249
254
|
}
|
|
@@ -343,8 +348,8 @@ export class PolygonMaskInstance {
|
|
|
343
348
|
return this._polygonBounce(particle, delta, direction);
|
|
344
349
|
}
|
|
345
350
|
particlePosition(position) {
|
|
346
|
-
const options = this._container.actualOptions.polygon;
|
|
347
|
-
if (!(options?.enable && (this.raw?.length ??
|
|
351
|
+
const options = this._container.actualOptions.polygon, defaultLength = 0;
|
|
352
|
+
if (!(options?.enable && (this.raw?.length ?? defaultLength) > defaultLength)) {
|
|
348
353
|
return;
|
|
349
354
|
}
|
|
350
355
|
return deepExtend({}, position ? position : this._randomPoint());
|
|
@@ -368,10 +373,13 @@ export class PolygonMaskInstance {
|
|
|
368
373
|
if (this.redrawTimeout) {
|
|
369
374
|
clearTimeout(this.redrawTimeout);
|
|
370
375
|
}
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
376
|
+
const timeout = 250;
|
|
377
|
+
this.redrawTimeout = window.setTimeout(() => {
|
|
378
|
+
void (async () => {
|
|
379
|
+
await this._initRawData(true);
|
|
380
|
+
await container.particles.redraw();
|
|
381
|
+
})();
|
|
382
|
+
}, timeout);
|
|
375
383
|
}
|
|
376
384
|
stop() {
|
|
377
385
|
delete this.raw;
|
package/browser/utils.js
CHANGED
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
import { Vector, getDistances, getStyleFromRgb, rangeColorToRgb } from "@tsparticles/engine";
|
|
2
|
+
const squareExp = 2, inSegmentRange = {
|
|
3
|
+
min: 0,
|
|
4
|
+
max: 1,
|
|
5
|
+
}, double = 2;
|
|
2
6
|
export function drawPolygonMask(context, rawData, stroke) {
|
|
3
7
|
const color = rangeColorToRgb(stroke.color);
|
|
4
8
|
if (!color) {
|
|
5
9
|
return;
|
|
6
10
|
}
|
|
11
|
+
const firstIndex = 0, firstItem = rawData[firstIndex];
|
|
7
12
|
context.beginPath();
|
|
8
|
-
context.moveTo(
|
|
13
|
+
context.moveTo(firstItem.x, firstItem.y);
|
|
9
14
|
for (const item of rawData) {
|
|
10
15
|
context.lineTo(item.x, item.y);
|
|
11
16
|
}
|
|
@@ -15,7 +20,13 @@ export function drawPolygonMask(context, rawData, stroke) {
|
|
|
15
20
|
context.stroke();
|
|
16
21
|
}
|
|
17
22
|
export function drawPolygonMaskPath(context, path, stroke, position) {
|
|
18
|
-
|
|
23
|
+
const defaultTransform = {
|
|
24
|
+
a: 1,
|
|
25
|
+
b: 0,
|
|
26
|
+
c: 0,
|
|
27
|
+
d: 1,
|
|
28
|
+
};
|
|
29
|
+
context.setTransform(defaultTransform.a, defaultTransform.b, defaultTransform.c, defaultTransform.d, position.x, position.y);
|
|
19
30
|
const color = rangeColorToRgb(stroke.color);
|
|
20
31
|
if (!color) {
|
|
21
32
|
return;
|
|
@@ -23,12 +34,12 @@ export function drawPolygonMaskPath(context, path, stroke, position) {
|
|
|
23
34
|
context.strokeStyle = getStyleFromRgb(color, stroke.opacity);
|
|
24
35
|
context.lineWidth = stroke.width;
|
|
25
36
|
context.stroke(path);
|
|
26
|
-
context.
|
|
37
|
+
context.resetTransform();
|
|
27
38
|
}
|
|
28
39
|
export function parsePaths(paths, scale, offset) {
|
|
29
|
-
const res = [];
|
|
40
|
+
const res = [], defaultCount = 0;
|
|
30
41
|
for (const path of paths) {
|
|
31
|
-
const segments = path.element.pathSegList, len = segments?.numberOfItems ??
|
|
42
|
+
const segments = path.element.pathSegList, len = segments?.numberOfItems ?? defaultCount, p = {
|
|
32
43
|
x: 0,
|
|
33
44
|
y: 0,
|
|
34
45
|
};
|
|
@@ -84,23 +95,23 @@ export function parsePaths(paths, scale, offset) {
|
|
|
84
95
|
return res;
|
|
85
96
|
}
|
|
86
97
|
export function calcClosestPtOnSegment(s1, s2, pos) {
|
|
87
|
-
const { dx: dx1, dy: dy1 } = getDistances(pos, s1), { dx: dx2, dy: dy2 } = getDistances(s2, s1), t = (dx1 * dx2 + dy1 * dy2) / (dx2 **
|
|
98
|
+
const { dx: dx1, dy: dy1 } = getDistances(pos, s1), { dx: dx2, dy: dy2 } = getDistances(s2, s1), t = (dx1 * dx2 + dy1 * dy2) / (dx2 ** squareExp + dy2 ** squareExp), res = {
|
|
88
99
|
x: s1.x + dx2 * t,
|
|
89
100
|
y: s1.y + dy2 * t,
|
|
90
|
-
isOnSegment: t >=
|
|
101
|
+
isOnSegment: t >= inSegmentRange.min && t <= inSegmentRange.max,
|
|
91
102
|
};
|
|
92
|
-
if (t <
|
|
103
|
+
if (t < inSegmentRange.min) {
|
|
93
104
|
res.x = s1.x;
|
|
94
105
|
res.y = s1.y;
|
|
95
106
|
}
|
|
96
|
-
else if (t >
|
|
107
|
+
else if (t > inSegmentRange.max) {
|
|
97
108
|
res.x = s2.x;
|
|
98
109
|
res.y = s2.y;
|
|
99
110
|
}
|
|
100
111
|
return res;
|
|
101
112
|
}
|
|
102
113
|
export function segmentBounce(start, stop, velocity) {
|
|
103
|
-
const { dx, dy } = getDistances(start, stop), wallAngle = Math.atan2(dy, dx), wallNormal = Vector.create(Math.sin(wallAngle), -Math.cos(wallAngle)), d =
|
|
114
|
+
const { dx, dy } = getDistances(start, stop), wallAngle = Math.atan2(dy, dx), wallNormal = Vector.create(Math.sin(wallAngle), -Math.cos(wallAngle)), d = double * (velocity.x * wallNormal.x + velocity.y * wallNormal.y);
|
|
104
115
|
wallNormal.multTo(d);
|
|
105
116
|
velocity.subFrom(wallNormal);
|
|
106
117
|
}
|
|
@@ -3,7 +3,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.PolygonMaskInstance = void 0;
|
|
4
4
|
const engine_1 = require("@tsparticles/engine");
|
|
5
5
|
const utils_js_1 = require("./utils.js");
|
|
6
|
-
const noPolygonDataLoaded = `${engine_1.errorPrefix} No polygon data loaded.`, noPolygonFound = `${engine_1.errorPrefix} No polygon found, you need to specify SVG url in config
|
|
6
|
+
const noPolygonDataLoaded = `${engine_1.errorPrefix} No polygon data loaded.`, noPolygonFound = `${engine_1.errorPrefix} No polygon found, you need to specify SVG url in config.`, origin = {
|
|
7
|
+
x: 0,
|
|
8
|
+
y: 0,
|
|
9
|
+
}, half = 0.5, double = 2;
|
|
7
10
|
class PolygonMaskInstance {
|
|
8
11
|
constructor(container, engine) {
|
|
9
12
|
this._checkInsidePolygon = (position) => {
|
|
@@ -14,19 +17,20 @@ class PolygonMaskInstance {
|
|
|
14
17
|
if (!this.raw) {
|
|
15
18
|
throw new Error(noPolygonFound);
|
|
16
19
|
}
|
|
17
|
-
const canvasSize = container.canvas.size, x = position?.x ?? (0, engine_1.getRandom)() * canvasSize.width, y = position?.y ?? (0, engine_1.getRandom)() * canvasSize.height;
|
|
20
|
+
const canvasSize = container.canvas.size, x = position?.x ?? (0, engine_1.getRandom)() * canvasSize.width, y = position?.y ?? (0, engine_1.getRandom)() * canvasSize.height, indexOffset = 1;
|
|
18
21
|
let inside = false;
|
|
19
|
-
for (let i = 0, j = this.raw.length -
|
|
22
|
+
for (let i = 0, j = this.raw.length - indexOffset; i < this.raw.length; j = i++) {
|
|
20
23
|
const pi = this.raw[i], pj = this.raw[j], intersect = pi.y > y !== pj.y > y && x < ((pj.x - pi.x) * (y - pi.y)) / (pj.y - pi.y) + pi.x;
|
|
21
24
|
if (intersect) {
|
|
22
25
|
inside = !inside;
|
|
23
26
|
}
|
|
24
27
|
}
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
28
|
+
if (options.type === "inside") {
|
|
29
|
+
return inside;
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
return options.type === "outside" ? !inside : false;
|
|
33
|
+
}
|
|
30
34
|
};
|
|
31
35
|
this._createPath2D = () => {
|
|
32
36
|
const container = this._container, options = container.actualOptions.polygon;
|
|
@@ -48,13 +52,14 @@ class PolygonMaskInstance {
|
|
|
48
52
|
else {
|
|
49
53
|
delete path.path2d;
|
|
50
54
|
}
|
|
51
|
-
if (path.path2d
|
|
55
|
+
if (path.path2d ?? !this.raw) {
|
|
52
56
|
continue;
|
|
53
57
|
}
|
|
54
58
|
path.path2d = new Path2D();
|
|
55
|
-
|
|
59
|
+
const firstIndex = 0, firstPoint = this.raw[firstIndex];
|
|
60
|
+
path.path2d.moveTo(firstPoint.x, firstPoint.y);
|
|
56
61
|
this.raw.forEach((pos, i) => {
|
|
57
|
-
if (i >
|
|
62
|
+
if (i > firstIndex) {
|
|
58
63
|
path.path2d?.lineTo(pos.x, pos.y);
|
|
59
64
|
}
|
|
60
65
|
});
|
|
@@ -66,7 +71,7 @@ class PolygonMaskInstance {
|
|
|
66
71
|
if (!options) {
|
|
67
72
|
return;
|
|
68
73
|
}
|
|
69
|
-
const url = svgUrl
|
|
74
|
+
const url = svgUrl ?? options.url, forceDownload = force ?? false;
|
|
70
75
|
if (!url || (this.paths !== undefined && !forceDownload)) {
|
|
71
76
|
return this.raw;
|
|
72
77
|
}
|
|
@@ -92,11 +97,11 @@ class PolygonMaskInstance {
|
|
|
92
97
|
if (!polygonMaskOptions) {
|
|
93
98
|
return;
|
|
94
99
|
}
|
|
95
|
-
if (!this.raw
|
|
100
|
+
if (!this.raw?.length || !this.paths?.length) {
|
|
96
101
|
throw new Error(noPolygonDataLoaded);
|
|
97
102
|
}
|
|
98
103
|
let offset = 0, point;
|
|
99
|
-
const totalLength = this.paths.reduce((tot, path) => tot + path.length,
|
|
104
|
+
const baseAccumulator = 0, totalLength = this.paths.reduce((tot, path) => tot + path.length, baseAccumulator), distance = totalLength / options.particles.number.value;
|
|
100
105
|
for (const path of this.paths) {
|
|
101
106
|
const pathDistance = distance * index - offset;
|
|
102
107
|
if (pathDistance <= path.length) {
|
|
@@ -109,12 +114,12 @@ class PolygonMaskInstance {
|
|
|
109
114
|
}
|
|
110
115
|
const scale = this._scale;
|
|
111
116
|
return {
|
|
112
|
-
x: (point?.x ??
|
|
113
|
-
y: (point?.y ??
|
|
117
|
+
x: (point?.x ?? origin.x) * scale + (this.offset?.x ?? origin.x),
|
|
118
|
+
y: (point?.y ?? origin.y) * scale + (this.offset?.y ?? origin.y),
|
|
114
119
|
};
|
|
115
120
|
};
|
|
116
121
|
this._getPointByIndex = (index) => {
|
|
117
|
-
if (!this.raw
|
|
122
|
+
if (!this.raw?.length) {
|
|
118
123
|
throw new Error(noPolygonDataLoaded);
|
|
119
124
|
}
|
|
120
125
|
const coords = this.raw[index % this.raw.length];
|
|
@@ -124,7 +129,7 @@ class PolygonMaskInstance {
|
|
|
124
129
|
};
|
|
125
130
|
};
|
|
126
131
|
this._getRandomPoint = () => {
|
|
127
|
-
if (!this.raw
|
|
132
|
+
if (!this.raw?.length) {
|
|
128
133
|
throw new Error(noPolygonDataLoaded);
|
|
129
134
|
}
|
|
130
135
|
const coords = (0, engine_1.itemFromArray)(this.raw);
|
|
@@ -138,13 +143,13 @@ class PolygonMaskInstance {
|
|
|
138
143
|
if (!options) {
|
|
139
144
|
return;
|
|
140
145
|
}
|
|
141
|
-
if (!this.raw
|
|
146
|
+
if (!this.raw?.length || !this.paths?.length) {
|
|
142
147
|
throw new Error(noPolygonDataLoaded);
|
|
143
148
|
}
|
|
144
|
-
const path = (0, engine_1.itemFromArray)(this.paths), distance = Math.floor((0, engine_1.getRandom)() * path.length) +
|
|
149
|
+
const path = (0, engine_1.itemFromArray)(this.paths), offset = 1, distance = Math.floor((0, engine_1.getRandom)() * path.length) + offset, point = path.element.getPointAtLength(distance), scale = this._scale;
|
|
145
150
|
return {
|
|
146
|
-
x: point.x * scale + (this.offset?.x
|
|
147
|
-
y: point.y * scale + (this.offset?.y
|
|
151
|
+
x: point.x * scale + (this.offset?.x ?? origin.x),
|
|
152
|
+
y: point.y * scale + (this.offset?.y ?? origin.y),
|
|
148
153
|
};
|
|
149
154
|
};
|
|
150
155
|
this._initRawData = async (force) => {
|
|
@@ -182,7 +187,7 @@ class PolygonMaskInstance {
|
|
|
182
187
|
if (!options) {
|
|
183
188
|
return;
|
|
184
189
|
}
|
|
185
|
-
const parser = new DOMParser(), doc = parser.parseFromString(xml, "image/svg+xml"), svg = doc.getElementsByTagName("svg")[
|
|
190
|
+
const parser = new DOMParser(), doc = parser.parseFromString(xml, "image/svg+xml"), firstIndex = 0, svg = doc.getElementsByTagName("svg")[firstIndex];
|
|
186
191
|
let svgPaths = svg.getElementsByTagName("path");
|
|
187
192
|
if (!svgPaths.length) {
|
|
188
193
|
svgPaths = doc.getElementsByTagName("path");
|
|
@@ -205,8 +210,8 @@ class PolygonMaskInstance {
|
|
|
205
210
|
y: 50,
|
|
206
211
|
}, canvasSize = container.canvas.size;
|
|
207
212
|
this.offset = {
|
|
208
|
-
x: (canvasSize.width * position.x) /
|
|
209
|
-
y: (canvasSize.height * position.y) /
|
|
213
|
+
x: (canvasSize.width * position.x) / engine_1.percentDenominator - this.dimension.width * half,
|
|
214
|
+
y: (canvasSize.height * position.y) / engine_1.percentDenominator - this.dimension.height * half,
|
|
210
215
|
};
|
|
211
216
|
return (0, utils_js_1.parsePaths)(this.paths, scale, this.offset);
|
|
212
217
|
};
|
|
@@ -217,8 +222,8 @@ class PolygonMaskInstance {
|
|
|
217
222
|
}
|
|
218
223
|
if (options.type === "inside" || options.type === "outside") {
|
|
219
224
|
let closest, dx, dy;
|
|
220
|
-
const pos = particle.getPosition(), radius = particle.getRadius();
|
|
221
|
-
for (let i = 0, j = this.raw.length -
|
|
225
|
+
const pos = particle.getPosition(), radius = particle.getRadius(), offset = 1;
|
|
226
|
+
for (let i = 0, j = this.raw.length - offset; i < this.raw.length; j = i++) {
|
|
222
227
|
const pi = this.raw[i], pj = this.raw[j];
|
|
223
228
|
closest = (0, utils_js_1.calcClosestPtOnSegment)(pi, pj, pos);
|
|
224
229
|
const dist = (0, engine_1.getDistances)(pos, closest);
|
|
@@ -229,7 +234,7 @@ class PolygonMaskInstance {
|
|
|
229
234
|
}
|
|
230
235
|
}
|
|
231
236
|
if (closest && dx !== undefined && dy !== undefined && !this._checkInsidePolygon(pos)) {
|
|
232
|
-
const factor = { x: 1, y: 1 }, diameter = radius *
|
|
237
|
+
const factor = { x: 1, y: 1 }, diameter = radius * double, inverse = -1;
|
|
233
238
|
if (pos.x >= closest.x) {
|
|
234
239
|
factor.x = -1;
|
|
235
240
|
}
|
|
@@ -238,15 +243,15 @@ class PolygonMaskInstance {
|
|
|
238
243
|
}
|
|
239
244
|
particle.position.x = closest.x + diameter * factor.x;
|
|
240
245
|
particle.position.y = closest.y + diameter * factor.y;
|
|
241
|
-
particle.velocity.mult(
|
|
246
|
+
particle.velocity.mult(inverse);
|
|
242
247
|
return true;
|
|
243
248
|
}
|
|
244
249
|
}
|
|
245
250
|
else if (options.type === "inline" && particle.initialPosition) {
|
|
246
251
|
const dist = (0, engine_1.getDistance)(particle.initialPosition, particle.getPosition()), { velocity } = particle;
|
|
247
252
|
if (dist > this._moveRadius) {
|
|
248
|
-
velocity.x = velocity.y
|
|
249
|
-
velocity.y = velocity.x
|
|
253
|
+
velocity.x = velocity.y * half - velocity.x;
|
|
254
|
+
velocity.y = velocity.x * half - velocity.y;
|
|
250
255
|
return true;
|
|
251
256
|
}
|
|
252
257
|
}
|
|
@@ -346,8 +351,8 @@ class PolygonMaskInstance {
|
|
|
346
351
|
return this._polygonBounce(particle, delta, direction);
|
|
347
352
|
}
|
|
348
353
|
particlePosition(position) {
|
|
349
|
-
const options = this._container.actualOptions.polygon;
|
|
350
|
-
if (!(options?.enable && (this.raw?.length ??
|
|
354
|
+
const options = this._container.actualOptions.polygon, defaultLength = 0;
|
|
355
|
+
if (!(options?.enable && (this.raw?.length ?? defaultLength) > defaultLength)) {
|
|
351
356
|
return;
|
|
352
357
|
}
|
|
353
358
|
return (0, engine_1.deepExtend)({}, position ? position : this._randomPoint());
|
|
@@ -371,10 +376,13 @@ class PolygonMaskInstance {
|
|
|
371
376
|
if (this.redrawTimeout) {
|
|
372
377
|
clearTimeout(this.redrawTimeout);
|
|
373
378
|
}
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
379
|
+
const timeout = 250;
|
|
380
|
+
this.redrawTimeout = window.setTimeout(() => {
|
|
381
|
+
void (async () => {
|
|
382
|
+
await this._initRawData(true);
|
|
383
|
+
await container.particles.redraw();
|
|
384
|
+
})();
|
|
385
|
+
}, timeout);
|
|
378
386
|
}
|
|
379
387
|
stop() {
|
|
380
388
|
delete this.raw;
|
package/cjs/utils.js
CHANGED
|
@@ -2,13 +2,18 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.segmentBounce = exports.calcClosestPtOnSegment = exports.parsePaths = exports.drawPolygonMaskPath = exports.drawPolygonMask = void 0;
|
|
4
4
|
const engine_1 = require("@tsparticles/engine");
|
|
5
|
+
const squareExp = 2, inSegmentRange = {
|
|
6
|
+
min: 0,
|
|
7
|
+
max: 1,
|
|
8
|
+
}, double = 2;
|
|
5
9
|
function drawPolygonMask(context, rawData, stroke) {
|
|
6
10
|
const color = (0, engine_1.rangeColorToRgb)(stroke.color);
|
|
7
11
|
if (!color) {
|
|
8
12
|
return;
|
|
9
13
|
}
|
|
14
|
+
const firstIndex = 0, firstItem = rawData[firstIndex];
|
|
10
15
|
context.beginPath();
|
|
11
|
-
context.moveTo(
|
|
16
|
+
context.moveTo(firstItem.x, firstItem.y);
|
|
12
17
|
for (const item of rawData) {
|
|
13
18
|
context.lineTo(item.x, item.y);
|
|
14
19
|
}
|
|
@@ -19,7 +24,13 @@ function drawPolygonMask(context, rawData, stroke) {
|
|
|
19
24
|
}
|
|
20
25
|
exports.drawPolygonMask = drawPolygonMask;
|
|
21
26
|
function drawPolygonMaskPath(context, path, stroke, position) {
|
|
22
|
-
|
|
27
|
+
const defaultTransform = {
|
|
28
|
+
a: 1,
|
|
29
|
+
b: 0,
|
|
30
|
+
c: 0,
|
|
31
|
+
d: 1,
|
|
32
|
+
};
|
|
33
|
+
context.setTransform(defaultTransform.a, defaultTransform.b, defaultTransform.c, defaultTransform.d, position.x, position.y);
|
|
23
34
|
const color = (0, engine_1.rangeColorToRgb)(stroke.color);
|
|
24
35
|
if (!color) {
|
|
25
36
|
return;
|
|
@@ -27,13 +38,13 @@ function drawPolygonMaskPath(context, path, stroke, position) {
|
|
|
27
38
|
context.strokeStyle = (0, engine_1.getStyleFromRgb)(color, stroke.opacity);
|
|
28
39
|
context.lineWidth = stroke.width;
|
|
29
40
|
context.stroke(path);
|
|
30
|
-
context.
|
|
41
|
+
context.resetTransform();
|
|
31
42
|
}
|
|
32
43
|
exports.drawPolygonMaskPath = drawPolygonMaskPath;
|
|
33
44
|
function parsePaths(paths, scale, offset) {
|
|
34
|
-
const res = [];
|
|
45
|
+
const res = [], defaultCount = 0;
|
|
35
46
|
for (const path of paths) {
|
|
36
|
-
const segments = path.element.pathSegList, len = segments?.numberOfItems ??
|
|
47
|
+
const segments = path.element.pathSegList, len = segments?.numberOfItems ?? defaultCount, p = {
|
|
37
48
|
x: 0,
|
|
38
49
|
y: 0,
|
|
39
50
|
};
|
|
@@ -90,16 +101,16 @@ function parsePaths(paths, scale, offset) {
|
|
|
90
101
|
}
|
|
91
102
|
exports.parsePaths = parsePaths;
|
|
92
103
|
function calcClosestPtOnSegment(s1, s2, pos) {
|
|
93
|
-
const { dx: dx1, dy: dy1 } = (0, engine_1.getDistances)(pos, s1), { dx: dx2, dy: dy2 } = (0, engine_1.getDistances)(s2, s1), t = (dx1 * dx2 + dy1 * dy2) / (dx2 **
|
|
104
|
+
const { dx: dx1, dy: dy1 } = (0, engine_1.getDistances)(pos, s1), { dx: dx2, dy: dy2 } = (0, engine_1.getDistances)(s2, s1), t = (dx1 * dx2 + dy1 * dy2) / (dx2 ** squareExp + dy2 ** squareExp), res = {
|
|
94
105
|
x: s1.x + dx2 * t,
|
|
95
106
|
y: s1.y + dy2 * t,
|
|
96
|
-
isOnSegment: t >=
|
|
107
|
+
isOnSegment: t >= inSegmentRange.min && t <= inSegmentRange.max,
|
|
97
108
|
};
|
|
98
|
-
if (t <
|
|
109
|
+
if (t < inSegmentRange.min) {
|
|
99
110
|
res.x = s1.x;
|
|
100
111
|
res.y = s1.y;
|
|
101
112
|
}
|
|
102
|
-
else if (t >
|
|
113
|
+
else if (t > inSegmentRange.max) {
|
|
103
114
|
res.x = s2.x;
|
|
104
115
|
res.y = s2.y;
|
|
105
116
|
}
|
|
@@ -107,7 +118,7 @@ function calcClosestPtOnSegment(s1, s2, pos) {
|
|
|
107
118
|
}
|
|
108
119
|
exports.calcClosestPtOnSegment = calcClosestPtOnSegment;
|
|
109
120
|
function segmentBounce(start, stop, velocity) {
|
|
110
|
-
const { dx, dy } = (0, engine_1.getDistances)(start, stop), wallAngle = Math.atan2(dy, dx), wallNormal = engine_1.Vector.create(Math.sin(wallAngle), -Math.cos(wallAngle)), d =
|
|
121
|
+
const { dx, dy } = (0, engine_1.getDistances)(start, stop), wallAngle = Math.atan2(dy, dx), wallNormal = engine_1.Vector.create(Math.sin(wallAngle), -Math.cos(wallAngle)), d = double * (velocity.x * wallNormal.x + velocity.y * wallNormal.y);
|
|
111
122
|
wallNormal.multTo(d);
|
|
112
123
|
velocity.subFrom(wallNormal);
|
|
113
124
|
}
|