-
Notifications
You must be signed in to change notification settings - Fork 2
/
AnimatedScale.tsx
72 lines (63 loc) · 1.72 KB
/
AnimatedScale.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
import { useState, useRef, useEffect } from 'react';
import { interpolate as d3interpolate } from 'd3-interpolate';
import { useSpring, SpringConfig } from 'react-spring';
interface GenericScale<ScaleInput> {
(input: ScaleInput): number;
domain(): number[] | [number, number];
domain(domain: number[]): this;
range(): number[] | [number, number];
range(range: number[]): this;
}
type AnimatedScaleProps<ScaleInput> = {
domain: number[];
range: number[];
springConfig?: SpringConfig;
};
export function useAnimatedScale<ScaleInput>(
scale: () => GenericScale<ScaleInput>,
props: AnimatedScaleProps<ScaleInput>
) {
const [state, setState] = useState({
domain: props.domain,
range: props.range,
});
const scaleRef = useRef(scale().domain(state.domain).range(state.range));
useEffect(() => {
setState((prevState) => ({
...prevState,
domain: props.domain,
}));
}, [props.domain[0], props.domain[1]]);
useEffect(() => {
setState((prevState) => ({
...prevState,
range: props.range,
}));
}, [props.range[0], props.range[1]]);
const interpolateDomain = d3interpolate(
scaleRef.current.domain(),
state.domain
);
const interpolateRange = d3interpolate(scaleRef.current.range(), state.range);
// @ts-ignore
const { t } = useSpring({
reset: true,
from: { t: 0 },
to: { t: 1 },
config: {
precision: 0.00001,
...props.springConfig,
},
onFrame: ({ t }: { t: number }) => {
scaleRef.current.domain(interpolateDomain(t)).range(interpolateRange(t));
},
});
return {
scale: scaleRef.current,
state,
setState,
interpolate: (func: (t: number) => any) => {
return t.interpolate(() => func(t));
},
};
}