-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathparticle.ts
117 lines (90 loc) · 3.41 KB
/
particle.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
import { RangeValue, AnyFunction } from './background-interfaces';
import { randomString, getRndInteger, getRightTriangleSides, chooseOption } from './utils';
import deepMerge from 'deepmerge';
import defaultParticleOptions from './default-particle-options';
export interface ParticleOptions {
size?: RangeValue;
background?: string | string[];
lifespan?: RangeValue; // Units are in updates
speed?: RangeValue; // Movements per update
keepAround?: boolean;
startPositionX?: number;
startPositionY?: number;
}
export class Particle {
options: ParticleOptions;
size: number;
background: string;
lifespan: number;
speed: number;
id: string;
age: number;
positionX: number;
positionY: number;
initialPositionX: number;
initialPositionY: number;
finalPositionX: number;
finalPositionY: number;
private callOnDestroy: AnyFunction[] = [];
private callOnUpdate: AnyFunction[] = [];
private addToExistingMultiplierX: number = Math.random() > 0.5 ? 1 : -1;
private addToExistingMultiplierY: number = Math.random() > 0.5 ? 1 : -1;
constructor(options?: ParticleOptions) {
this.options = deepMerge(defaultParticleOptions, options || {});
this.id = randomString(20);
this.positionX = this.options.startPositionX || Math.random();
this.positionY = this.options.startPositionY || Math.random();
this.initialPositionX = this.positionX;
this.initialPositionY = this.positionY;
this.age = 0;
this.lifespan = getRndInteger(this.options.lifespan.min, this.options.lifespan.max);
this.size = getRndInteger(this.options.size.min, this.options.size.max);
this.speed = getRndInteger(this.options.speed.min, this.options.speed.max);
this.background = chooseOption(this.options.background);
this.setDestination();
}
triggerUpdate() {
this.age++;
if (this.age > this.lifespan) {
if (!this.options.keepAround) return this.destroy();
// Reset Position
this.initialPositionX = this.positionX;
this.initialPositionY = this.positionY;
// Reset age
this.age = 0;
this.lifespan = getRndInteger(this.options.lifespan.min, this.options.lifespan.max);
this.speed = getRndInteger(this.options.speed.min, this.options.speed.max);
// Reset destination
this.setDestination();
}
this.positionX = this.getNextPosition(this.initialPositionX, this.finalPositionX);
this.positionY = this.getNextPosition(this.initialPositionY, this.finalPositionY);
if (this.positionX > 1 || this.positionX < 0 || this.positionY > 1 || this.positionY < 0) return this.destroy();
this.callOnUpdate.forEach(fn => fn());
}
destroy() {
this.callOnDestroy.forEach(fn => fn());
}
onUpdate(fn: AnyFunction) {
this.callOnUpdate.push(fn);
}
onDestroy(fn: AnyFunction) {
this.callOnDestroy.push(fn);
}
private setDestination() {
const hypotenuse = this.lifespan * this.speed;
const angle = getRndInteger(0, 90);
const { adjacent, opposite } = getRightTriangleSides(hypotenuse, angle);
this.finalPositionX = addToExisting(this.positionX, adjacent, this.addToExistingMultiplierX);
this.finalPositionY = addToExisting(this.positionY, opposite, this.addToExistingMultiplierY);
function addToExisting(pos: number, extra: number, multiplyer: number): number {
return pos + extra * multiplyer;
}
}
private getNextPosition(start: number, finish: number): number {
if (start === finish) return start;
const difference = finish - start;
const step = difference / this.lifespan;
return start + step * this.age;
}
}