UNPKG

16.4 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3exports.Particle = void 0;
4const ParticlesOptions_1 = require("../Options/Classes/Particles/ParticlesOptions");
5const Shape_1 = require("../Options/Classes/Particles/Shape/Shape");
6const Enums_1 = require("../Enums");
7const Utils_1 = require("../Utils");
8const Vector_1 = require("./Particle/Vector");
9const Vector3d_1 = require("./Particle/Vector3d");
10const 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};
20class 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}
371exports.Particle = Particle;