You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
59 lines
1.6 KiB
59 lines
1.6 KiB
import { useEffect, useState } from 'react'; |
|
|
|
import { animated, useSpring, config } from '@react-spring/web'; |
|
|
|
import { reduceMotion } from '../initial_state'; |
|
|
|
import { ShortNumber } from './short_number'; |
|
|
|
interface Props { |
|
value: number; |
|
} |
|
export const AnimatedNumber: React.FC<Props> = ({ value }) => { |
|
const [previousValue, setPreviousValue] = useState(value); |
|
const direction = value > previousValue ? -1 : 1; |
|
|
|
const [styles, api] = useSpring( |
|
() => ({ |
|
from: { transform: `translateY(${100 * direction}%)` }, |
|
to: { transform: 'translateY(0%)' }, |
|
onRest() { |
|
setPreviousValue(value); |
|
}, |
|
config: { ...config.gentle, duration: 200 }, |
|
immediate: true, // This ensures that the animation is not played when the component is first rendered |
|
}), |
|
[value, previousValue], |
|
); |
|
|
|
// When the value changes, start the animation |
|
useEffect(() => { |
|
if (value !== previousValue) { |
|
void api.start({ reset: true }); |
|
} |
|
}, [api, previousValue, value]); |
|
|
|
if (reduceMotion) { |
|
return <ShortNumber value={value} />; |
|
} |
|
|
|
return ( |
|
<span className='animated-number'> |
|
<animated.span style={styles}> |
|
<ShortNumber value={value} /> |
|
</animated.span> |
|
{value !== previousValue && ( |
|
<animated.span |
|
style={{ |
|
...styles, |
|
position: 'absolute', |
|
top: `${-100 * direction}%`, // Adds extra space on top of translateY |
|
}} |
|
role='presentation' |
|
> |
|
<ShortNumber value={previousValue} /> |
|
</animated.span> |
|
)} |
|
</span> |
|
); |
|
};
|
|
|