-
Notifications
You must be signed in to change notification settings - Fork 17
/
Emitter.js
125 lines (112 loc) · 3.4 KB
/
Emitter.js
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
118
119
120
121
122
123
124
125
//@flow
import type { Element } from 'react';
import React from 'react';
import { Animated, Easing } from 'react-native';
import { Vector } from './entities/Vector';
import { fromAngle, toRadians } from './utils/vector-helpers';
import { move } from './utils/move-particle';
import emitParticle from './utils/emit-particle';
import type { VectorType } from './entities/Vector';
import type { ParticleType } from './entities/Particle';
import type { BaseEmitterType } from './BaseEmitter';
import BaseEmitter from './BaseEmitter';
export type EmitterType = BaseEmitterType & {
/** The direction angle of the particle (in degrees) */
direction: number,
/** The spread angle where particles are allowed to be rendered (in degrees) */
spread: number,
/** The speed of each particle */
speed?: number,
/** Gravity force to be applied to the particle movement */
gravity?: number,
/** number of steps the animation will be divided ( more segments == more precise animation == slow performance) */
segments?: number
};
export class Emitter extends React.Component<EmitterType> {
emitter: BaseEmitter;
static defaultProps = {
gravity: 0.2,
segments: 10,
speed: 5
};
_storeEmitterRef: any => void;
constructor(props: EmitterType) {
super(props);
this._calculate = this._calculate.bind(this);
this._animateParticle = this._animateParticle.bind(this);
this._storeEmitterRef = emitter => (this.emitter = emitter);
}
render() {
return (
<BaseEmitter
{...this.props}
onCalculate={this._calculate}
ref={this._storeEmitterRef}
onAnimate={this._animateParticle}
/>
);
}
_calculate = (initialPosition: VectorType, particlesCounter: number) => {
const {
numberOfParticles,
emissionRate,
direction,
speed,
spread,
gravity,
segments
} = this.props;
// if we're at our max, stop emitting.
const rate = Math.min(numberOfParticles, emissionRate);
const newParticles = [];
// for [emissionRate], emit a particle
for (let j = 0; j < rate; j++) {
/*
first step - Emit new particles
*/
const particle = emitParticle(
initialPosition,
fromAngle(toRadians(direction), speed),
toRadians(spread),
//Apply gravity to the vertical axis
Vector(0, gravity),
// Particle id
particlesCounter + j
);
// Calculate the particle path
// TODO: Improve the performance currently O(n2)
let path: VectorType[] = [];
let particleMovement: ParticleType = particle;
for (let j = 0; j < segments; j++) {
path.push(particleMovement.position);
particleMovement = move(particleMovement);
}
newParticles.push({
particle,
path
});
}
return newParticles;
};
_animateParticle = (path, transformValue, opacityValue) => {
const { particleLife } = this.props;
return Animated.parallel([
Animated.timing(transformValue, {
toValue: path.length,
duration: particleLife,
useNativeDriver: true
}),
Animated.timing(opacityValue, {
toValue: 0,
ease: Easing.inOut(Easing.quad),
delay: particleLife * 0.8,
duration: particleLife * 0.2,
useNativeDriver: true
})
]);
};
start() {
this.emitter.start();
}
}
export default Emitter;