Animating Progress Rings with React Native Reanimated 2
Reanimated 2 and React Native SVG can be combined to make some powerful and impressive animations. One of the most practical shapes to animate is circles because they can be animated to create all types of useful UI components like progress rings. Once you understand a couple of SVG fundamentals, it’s a breeze to create any animated progress ring you can dream up.
The fundamentals: strokeDasharray and strokeDashoffset
The technique we’ll use to animate our circles depends on a neat hack using two common SVG properties: strokeDasharray
and strokeDashoffset
Here’s what they do.
strokeDasharray
lets us create a dashed stroke around the outside of our circle, rather than a solid stoke. If we provide just one number, we’ll get a dashed stroke with dashes and gaps that are the same size.
Alternatively, we can pass two numbers — the first will define the length of the dash, and the second will define the length of the gap between dashes.
strokeDashoffset
lets us offset the position of our stroke by a given number.
How do these two properties help us animate a circle? This is where it’s time to get creative. Let’s see what happens when we set our stroke dash size to be the entire circumference of our circle (using radius * PI * 2). We end up with what looks like a solid stroke. Technically our stroke is still dashed, but our dash covers the entire circumference of the circle, so we don’t see a gap.
Now, let’s add an offset that’s also the size of the circumference. It looks like our stroke is gone, but what’s actually happened is that our stroke gap covers the entire circumference of the circle, since our stroke gap is also the full circumference of the circle. This makes it so we don’t see any stroke.
We can subtract any arbitrary amount from our offset to “reveal” that much of our stroke. We’ll subtract 40 and see what happens.
Do you see where this is going? 😏
Time to Animate!
To get the progress ring effect we’re after, we just need to animate strokeDashoffset
from the circle’s full circumference to zero. This is pretty straightforward with Reanimated 2.
There are a few things to understand here.
- We create a new component called
AnimatedCircle
by passing React Native SVG’sCircle
through Reanimated’screateAnimatedComponent
. This will allow us to animate the component’s props using Reanimated - We create a shared value for stroke offset, initially set to the circumference of the circle so our stroke isn’t visible
- With
useAnimatedProps
, we define awithTiming
animation to handle changes in thestrokeDashoffset
value - With the
useEffect
hook, we update our shared value of zero, thus kicking off our progress ring animation
It’s that simple to create beautiful progress ring animations with Reanimated and React Native. Of course, progress rings are usually accompanied by a numeric indicator, often expressed as a percentage. We can animate that, too!
Animating percentage text
We can apply many of the same principles we used to animate our circle and use them to animate our percentage text.
Here’s what we’ve added:
- We create a new component called
AnimatedText
by passing React Native’sTextInput
through Reanimated’screateAnimatedComponent
. This will allow us to animate the component’s props with Reanimated - We create a shared value called
percentage
that converts the stroke offset value to a number between 0 and 100, to track the animation’s progress - With
useAnimatedProps
, we create an animatedtext
prop that we can pass to ourAnimatedText
component
Now you have a silky smooth progress ring with animated percentage text!
What’s that you say? You want to animate the color of the progress ring from red to yellow to green as it moves from 0 to 100% percent? OK, we can do that easily with Reanimated.
Animating the progress ring color
We can add the color animation we want with just a few more lines of code, thanks to Reanimated’s interpolateColor
Here we create a new strokeColor
value with useDerivedValue
. To get it, we use interpolateColor
to map our percentage value to the colors we want to animate. After that, we add our stroke color value to our animatedCircleProps
and that’s it!
Conclusion
I hope this post has given you the knowledge to start creating your own progress rings and other animated circle components with Reanimated. I’d love to see what you build; tag me on twitter @useRNRocket! As always, comments and corrections are welcome! Follow me here and on Twitter for more React Native and Reanimated content.