✍️Creating React Native Animated Number Count using Reanimated
How to create Animated Number count in React Native
Hey all!
With the exciting new version release of the Nutsss mobile application (here is the link if you wanna check it out), I’ve put together a detailed blog post on how to create one of the standout animations featured within our app.
Considering that we invest a lot of time in the gamification of elements in our mobile application, this was one of the first elements we added.
Let’s see how can we build this in React Native using Reanimated.
I will guide you through the entire process, from initializing the project and adding the required dependencies, to crafting the animation effects and designing the components. This blog series aims to be informative and motivational, regardless of whether you're just starting out with React Native or you've been developing with it for some time.
React Native Project Setup
First off, we are going to setup our project using Expo.
Head off to expo documentation - Get Started. And you can easily follow instructions written on their get started guide.
To make the process even easier you can use one of their pre-made templates. I recommend using typescript template since in this blog post we will be using typescript
npx create-expo-app --template
Installing dependencies
We will be using react native reanimated to create animations.
Install:
npx expo install react-native-reanimated
Then head over to their webiste to follow more instructions on how to set it up in expo. Its pretty simple, we just add reanimated plugin to our babel config.
And that would be it from dependencies🤷
Code part
Let’s first create a component called AnimatedNumber:
const AnimatedNumber = ({ number }: { number: number }): JSX.Element => {
return (
<Animated.View
style={{
flexDirection: "row",
}}
></Animated.View>
);
};
As observed, we're passing a 'number' prop of the type 'number'. This indicates that the value must be of the number type, rather than a string.
Additionally, given our requirement to display a comma for every thousand, the provided code will accommodate this. Now, let's proceed to extract digits with a bit of assistance from ChatGPT:
const AnimatedNumber = ({ number }: { number: number }): JSX.Element => {
const digits = React.useMemo(() => {
// Convert the number to a string and then to an array of its characters
const chars = number.toString().split("");
// Initialize an empty array to hold the digits and commas
const result: (number | string)[] = [];
// Calculate the number of commas needed
const commas = Math.floor((chars.length - 1) / 3);
// Insert digits and commas into the result array
chars.forEach((char, index) => {
// Convert char to number and push to result
result.push(parseInt(char, 10));
// Check if a comma should be inserted next
// This checks if the current position, moving backwards, is a multiple of 3 away from the last digit
// And also ensures a comma is not added at the end
if (
(index + 1) % 3 === chars.length % 3 &&
commas > 0 &&
index < chars.length - 1
) {
result.push(",");
}
});
return result.reverse();
}, [number]);
return (
<Animated.View
style={{
flexDirection: "row",
}}
></Animated.View>
);
};
Now that we have our digits in array like [1,’,’,2,3,4] we can proceed.
In our Animated.View we can now map our digits and create our AnimatedDigit component in the next step
{
digits.map((digit, index) => {
if (typeof digit === "string") {
return (
<Text
key={index}
style={{
...typography,
color: "white",
width: 14,
height: typography.lineHeight,
}}
>
{digit}
</Text>
);
}
return (
<AnimatedDigit
duration={1000}
key={index}
digit={digit}
width={digit === 1 ? 22 : 32}
/>
);
});
}
If the digit is a comma (','), we'll simply use a standard Text component with consistent typography. Otherwise, we'll display our AnimatedDigit component.
You might wonder why the text width for the number '1' is manually set.
Technically, '1' has the same width as other numbers. However, visually, it doesn't occupy as much space, leading to an awkward gap between numbers. To address this, I've adjusted the width of '1' to be narrower, which helps achieve a more uniform appearance. We can experiment with this width to find the best fit.
Now, let's move on to creating our AnimatedDigit component.
type AnimatedDigitProps = {
digit: number;
width: number;
duration?: number;
};
const AnimatedDigit = ({
digit,
width,
duration = 1000,
}: AnimatedDigitProps): JSX.Element => {
<Animated.View
entering={FadeIn.duration(duration)}
style={[
{
height: numberHeight,
overflow: "hidden",
},
]}
></Animated.View>;
};
What we aim to achieve is the following: For each digit, we render all possible numbers from 0 to 9. Then, we animate the desired digit's position using translateY.
Here’s how we do that:
const NUMBERS = Array(10)
.fill([])
.map((_, i) => i);
Then we render all the digits like this
<Animated.View
entering={FadeIn.duration(duration)}
style={[
{
height: numberHeight,
overflow: "hidden",
},
]}
>
<Animated.View style={translateYStyle}>
{NUMBERS.map((_, index) => {
return (
<Text
key={index}
style={{
...typography,
color: "white",
width,
height: numberHeight,
textAlign: "center",
}}
>
{index}
</Text>
);
})}
</Animated.View>
</Animated.View>
And the only thing we need now is that translateY animation which we can define like this:
const translateYStyle = useAnimatedStyle(() => {
return {
transform: [
{
translateY: withSpring(-numberHeight * digit, {
mass: 1.2,
}),
},
],
};
});
This code pretty much says, okay animate my Y position based on the height of the number and position of the digit that is desired. Feel free to play around with the mass prop and adjust it to your needs. I like 1.2 because it gives us this “slot machine” vibe😆
The end
I hope this guide proves beneficial for anyone looking to implement animated numbers in React Native. While we drew inspiration from the react-native-animated-numbers library, our specific requirements meant we couldn't leverage the ready-made solution directly. However, I highly recommend exploring it, as it may perfectly suit your needs.
A little bit about nutsss.app:
Nutsss is revolutionizing personal finance management with its unique and fun approach by gamifying daily financial activities. Don't miss out on this innovative solution to making finance management engaging and intuitive. Be sure to check it out, nutsss is available on both Google play and App store
Ill try to be more active and do at least 1-3 publications per month. Ill be writing in general about startups, coding and animations. Be sure to subscribe.
I’m always on the lookout for fresh ideas for future publications, so if you have any suggestions or topics you’re curious about, please let me know!
Oh, and one more thing, if you are failing - be sure to fail fast and try again.
Talk to you soon,
Uroš