react-native-animated-numbers
Version:
Module that shows number changes through simple animation
111 lines • 3.94 kB
JavaScript
import React, { useCallback, useMemo, useRef } from 'react';
import { Text, View, Animated, Easing, StyleSheet } from 'react-native';
import usePrevious from '../hooks/usePrevious';
import { createNumberArrayWithComma, toAbsoluteInteger } from '../utils';
import { DEFAULT_FONT_VARIANT } from '../constants';
const AnimatedNumber = ({
animateToNumber: givenAnimateToNumber,
fontStyle,
animationDuration,
includeComma,
easing,
containerStyle,
fontVariant = DEFAULT_FONT_VARIANT,
locale = 'en-US'
}) => {
const animationRef = useRef(null);
const animateToNumber = toAbsoluteInteger(givenAnimateToNumber);
const prevNumber = usePrevious(animateToNumber);
const animateToNumberString = animateToNumber.toString();
const prevNumberString = prevNumber.toString();
const NUMBERS = useMemo(() => Array(10).fill(null).map((_, i) => i), []);
const nextNumbersArr = useMemo(() => {
return includeComma ? createNumberArrayWithComma(animateToNumberString, locale) : Array.from(animateToNumberString, Number);
}, [animateToNumberString, includeComma, locale]);
const prevNumbersArr = useMemo(() => {
return includeComma ? createNumberArrayWithComma(prevNumberString, locale) : Array.from(prevNumberString, Number);
}, [includeComma, prevNumberString, locale]);
const [height, setHeight] = React.useState(0);
const animations = useMemo(() => height === 0 ? [] : nextNumbersArr.map((__, index) => {
const value = prevNumbersArr[index];
if (typeof value !== 'number') {
return new Animated.Value(0);
}
const animationHeight = -1 * (height * value);
return new Animated.Value(animationHeight);
}), [nextNumbersArr, height, prevNumbersArr]);
const setButtonLayout = useCallback(e => {
setHeight(e.nativeEvent.layout.height);
}, []);
React.useEffect(() => {
if (height === 0) return;
if (animationRef.current) {
animationRef.current.stop();
}
const compositions = animations.reduce((acc, animation, index) => {
const value = nextNumbersArr[index];
if (typeof value === 'number') {
acc.push(Animated.timing(animation, {
toValue: -1 * (height * value),
duration: animationDuration || 1400,
useNativeDriver: true,
easing: easing || Easing.elastic(1.2)
}));
}
return acc;
}, []);
animationRef.current = Animated.parallel(compositions);
animationRef.current.start();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [animations, height]);
return /*#__PURE__*/React.createElement(React.Fragment, null, height !== 0 && /*#__PURE__*/React.createElement(View, {
style: StyleSheet.flatten([containerStyle, {
flexDirection: 'row',
height
}])
}, animateToNumber < 0 && /*#__PURE__*/React.createElement(Text, {
style: [fontStyle, {
height
}]
}, "-"), nextNumbersArr.map((n, index) => {
if (typeof n === 'string') {
return /*#__PURE__*/React.createElement(Text, {
key: index,
style: [fontStyle, {
height
}]
}, n);
}
return /*#__PURE__*/React.createElement(View, {
key: index,
style: {
height,
overflow: 'hidden'
}
}, /*#__PURE__*/React.createElement(Animated.View, {
style: [{
transform: [{
translateY: animations[index]
}]
}]
}, NUMBERS.map((number, i) => /*#__PURE__*/React.createElement(Text, {
style: StyleSheet.flatten([fontStyle, {
fontVariant,
height
}]),
key: i
}, number))));
})), /*#__PURE__*/React.createElement(View, {
style: {
opacity: 0,
position: 'absolute'
},
pointerEvents: "none"
}, /*#__PURE__*/React.createElement(Text, {
style: fontStyle,
onLayout: setButtonLayout,
numberOfLines: 1
}, animateToNumber)));
};
export default AnimatedNumber;
//# sourceMappingURL=AnimatedNumber.js.map