Intro to SVG Animations with React-Native ReAnimated 2

Daniel Friyia
TribalScale
Published in
8 min readJan 31, 2022

--

Written by: Daniel Friyia, Agile Software Engineer, TribalScale

📫 Subscribe to receive our content here.

💬 Have any questions about our development capabilities? Click here to chat with one of our experts!

The app we’ll be building today!

A few months ago, React Native ReAnimated Version 2 came out and I’ve really enjoyed working with it. Honestly, before this library was released, I was hesitant to write many animations in React Native 😅. It felt like they always came out janky in Android while Flutter seemed to have a clear edge in this domain. From the tinkering I’ve done so far, ReAnimated 2 feels like a game changer.

’s use of the JavaScript Interface (JSI) with the new React-Native architecture has made a world of difference.

ReAnimated 2 makes me feel way more free as a developer to express my creativity through animations. It’s always a great feeling when you can take a design and make it come to life by animating it for users to view. I thought I would share a short introduction on how to do SVG animations with this new library so other React-Native developers could get a taste of how it works. I can’t wait to see the new things this library allows us to create 💪! If you get stuck at any point, feel free to check out my sample code on GitHub here.

What Are We Building Today?

My background is primarily building fitness apps. In these types of apps it’s always critical to have beautiful charts as eye candy. This makes me gravitate towards charts when I think of animations. I’ve created a simple chart for us to build and animate:

Our animation on Android

Project Setup

To get started, install ReAnimated 2 and React Native SVG. The full instructions for setting up ReAnimated 2 are available on the

website here. I’ll put my version of the instructions into this article for convenience.

TypeScript Instructions

Start by generating the project using this command in the terminal.

npx react-native init SVGAnimationSample --template react-native-template-typescript

Then, add react-native-svg and react-native-reanimated by running yarn add at the root of your project.

yarn add react-native-svg react-native-reanimated

Next, add the following to your babel.config.js to enable the ReAnimated plugin for the Metro Bundler.

module.exports = {
...
plugins: ['react-native-reanimated/plugin'],
};

If you are having trouble with this step check out my metro config here.

Android Instructions

You’ll need to activate Hermes at this point. Hermes is the new JavaScript Engine for React-Native produced by Facebook. Unfortunately, this will disable your ability to use the Chrome debugger. Luckily, Facebook provided us with Flipper for debugging with Hermes apps if you need that functionality. We won’t be setting it up in this tutorial though.

To enable Hermes, start in the android directory’s build.gradle file, find this line, and change enableHermes to true.

project.ext.react = [  
enableHermes: true
]

Then find this line and do the same.

def enableHermes = project.ext.react.get("enableHermes", true);

Finally, add these lines to your MainApplication.java.

import com.facebook.react.bridge.JSIModulePackage;  
import com.swmansion.reanimated.ReanimatedJSIModulePackage;
...
@Override
protected JSIModulePackage getJSIModulePackage() {
return new ReanimatedJSIModulePackage();
}

Doing this is always pretty tedious, if you get lost, check out my gradle file.

iOS Instructions

Make sure your :hermes_enabled variable is set to true in your ios/Podfile then run pod install in the ios directory of your app in the terminal.

Making a Custom Circle SVG

Alright, now that the boring stuff is out of the way, let’s write some code 🧑‍💻! First, clear out the boilerplate code in the App.tsx file generated with our project. You can replace the App.tsx boilerplate with my code in the Gist below. All I do here is declare a radius for our circle and pass in basic props like the colour of the chart and the percentage of it we want to fill.

Next, create a file named CircularProgress.tsx under the root directory. We’ll import react-native-svg and create our prop type.

import React, {FC} from 'react
import {View, Text} from 'react-native'
import Svg, {Circle} from 'react-native-svg'
type CircularProgressProps = {
strokeWidth: number;
radius: number;
backgroundColor: string;
percentageComplete: number;
}

Now create the container style for our component so it can be used in the CircularProgress component’s container.

const styles = StyleSheet.create({
container: {
...StyleSheet.absoluteFillObject,
justifyContent: 'center',
alignItems: 'center',
}
})

At long last, let’s add our SVG to the component.

Your CircularProgress.tsx file so far should look like this:

This code will give us a really vanilla, uninteresting, circle 😑.

Full circle on Android device

Making Our Circle into a Chart

Let’s 🌶 this up a little and hollow out the circle so it looks a more like a chart. We start by calculating the inner radius of the circle. This value is needed because the strokeDashoffset takes up some space outside the circle. To fit our component in the space allotted for it, using the inner radius forces the results of our calculations to remain within the container.

const innerRadius = radius - strokeWidth / 2;

We calculate circumference based on the inner radius for the same reason. Our circumference should fit in the component’s container.

const circumfrence = 2 * Math.PI * innerRadius;

Now change the fill colour to transparent, change the circumfrence to the value we just calculated, and adjust the r value to be the innerRadius in our SVG.

Sweet 🙂 this is finally starting to look like a chart 👏.

Hollow circle on Android device

An Aside About How Circles Work

Let’s tinker around with strokeDashoffset so we can understand how it works before animating anything. As we already know, a full revolution of a circle is 2𝛑r. Let’s make the following change to our circle so we can have a strokeDashOffset modified from its default value we’ve been using so far.

If we multiply innerRadius in the dashstrokeOffset prop by 50% we see that dashstrokeOffset actually moves and becomes a half circle. This is because we are reducing the offset based on a smaller value of 𝛑. We can use this property of circles to animate our SVG 🤯.

A semi circle on iOS

Let’s Start Animating

Pretty cool right? But you’re not here to learn about circles, thats boring High School math, let’s animate this chart 😤!

The first thing to do is wrap our circle using createAnimatedComponent. We do this because SVG Circle by itself is not animatable.

const AnimatedCircle = Animated.createAnimatedComponent(Circle);

Then just replace Circle with AnimatedCircle in the markup.

<View style={styles.container}>      
<Svg style={StyleSheet.absoluteFill}>
<AnimatedCircle
...

Next, wrap the values we use for the circle animation in values from the ReAnimated 2 library. We are going to use three types of these special animated values here. useSharedValue creates a shared memory variable between the UI thread and TypeScript. useDerivedValue allows us to perform computations on variables that exist in the UI thread. Finally, useAnimatedProps allows you to manipulate styles on the UI thread from JavaScript. No bridging is used here like in the legacy animation libraries. All manipulations are done through shared memory.

Before wrapping our values, we have to start by inverting the percentage complete that we want to show so it works with the dashstrokeOffset. It looks like this:

const invertedCompletion = (100 - percentageComplete) / 100;

Now let’s wrap our theta value in a shared value so we can use it for animations.

const theta = useSharedValue(2 * Math.PI);

We can then compute the value we want to animate to using useDerivedValue.

const animateTo = useDerivedValue(() => 2 * Math.PI * invertedCompletion);

Now let’s use animated props instead of the default strokeDashoffset so the animation grows as time passes. We are going to use a transition here. Transitions are mini animations provided by ReAnimated to create animated effects. The transition we are going to use here is withTiming. You basically supply it with a value, and it changes for the duration of time you provide. The animated prop ends up looking like this:

const animatedProps = useAnimatedProps(() => {
return {
strokeDashoffset: withTiming(theta.value * innerRadius, {
duration: 1500,
}),
};
});

We then need to activate the animation by setting a new value to theta. To do this we’ll add a button to the app that updates the theta when pressed. Your animation code should now look like this:

The animation should now look like this:

Finishing Touches

Let’s complete this chart by showing the percentage and having everything fade in using an opacity. We start by creating a global variable with how fast we want everything to fade in.

const FADE_DELAY = 1500;

Then we create a shared value for the opacity of text.

const textOpacity = useSharedValue(0);

Let’s now create a couple of styles for fading in the text.

const powerTextStyle = useAnimatedStyle(() => {
return {
opacity: withTiming(textOpacity.value, {
duration: FADE_DELAY,
}),
};
});
const powerPercentTextStyle = useAnimatedStyle(() => {
return {
opacity: withTiming(textOpacity.value, {
duration: FADE_DELAY,
}),
};
});

Next, add a couple Animated.Text components that we can place overtop the chart.

<Animated.Text style={[styles.powerText, powerTextStyle]}>
Power %
</Animated.Text>
<Animated.Text style={[styles.powerPercentage, powerPercentTextStyle]}>
{percentageComplete}
</Animated.Text>

At this point, we can finish up by adding a button that animates in and out whenever we press it by changing the theta value and the textOpacity value.

<Button
title="Animate!"
onPress={() => {
if (!textOpacity.value) {
theta.value = animateTo.value;
textOpacity.value = 1;
} else {
theta.value = 2 * Math.PI * 1.001;
textOpacity.value = 0;
}
}}
/>

Your full CircularProgress.tsx should now look like this:

And there you have it! Your first SVG animation with ReAnimated 2 🤜 🤛.

Our animation running on iOS

I hope you enjoyed building this 📊 animation with ReAnimated 2. I’ve personally had a ton of fun with this library since it’s come out and I hope you’ll enjoy it as much as I have 😀.

Daniel is an Agile Software Developer specializing in Flutter and React-Native. As much as he likes cross platform he is passionate about all types of mobile development including native iOS and Android and is always looking to advance his skill in the mobile development world.

TribalScale is a global innovation firm that helps enterprises adapt and thrive in the digital era. We transform teams and processes, build best-in-class digital products, and create disruptive startups. Learn more about us on our website. Connect with us on Twitter, LinkedIn & Facebook!

--

--

Daniel Friyia
TribalScale

Daniel is an Agile Software Developer specializing in all forms of mobile development, Native iOS, React-Native and beyond