1 | "use strict";
|
2 | Object.defineProperty(exports, "__esModule", { value: true });
|
3 | exports.Particle = void 0;
|
4 | const ParticlesOptions_1 = require("../Options/Classes/Particles/ParticlesOptions");
|
5 | const Shape_1 = require("../Options/Classes/Particles/Shape/Shape");
|
6 | const Enums_1 = require("../Enums");
|
7 | const Utils_1 = require("../Utils");
|
8 | const Vector_1 = require("./Particle/Vector");
|
9 | const Vector3d_1 = require("./Particle/Vector3d");
|
10 | const fixOutMode = (data) => {
|
11 | if ((0, Utils_1.isInArray)(data.outMode, data.checkModes) || (0, Utils_1.isInArray)(data.outMode, data.checkModes)) {
|
12 | if (data.coord > data.maxCoord - data.radius * 2) {
|
13 | data.setCb(-data.radius);
|
14 | }
|
15 | else if (data.coord < data.radius * 2) {
|
16 | data.setCb(data.radius);
|
17 | }
|
18 | }
|
19 | };
|
20 | class Particle {
|
21 | constructor(id, container, position, overrideOptions, group) {
|
22 | var _a, _b, _c, _d, _e, _f, _g, _h, _j;
|
23 | this.id = id;
|
24 | this.container = container;
|
25 | this.group = group;
|
26 | this.fill = true;
|
27 | this.close = true;
|
28 | this.lastPathTime = 0;
|
29 | this.destroyed = false;
|
30 | this.unbreakable = false;
|
31 | this.splitCount = 0;
|
32 | this.misplaced = false;
|
33 | this.retina = {
|
34 | maxDistance: {},
|
35 | };
|
36 | const pxRatio = container.retina.pixelRatio;
|
37 | const mainOptions = container.actualOptions;
|
38 | const particlesOptions = new ParticlesOptions_1.ParticlesOptions();
|
39 | particlesOptions.load(mainOptions.particles);
|
40 | const shapeType = particlesOptions.shape.type;
|
41 | const reduceDuplicates = particlesOptions.reduceDuplicates;
|
42 | this.shape = shapeType instanceof Array ? (0, Utils_1.itemFromArray)(shapeType, this.id, reduceDuplicates) : shapeType;
|
43 | if (overrideOptions === null || overrideOptions === void 0 ? void 0 : overrideOptions.shape) {
|
44 | if (overrideOptions.shape.type) {
|
45 | const overrideShapeType = overrideOptions.shape.type;
|
46 | this.shape =
|
47 | overrideShapeType instanceof Array
|
48 | ? (0, Utils_1.itemFromArray)(overrideShapeType, this.id, reduceDuplicates)
|
49 | : overrideShapeType;
|
50 | }
|
51 | const shapeOptions = new Shape_1.Shape();
|
52 | shapeOptions.load(overrideOptions.shape);
|
53 | if (this.shape) {
|
54 | this.shapeData = this.loadShapeData(shapeOptions, reduceDuplicates);
|
55 | }
|
56 | }
|
57 | else {
|
58 | this.shapeData = this.loadShapeData(particlesOptions.shape, reduceDuplicates);
|
59 | }
|
60 | if (overrideOptions !== undefined) {
|
61 | particlesOptions.load(overrideOptions);
|
62 | }
|
63 | if (((_a = this.shapeData) === null || _a === void 0 ? void 0 : _a.particles) !== undefined) {
|
64 | particlesOptions.load((_b = this.shapeData) === null || _b === void 0 ? void 0 : _b.particles);
|
65 | }
|
66 | this.fill = (_d = (_c = this.shapeData) === null || _c === void 0 ? void 0 : _c.fill) !== null && _d !== void 0 ? _d : this.fill;
|
67 | this.close = (_f = (_e = this.shapeData) === null || _e === void 0 ? void 0 : _e.close) !== null && _f !== void 0 ? _f : this.close;
|
68 | this.options = particlesOptions;
|
69 | this.pathDelay = (0, Utils_1.getValue)(this.options.move.path.delay) * 1000;
|
70 | const zIndexValue = (0, Utils_1.getRangeValue)(this.options.zIndex.value);
|
71 | container.retina.initParticle(this);
|
72 | const sizeOptions = this.options.size, sizeRange = sizeOptions.value;
|
73 | this.size = {
|
74 | enable: sizeOptions.animation.enable,
|
75 | value: (0, Utils_1.getValue)(sizeOptions) * container.retina.pixelRatio,
|
76 | max: (0, Utils_1.getRangeMax)(sizeRange) * pxRatio,
|
77 | min: (0, Utils_1.getRangeMin)(sizeRange) * pxRatio,
|
78 | loops: 0,
|
79 | maxLoops: sizeOptions.animation.count,
|
80 | };
|
81 | const sizeAnimation = sizeOptions.animation;
|
82 | if (sizeAnimation.enable) {
|
83 | this.size.status = Enums_1.AnimationStatus.increasing;
|
84 | switch (sizeAnimation.startValue) {
|
85 | case Enums_1.StartValueType.min:
|
86 | this.size.value = this.size.min;
|
87 | this.size.status = Enums_1.AnimationStatus.increasing;
|
88 | break;
|
89 | case Enums_1.StartValueType.random:
|
90 | this.size.value = (0, Utils_1.randomInRange)(this.size) * pxRatio;
|
91 | this.size.status = Math.random() >= 0.5 ? Enums_1.AnimationStatus.increasing : Enums_1.AnimationStatus.decreasing;
|
92 | break;
|
93 | case Enums_1.StartValueType.max:
|
94 | default:
|
95 | this.size.value = this.size.max;
|
96 | this.size.status = Enums_1.AnimationStatus.decreasing;
|
97 | break;
|
98 | }
|
99 | this.size.velocity =
|
100 | (((_g = this.retina.sizeAnimationSpeed) !== null && _g !== void 0 ? _g : container.retina.sizeAnimationSpeed) / 100) *
|
101 | container.retina.reduceFactor;
|
102 | if (!sizeAnimation.sync) {
|
103 | this.size.velocity *= Math.random();
|
104 | }
|
105 | }
|
106 | this.direction = (0, Utils_1.getParticleDirectionAngle)(this.options.move.direction);
|
107 | this.bubble = {
|
108 | inRange: false,
|
109 | };
|
110 | this.initialVelocity = this.calculateVelocity();
|
111 | this.velocity = this.initialVelocity.copy();
|
112 | this.moveDecay = 1 - (0, Utils_1.getRangeValue)(this.options.move.decay);
|
113 | this.position = this.calcPosition(container, position, (0, Utils_1.clamp)(zIndexValue, 0, container.zLayers));
|
114 | this.initialPosition = this.position.copy();
|
115 | this.offset = Vector_1.Vector.origin;
|
116 | const particles = container.particles;
|
117 | particles.needsSort = particles.needsSort || particles.lastZIndex < this.position.z;
|
118 | particles.lastZIndex = this.position.z;
|
119 | this.zIndexFactor = this.position.z / container.zLayers;
|
120 | this.sides = 24;
|
121 | let drawer = container.drawers.get(this.shape);
|
122 | if (!drawer) {
|
123 | drawer = Utils_1.Plugins.getShapeDrawer(this.shape);
|
124 | if (drawer) {
|
125 | container.drawers.set(this.shape, drawer);
|
126 | }
|
127 | }
|
128 | if (drawer === null || drawer === void 0 ? void 0 : drawer.loadShape) {
|
129 | drawer === null || drawer === void 0 ? void 0 : drawer.loadShape(this);
|
130 | }
|
131 | const sideCountFunc = drawer === null || drawer === void 0 ? void 0 : drawer.getSidesCount;
|
132 | if (sideCountFunc) {
|
133 | this.sides = sideCountFunc(this);
|
134 | }
|
135 | this.life = this.loadLife();
|
136 | this.spawning = this.life.delay > 0;
|
137 | if (this.options.move.spin.enable) {
|
138 | const spinPos = (_h = this.options.move.spin.position) !== null && _h !== void 0 ? _h : { x: 50, y: 50 };
|
139 | const spinCenter = {
|
140 | x: (spinPos.x / 100) * container.canvas.size.width,
|
141 | y: (spinPos.y / 100) * container.canvas.size.height,
|
142 | };
|
143 | const pos = this.getPosition();
|
144 | const distance = (0, Utils_1.getDistance)(pos, spinCenter);
|
145 | this.spin = {
|
146 | center: spinCenter,
|
147 | direction: this.velocity.x >= 0 ? Enums_1.RotateDirection.clockwise : Enums_1.RotateDirection.counterClockwise,
|
148 | angle: this.velocity.angle,
|
149 | radius: distance,
|
150 | acceleration: (_j = this.retina.spinAcceleration) !== null && _j !== void 0 ? _j : (0, Utils_1.getRangeValue)(this.options.move.spin.acceleration),
|
151 | };
|
152 | }
|
153 | this.shadowColor = (0, Utils_1.colorToRgb)(this.options.shadow.color);
|
154 | for (const updater of container.particles.updaters) {
|
155 | if (updater.init) {
|
156 | updater.init(this);
|
157 | }
|
158 | }
|
159 | if (drawer && drawer.particleInit) {
|
160 | drawer.particleInit(container, this);
|
161 | }
|
162 | for (const [, plugin] of container.plugins) {
|
163 | if (plugin.particleCreated) {
|
164 | plugin.particleCreated(this);
|
165 | }
|
166 | }
|
167 | }
|
168 | isVisible() {
|
169 | return !this.destroyed && !this.spawning && this.isInsideCanvas();
|
170 | }
|
171 | isInsideCanvas() {
|
172 | const radius = this.getRadius();
|
173 | const canvasSize = this.container.canvas.size;
|
174 | return (this.position.x >= -radius &&
|
175 | this.position.y >= -radius &&
|
176 | this.position.y <= canvasSize.height + radius &&
|
177 | this.position.x <= canvasSize.width + radius);
|
178 | }
|
179 | draw(delta) {
|
180 | const container = this.container;
|
181 | for (const [, plugin] of container.plugins) {
|
182 | container.canvas.drawParticlePlugin(plugin, this, delta);
|
183 | }
|
184 | container.canvas.drawParticle(this, delta);
|
185 | }
|
186 | getPosition() {
|
187 | return {
|
188 | x: this.position.x + this.offset.x,
|
189 | y: this.position.y + this.offset.y,
|
190 | z: this.position.z,
|
191 | };
|
192 | }
|
193 | getRadius() {
|
194 | var _a;
|
195 | return (_a = this.bubble.radius) !== null && _a !== void 0 ? _a : this.size.value;
|
196 | }
|
197 | getMass() {
|
198 | return (this.getRadius() ** 2 * Math.PI) / 2;
|
199 | }
|
200 | getFillColor() {
|
201 | var _a, _b, _c;
|
202 | const color = (_a = this.bubble.color) !== null && _a !== void 0 ? _a : (0, Utils_1.getHslFromAnimation)(this.color);
|
203 | if (color && this.roll && (this.backColor || this.roll.alter)) {
|
204 | const rolled = Math.floor(((_c = (_b = this.roll) === null || _b === void 0 ? void 0 : _b.angle) !== null && _c !== void 0 ? _c : 0) / (Math.PI / 2)) % 2;
|
205 | if (rolled) {
|
206 | if (this.backColor) {
|
207 | return this.backColor;
|
208 | }
|
209 | if (this.roll.alter) {
|
210 | return (0, Utils_1.alterHsl)(color, this.roll.alter.type, this.roll.alter.value);
|
211 | }
|
212 | }
|
213 | }
|
214 | return color;
|
215 | }
|
216 | getStrokeColor() {
|
217 | var _a, _b;
|
218 | return (_b = (_a = this.bubble.color) !== null && _a !== void 0 ? _a : (0, Utils_1.getHslFromAnimation)(this.strokeColor)) !== null && _b !== void 0 ? _b : this.getFillColor();
|
219 | }
|
220 | destroy(override) {
|
221 | this.destroyed = true;
|
222 | this.bubble.inRange = false;
|
223 | if (this.unbreakable) {
|
224 | return;
|
225 | }
|
226 | this.destroyed = true;
|
227 | this.bubble.inRange = false;
|
228 | for (const [, plugin] of this.container.plugins) {
|
229 | if (plugin.particleDestroyed) {
|
230 | plugin.particleDestroyed(this, override);
|
231 | }
|
232 | }
|
233 | if (override) {
|
234 | return;
|
235 | }
|
236 | const destroyOptions = this.options.destroy;
|
237 | if (destroyOptions.mode === Enums_1.DestroyMode.split) {
|
238 | this.split();
|
239 | }
|
240 | }
|
241 | reset() {
|
242 | if (this.opacity) {
|
243 | this.opacity.loops = 0;
|
244 | }
|
245 | this.size.loops = 0;
|
246 | }
|
247 | split() {
|
248 | const splitOptions = this.options.destroy.split;
|
249 | if (splitOptions.count >= 0 && this.splitCount++ > splitOptions.count) {
|
250 | return;
|
251 | }
|
252 | const rate = (0, Utils_1.getRangeValue)(splitOptions.rate.value);
|
253 | for (let i = 0; i < rate; i++) {
|
254 | this.container.particles.addSplitParticle(this);
|
255 | }
|
256 | }
|
257 | calcPosition(container, position, zIndex, tryCount = 0) {
|
258 | var _a, _b, _c, _d, _e, _f;
|
259 | for (const [, plugin] of container.plugins) {
|
260 | const pluginPos = plugin.particlePosition !== undefined ? plugin.particlePosition(position, this) : undefined;
|
261 | if (pluginPos !== undefined) {
|
262 | return Vector3d_1.Vector3d.create(pluginPos.x, pluginPos.y, zIndex);
|
263 | }
|
264 | }
|
265 | const canvasSize = container.canvas.size;
|
266 | const pos = Vector3d_1.Vector3d.create((_a = position === null || position === void 0 ? void 0 : position.x) !== null && _a !== void 0 ? _a : Math.random() * canvasSize.width, (_b = position === null || position === void 0 ? void 0 : position.y) !== null && _b !== void 0 ? _b : Math.random() * canvasSize.height, zIndex);
|
267 | const radius = this.getRadius();
|
268 | const outModes = this.options.move.outModes, fixHorizontal = (outMode) => {
|
269 | fixOutMode({
|
270 | outMode,
|
271 | checkModes: [Enums_1.OutMode.bounce, Enums_1.OutMode.bounceHorizontal],
|
272 | coord: pos.x,
|
273 | maxCoord: container.canvas.size.width,
|
274 | setCb: (value) => (pos.x += value),
|
275 | radius,
|
276 | });
|
277 | }, fixVertical = (outMode) => {
|
278 | fixOutMode({
|
279 | outMode,
|
280 | checkModes: [Enums_1.OutMode.bounce, Enums_1.OutMode.bounceVertical],
|
281 | coord: pos.y,
|
282 | maxCoord: container.canvas.size.height,
|
283 | setCb: (value) => (pos.y += value),
|
284 | radius,
|
285 | });
|
286 | };
|
287 | fixHorizontal((_c = outModes.left) !== null && _c !== void 0 ? _c : outModes.default);
|
288 | fixHorizontal((_d = outModes.right) !== null && _d !== void 0 ? _d : outModes.default);
|
289 | fixVertical((_e = outModes.top) !== null && _e !== void 0 ? _e : outModes.default);
|
290 | fixVertical((_f = outModes.bottom) !== null && _f !== void 0 ? _f : outModes.default);
|
291 | if (this.checkOverlap(pos, tryCount)) {
|
292 | return this.calcPosition(container, undefined, zIndex, tryCount + 1);
|
293 | }
|
294 | return pos;
|
295 | }
|
296 | checkOverlap(pos, tryCount = 0) {
|
297 | const collisionsOptions = this.options.collisions;
|
298 | const radius = this.getRadius();
|
299 | if (!collisionsOptions.enable) {
|
300 | return false;
|
301 | }
|
302 | const overlapOptions = collisionsOptions.overlap;
|
303 | if (overlapOptions.enable) {
|
304 | return false;
|
305 | }
|
306 | const retries = overlapOptions.retries;
|
307 | if (retries >= 0 && tryCount > retries) {
|
308 | throw new Error("Particle is overlapping and can't be placed");
|
309 | }
|
310 | let overlaps = false;
|
311 | for (const particle of this.container.particles.array) {
|
312 | if ((0, Utils_1.getDistance)(pos, particle.position) < radius + particle.getRadius()) {
|
313 | overlaps = true;
|
314 | break;
|
315 | }
|
316 | }
|
317 | return overlaps;
|
318 | }
|
319 | calculateVelocity() {
|
320 | const baseVelocity = (0, Utils_1.getParticleBaseVelocity)(this.direction);
|
321 | const res = baseVelocity.copy();
|
322 | const moveOptions = this.options.move;
|
323 | const rad = (Math.PI / 180) * moveOptions.angle.value;
|
324 | const radOffset = (Math.PI / 180) * moveOptions.angle.offset;
|
325 | const range = {
|
326 | left: radOffset - rad / 2,
|
327 | right: radOffset + rad / 2,
|
328 | };
|
329 | if (!moveOptions.straight) {
|
330 | res.angle += (0, Utils_1.randomInRange)((0, Utils_1.setRangeValue)(range.left, range.right));
|
331 | }
|
332 | if (moveOptions.random && typeof moveOptions.speed === "number") {
|
333 | res.length *= Math.random();
|
334 | }
|
335 | return res;
|
336 | }
|
337 | loadShapeData(shapeOptions, reduceDuplicates) {
|
338 | const shapeData = shapeOptions.options[this.shape];
|
339 | if (shapeData) {
|
340 | return (0, Utils_1.deepExtend)({}, shapeData instanceof Array ? (0, Utils_1.itemFromArray)(shapeData, this.id, reduceDuplicates) : shapeData);
|
341 | }
|
342 | }
|
343 | loadLife() {
|
344 | const container = this.container;
|
345 | const particlesOptions = this.options;
|
346 | const lifeOptions = particlesOptions.life;
|
347 | const life = {
|
348 | delay: container.retina.reduceFactor
|
349 | ? (((0, Utils_1.getRangeValue)(lifeOptions.delay.value) * (lifeOptions.delay.sync ? 1 : Math.random())) /
|
350 | container.retina.reduceFactor) *
|
351 | 1000
|
352 | : 0,
|
353 | delayTime: 0,
|
354 | duration: container.retina.reduceFactor
|
355 | ? (((0, Utils_1.getRangeValue)(lifeOptions.duration.value) * (lifeOptions.duration.sync ? 1 : Math.random())) /
|
356 | container.retina.reduceFactor) *
|
357 | 1000
|
358 | : 0,
|
359 | time: 0,
|
360 | count: particlesOptions.life.count,
|
361 | };
|
362 | if (life.duration <= 0) {
|
363 | life.duration = -1;
|
364 | }
|
365 | if (life.count <= 0) {
|
366 | life.count = -1;
|
367 | }
|
368 | return life;
|
369 | }
|
370 | }
|
371 | exports.Particle = Particle;
|