@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.
@@ -12,7 +12,10 @@
12
12
  exports.PolygonMaskInstance = void 0;
13
13
  const engine_1 = require("@tsparticles/engine");
14
14
  const utils_js_1 = require("./utils.js");
15
- const noPolygonDataLoaded = `${engine_1.errorPrefix} No polygon data loaded.`, noPolygonFound = `${engine_1.errorPrefix} No polygon found, you need to specify SVG url in config.`;
15
+ 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 = {
16
+ x: 0,
17
+ y: 0,
18
+ }, half = 0.5, double = 2;
16
19
  class PolygonMaskInstance {
17
20
  constructor(container, engine) {
18
21
  this._checkInsidePolygon = (position) => {
@@ -23,19 +26,20 @@
23
26
  if (!this.raw) {
24
27
  throw new Error(noPolygonFound);
25
28
  }
26
- const canvasSize = container.canvas.size, x = position?.x ?? (0, engine_1.getRandom)() * canvasSize.width, y = position?.y ?? (0, engine_1.getRandom)() * canvasSize.height;
29
+ 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;
27
30
  let inside = false;
28
- for (let i = 0, j = this.raw.length - 1; i < this.raw.length; j = i++) {
31
+ for (let i = 0, j = this.raw.length - indexOffset; i < this.raw.length; j = i++) {
29
32
  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;
30
33
  if (intersect) {
31
34
  inside = !inside;
32
35
  }
33
36
  }
34
- return options.type === "inside"
35
- ? inside
36
- : options.type === "outside"
37
- ? !inside
38
- : false;
37
+ if (options.type === "inside") {
38
+ return inside;
39
+ }
40
+ else {
41
+ return options.type === "outside" ? !inside : false;
42
+ }
39
43
  };
40
44
  this._createPath2D = () => {
41
45
  const container = this._container, options = container.actualOptions.polygon;
@@ -57,13 +61,14 @@
57
61
  else {
58
62
  delete path.path2d;
59
63
  }
60
- if (path.path2d || !this.raw) {
64
+ if (path.path2d ?? !this.raw) {
61
65
  continue;
62
66
  }
63
67
  path.path2d = new Path2D();
64
- path.path2d.moveTo(this.raw[0].x, this.raw[0].y);
68
+ const firstIndex = 0, firstPoint = this.raw[firstIndex];
69
+ path.path2d.moveTo(firstPoint.x, firstPoint.y);
65
70
  this.raw.forEach((pos, i) => {
66
- if (i > 0) {
71
+ if (i > firstIndex) {
67
72
  path.path2d?.lineTo(pos.x, pos.y);
68
73
  }
69
74
  });
@@ -75,7 +80,7 @@
75
80
  if (!options) {
76
81
  return;
77
82
  }
78
- const url = svgUrl || options.url, forceDownload = force ?? false;
83
+ const url = svgUrl ?? options.url, forceDownload = force ?? false;
79
84
  if (!url || (this.paths !== undefined && !forceDownload)) {
80
85
  return this.raw;
81
86
  }
@@ -101,11 +106,11 @@
101
106
  if (!polygonMaskOptions) {
102
107
  return;
103
108
  }
104
- if (!this.raw || !this.raw.length || !this.paths?.length) {
109
+ if (!this.raw?.length || !this.paths?.length) {
105
110
  throw new Error(noPolygonDataLoaded);
106
111
  }
107
112
  let offset = 0, point;
108
- const totalLength = this.paths.reduce((tot, path) => tot + path.length, 0), distance = totalLength / options.particles.number.value;
113
+ const baseAccumulator = 0, totalLength = this.paths.reduce((tot, path) => tot + path.length, baseAccumulator), distance = totalLength / options.particles.number.value;
109
114
  for (const path of this.paths) {
110
115
  const pathDistance = distance * index - offset;
111
116
  if (pathDistance <= path.length) {
@@ -118,12 +123,12 @@
118
123
  }
119
124
  const scale = this._scale;
120
125
  return {
121
- x: (point?.x ?? 0) * scale + (this.offset?.x ?? 0),
122
- y: (point?.y ?? 0) * scale + (this.offset?.y ?? 0),
126
+ x: (point?.x ?? origin.x) * scale + (this.offset?.x ?? origin.x),
127
+ y: (point?.y ?? origin.y) * scale + (this.offset?.y ?? origin.y),
123
128
  };
124
129
  };
125
130
  this._getPointByIndex = (index) => {
126
- if (!this.raw || !this.raw.length) {
131
+ if (!this.raw?.length) {
127
132
  throw new Error(noPolygonDataLoaded);
128
133
  }
129
134
  const coords = this.raw[index % this.raw.length];
@@ -133,7 +138,7 @@
133
138
  };
134
139
  };
135
140
  this._getRandomPoint = () => {
136
- if (!this.raw || !this.raw.length) {
141
+ if (!this.raw?.length) {
137
142
  throw new Error(noPolygonDataLoaded);
138
143
  }
139
144
  const coords = (0, engine_1.itemFromArray)(this.raw);
@@ -147,13 +152,13 @@
147
152
  if (!options) {
148
153
  return;
149
154
  }
150
- if (!this.raw || !this.raw.length || !this.paths?.length) {
155
+ if (!this.raw?.length || !this.paths?.length) {
151
156
  throw new Error(noPolygonDataLoaded);
152
157
  }
153
- 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;
158
+ 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;
154
159
  return {
155
- x: point.x * scale + (this.offset?.x || 0),
156
- y: point.y * scale + (this.offset?.y || 0),
160
+ x: point.x * scale + (this.offset?.x ?? origin.x),
161
+ y: point.y * scale + (this.offset?.y ?? origin.y),
157
162
  };
158
163
  };
159
164
  this._initRawData = async (force) => {
@@ -191,7 +196,7 @@
191
196
  if (!options) {
192
197
  return;
193
198
  }
194
- const parser = new DOMParser(), doc = parser.parseFromString(xml, "image/svg+xml"), svg = doc.getElementsByTagName("svg")[0];
199
+ const parser = new DOMParser(), doc = parser.parseFromString(xml, "image/svg+xml"), firstIndex = 0, svg = doc.getElementsByTagName("svg")[firstIndex];
195
200
  let svgPaths = svg.getElementsByTagName("path");
196
201
  if (!svgPaths.length) {
197
202
  svgPaths = doc.getElementsByTagName("path");
@@ -214,8 +219,8 @@
214
219
  y: 50,
215
220
  }, canvasSize = container.canvas.size;
216
221
  this.offset = {
217
- x: (canvasSize.width * position.x) / 100 - this.dimension.width / 2,
218
- y: (canvasSize.height * position.y) / 100 - this.dimension.height / 2,
222
+ x: (canvasSize.width * position.x) / engine_1.percentDenominator - this.dimension.width * half,
223
+ y: (canvasSize.height * position.y) / engine_1.percentDenominator - this.dimension.height * half,
219
224
  };
220
225
  return (0, utils_js_1.parsePaths)(this.paths, scale, this.offset);
221
226
  };
@@ -226,8 +231,8 @@
226
231
  }
227
232
  if (options.type === "inside" || options.type === "outside") {
228
233
  let closest, dx, dy;
229
- const pos = particle.getPosition(), radius = particle.getRadius();
230
- for (let i = 0, j = this.raw.length - 1; i < this.raw.length; j = i++) {
234
+ const pos = particle.getPosition(), radius = particle.getRadius(), offset = 1;
235
+ for (let i = 0, j = this.raw.length - offset; i < this.raw.length; j = i++) {
231
236
  const pi = this.raw[i], pj = this.raw[j];
232
237
  closest = (0, utils_js_1.calcClosestPtOnSegment)(pi, pj, pos);
233
238
  const dist = (0, engine_1.getDistances)(pos, closest);
@@ -238,7 +243,7 @@
238
243
  }
239
244
  }
240
245
  if (closest && dx !== undefined && dy !== undefined && !this._checkInsidePolygon(pos)) {
241
- const factor = { x: 1, y: 1 }, diameter = radius * 2;
246
+ const factor = { x: 1, y: 1 }, diameter = radius * double, inverse = -1;
242
247
  if (pos.x >= closest.x) {
243
248
  factor.x = -1;
244
249
  }
@@ -247,15 +252,15 @@
247
252
  }
248
253
  particle.position.x = closest.x + diameter * factor.x;
249
254
  particle.position.y = closest.y + diameter * factor.y;
250
- particle.velocity.mult(-1);
255
+ particle.velocity.mult(inverse);
251
256
  return true;
252
257
  }
253
258
  }
254
259
  else if (options.type === "inline" && particle.initialPosition) {
255
260
  const dist = (0, engine_1.getDistance)(particle.initialPosition, particle.getPosition()), { velocity } = particle;
256
261
  if (dist > this._moveRadius) {
257
- velocity.x = velocity.y / 2 - velocity.x;
258
- velocity.y = velocity.x / 2 - velocity.y;
262
+ velocity.x = velocity.y * half - velocity.x;
263
+ velocity.y = velocity.x * half - velocity.y;
259
264
  return true;
260
265
  }
261
266
  }
@@ -355,8 +360,8 @@
355
360
  return this._polygonBounce(particle, delta, direction);
356
361
  }
357
362
  particlePosition(position) {
358
- const options = this._container.actualOptions.polygon;
359
- if (!(options?.enable && (this.raw?.length ?? 0) > 0)) {
363
+ const options = this._container.actualOptions.polygon, defaultLength = 0;
364
+ if (!(options?.enable && (this.raw?.length ?? defaultLength) > defaultLength)) {
360
365
  return;
361
366
  }
362
367
  return (0, engine_1.deepExtend)({}, position ? position : this._randomPoint());
@@ -380,10 +385,13 @@
380
385
  if (this.redrawTimeout) {
381
386
  clearTimeout(this.redrawTimeout);
382
387
  }
383
- this.redrawTimeout = window.setTimeout(async () => {
384
- await this._initRawData(true);
385
- await container.particles.redraw();
386
- }, 250);
388
+ const timeout = 250;
389
+ this.redrawTimeout = window.setTimeout(() => {
390
+ void (async () => {
391
+ await this._initRawData(true);
392
+ await container.particles.redraw();
393
+ })();
394
+ }, timeout);
387
395
  }
388
396
  stop() {
389
397
  delete this.raw;
package/umd/utils.js CHANGED
@@ -11,13 +11,18 @@
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
12
  exports.segmentBounce = exports.calcClosestPtOnSegment = exports.parsePaths = exports.drawPolygonMaskPath = exports.drawPolygonMask = void 0;
13
13
  const engine_1 = require("@tsparticles/engine");
14
+ const squareExp = 2, inSegmentRange = {
15
+ min: 0,
16
+ max: 1,
17
+ }, double = 2;
14
18
  function drawPolygonMask(context, rawData, stroke) {
15
19
  const color = (0, engine_1.rangeColorToRgb)(stroke.color);
16
20
  if (!color) {
17
21
  return;
18
22
  }
23
+ const firstIndex = 0, firstItem = rawData[firstIndex];
19
24
  context.beginPath();
20
- context.moveTo(rawData[0].x, rawData[0].y);
25
+ context.moveTo(firstItem.x, firstItem.y);
21
26
  for (const item of rawData) {
22
27
  context.lineTo(item.x, item.y);
23
28
  }
@@ -28,7 +33,13 @@
28
33
  }
29
34
  exports.drawPolygonMask = drawPolygonMask;
30
35
  function drawPolygonMaskPath(context, path, stroke, position) {
31
- context.setTransform(1, 0, 0, 1, position.x, position.y);
36
+ const defaultTransform = {
37
+ a: 1,
38
+ b: 0,
39
+ c: 0,
40
+ d: 1,
41
+ };
42
+ context.setTransform(defaultTransform.a, defaultTransform.b, defaultTransform.c, defaultTransform.d, position.x, position.y);
32
43
  const color = (0, engine_1.rangeColorToRgb)(stroke.color);
33
44
  if (!color) {
34
45
  return;
@@ -36,13 +47,13 @@
36
47
  context.strokeStyle = (0, engine_1.getStyleFromRgb)(color, stroke.opacity);
37
48
  context.lineWidth = stroke.width;
38
49
  context.stroke(path);
39
- context.setTransform(1, 0, 0, 1, 0, 0);
50
+ context.resetTransform();
40
51
  }
41
52
  exports.drawPolygonMaskPath = drawPolygonMaskPath;
42
53
  function parsePaths(paths, scale, offset) {
43
- const res = [];
54
+ const res = [], defaultCount = 0;
44
55
  for (const path of paths) {
45
- const segments = path.element.pathSegList, len = segments?.numberOfItems ?? 0, p = {
56
+ const segments = path.element.pathSegList, len = segments?.numberOfItems ?? defaultCount, p = {
46
57
  x: 0,
47
58
  y: 0,
48
59
  };
@@ -99,16 +110,16 @@
99
110
  }
100
111
  exports.parsePaths = parsePaths;
101
112
  function calcClosestPtOnSegment(s1, s2, pos) {
102
- 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 = {
113
+ 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 = {
103
114
  x: s1.x + dx2 * t,
104
115
  y: s1.y + dy2 * t,
105
- isOnSegment: t >= 0 && t <= 1,
116
+ isOnSegment: t >= inSegmentRange.min && t <= inSegmentRange.max,
106
117
  };
107
- if (t < 0) {
118
+ if (t < inSegmentRange.min) {
108
119
  res.x = s1.x;
109
120
  res.y = s1.y;
110
121
  }
111
- else if (t > 1) {
122
+ else if (t > inSegmentRange.max) {
112
123
  res.x = s2.x;
113
124
  res.y = s2.y;
114
125
  }
@@ -116,7 +127,7 @@
116
127
  }
117
128
  exports.calcClosestPtOnSegment = calcClosestPtOnSegment;
118
129
  function segmentBounce(start, stop, velocity) {
119
- 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);
130
+ 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);
120
131
  wallNormal.multTo(d);
121
132
  velocity.subFrom(wallNormal);
122
133
  }