@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.
@@ -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 - 1; i < this.raw.length; j = i++) {
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
- return options.type === "inside"
23
- ? inside
24
- : options.type === "outside"
25
- ? !inside
26
- : false;
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 || !this.raw) {
52
+ if (path.path2d ?? !this.raw) {
49
53
  continue;
50
54
  }
51
55
  path.path2d = new Path2D();
52
- path.path2d.moveTo(this.raw[0].x, this.raw[0].y);
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 > 0) {
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 || options.url, forceDownload = force ?? false;
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 || !this.raw.length || !this.paths?.length) {
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, 0), distance = totalLength / options.particles.number.value;
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 ?? 0) * scale + (this.offset?.x ?? 0),
110
- y: (point?.y ?? 0) * scale + (this.offset?.y ?? 0),
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 || !this.raw.length) {
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 || !this.raw.length) {
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 || !this.raw.length || !this.paths?.length) {
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) + 1, point = path.element.getPointAtLength(distance), scale = this._scale;
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 || 0),
144
- y: point.y * scale + (this.offset?.y || 0),
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")[0];
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) / 100 - this.dimension.width / 2,
206
- y: (canvasSize.height * position.y) / 100 - this.dimension.height / 2,
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 - 1; i < this.raw.length; j = i++) {
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 * 2;
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(-1);
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 / 2 - velocity.x;
246
- velocity.y = velocity.x / 2 - velocity.y;
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 ?? 0) > 0)) {
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
- this.redrawTimeout = window.setTimeout(async () => {
372
- await this._initRawData(true);
373
- await container.particles.redraw();
374
- }, 250);
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(rawData[0].x, rawData[0].y);
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
- context.setTransform(1, 0, 0, 1, position.x, position.y);
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.setTransform(1, 0, 0, 1, 0, 0);
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 ?? 0, p = {
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 ** 2 + dy2 ** 2), res = {
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 >= 0 && t <= 1,
101
+ isOnSegment: t >= inSegmentRange.min && t <= inSegmentRange.max,
91
102
  };
92
- if (t < 0) {
103
+ if (t < inSegmentRange.min) {
93
104
  res.x = s1.x;
94
105
  res.y = s1.y;
95
106
  }
96
- else if (t > 1) {
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 = 2 * (velocity.x * wallNormal.x + velocity.y * wallNormal.y);
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 - 1; i < this.raw.length; j = i++) {
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
- return options.type === "inside"
26
- ? inside
27
- : options.type === "outside"
28
- ? !inside
29
- : false;
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 || !this.raw) {
55
+ if (path.path2d ?? !this.raw) {
52
56
  continue;
53
57
  }
54
58
  path.path2d = new Path2D();
55
- path.path2d.moveTo(this.raw[0].x, this.raw[0].y);
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 > 0) {
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 || options.url, forceDownload = force ?? false;
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 || !this.raw.length || !this.paths?.length) {
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, 0), distance = totalLength / options.particles.number.value;
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 ?? 0) * scale + (this.offset?.x ?? 0),
113
- y: (point?.y ?? 0) * scale + (this.offset?.y ?? 0),
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 || !this.raw.length) {
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 || !this.raw.length) {
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 || !this.raw.length || !this.paths?.length) {
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) + 1, point = path.element.getPointAtLength(distance), scale = this._scale;
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 || 0),
147
- y: point.y * scale + (this.offset?.y || 0),
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")[0];
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) / 100 - this.dimension.width / 2,
209
- y: (canvasSize.height * position.y) / 100 - this.dimension.height / 2,
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 - 1; i < this.raw.length; j = i++) {
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 * 2;
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(-1);
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 / 2 - velocity.x;
249
- velocity.y = velocity.x / 2 - velocity.y;
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 ?? 0) > 0)) {
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
- this.redrawTimeout = window.setTimeout(async () => {
375
- await this._initRawData(true);
376
- await container.particles.redraw();
377
- }, 250);
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(rawData[0].x, rawData[0].y);
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
- context.setTransform(1, 0, 0, 1, position.x, position.y);
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.setTransform(1, 0, 0, 1, 0, 0);
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 ?? 0, p = {
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 ** 2 + dy2 ** 2), res = {
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 >= 0 && t <= 1,
107
+ isOnSegment: t >= inSegmentRange.min && t <= inSegmentRange.max,
97
108
  };
98
- if (t < 0) {
109
+ if (t < inSegmentRange.min) {
99
110
  res.x = s1.x;
100
111
  res.y = s1.y;
101
112
  }
102
- else if (t > 1) {
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 = 2 * (velocity.x * wallNormal.x + velocity.y * wallNormal.y);
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
  }