/
Emitter.tsx
90 lines (76 loc) · 2.32 KB
/
Emitter.tsx
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
import React, {
cloneElement,
createRef,
FC,
MutableRefObject,
ReactElement,
useEffect,
useRef,
} from "react";
import { Entity as _Entity } from "tick-knock";
import { useEngine } from "../hooks/useEngine";
import { useStatefulRef } from "../hooks/useStatefulRef";
import { useTimer } from "../hooks/useTimer";
import { Entity } from "./Entity";
export type EmitterProps = {
emissionDelay?: number;
children: ReactElement | (() => ReactElement);
};
const emitterDefaults = {
emissionDelay: 0.1,
};
type ManagedElement<T> = {
element: ReactElement;
ref: MutableRefObject<T>;
};
export const Emitter: FC<EmitterProps> = (props) => {
const engine = useEngine();
props = { ...emitterDefaults, ...props };
const { emissionDelay } = props;
const id = useRef(0);
const template = props.children;
const [particles, setParticles] = useStatefulRef(
[] as ManagedElement<Entity>[],
);
const clone = () => {
const ref = createRef<Entity>();
const key = id.current++;
let element: ReactElement;
if (template instanceof Function) {
element = Object.assign({}, template(), { ref, key });
} else {
element = cloneElement(
template as ReactElement,
Object.assign({}, {}, { ref, key }),
);
}
return { element, ref } as ManagedElement<Entity>;
};
const remove = (index: number) => {
if (index > -1) {
if (particles.current.length === 1) {
setParticles([]);
} else {
const head = particles.current.slice(0, index);
const tail = particles.current.slice(index + 1);
setParticles(head.concat(tail));
}
}
};
useTimer(emissionDelay!, () => {
setParticles([clone(), ...particles.current]);
});
const onRemoved = (entity: _Entity) => {
const index = particles.current.findIndex(
(p) => p.ref.current?.entity.id === entity.id,
);
remove(index);
};
useEffect(() => {
engine.onEntityRemoved.connect(onRemoved);
return () => {
engine.onEntityRemoved.disconnect(onRemoved);
};
}, []);
return <>{particles.current.map((p) => p.element)}</>;
};