Animations in React Native. How to achieve great performance and tips — 1x04

Tasos Maroudas
Mar 15, 2018 · 5 min read

This story is part of a series where I am sharing my experiences on React Native: how I approached and worked with RN Components, APIs, external packages and all sorts of issues. I hope that this series of posts will prove useful to the React Native community and provide helpful insights. For app development enquiries check us out or contact me here.

Why are animations important?

Animations are important in order to create an optimal user experience, especially in the mobile world. App users have been exposed to a lot of them already, and have become familiar with their usability and beauty. And since they have hands-on experience with them, they expect them as a given in every single app they download. As per the React Native animations documentation:

Objects in motion have momentum.

Animations allow you to convey physically believable motion in your interface.

Image 1: Animations are awesome!

Animation systems in React Native

React Native provides two complementary animation systems: Animated and LayoutAnimation. These are 2 different APIs with a different usability and purpose.

My personal feeling is that Animated is more widely used from developers, probably because it provides more control — and it also comes with better documentation.

In this article I assume that you are familiar with Animated API and I will blog on how to achieve the best performance out of it, alongside some other practical tips.

Animated API tips: Performance and more

In order to create an animation you can find very good documentation and “how to start” examples in React Native animations documentation.

How to achieve great performance

To achieve great performance you always need to make use of the configuration prop useNativeDriver (with value set to true). For example, in order to animate the rotation of an image our timing function would look like that:

Animated.timing(
this.state.spinValue,
{
toValue: 1,
duration: 3000,
easing: Easing.linear,
useNativeDriver: true
}
).start();

This config prop is very very important because that way, RN sends the animation to be executed at the native realm directly passing through the bridge only once, thus achieving the best possible performance. If you are not familiar with the JavaScript/native realms and their bridge, you can get more insights in this post by @TalKol.

Now, according to RN documentation useNativeDriver prop cannot be used for all animation types:

Not everything you can do with Animated is currently supported in Native Animated. The main limitation is that you can only animate non-layout properties, things like transform and opacity will work but flexbox and position properties won’t.

There is a way though to overcome this, if you are composing animations. For instance let’s take an example from the Math Warriors Android app where we change the size of our logo image when the user clicks the start game button.

Image 2: Logo size increase animation when start game button is clicked in Math Warriors

What we do at this point is that we change the width and height of the logo simultaneously and these properties do not support use of the animated driver property. Their parallel execution though does support it as shown below:

Animated.parallel(
[
Animated.timing(
this.state.rankImageWidth,
{
toValue: 110
}
),
Animated.timing(
this.state.rankImageHeight,
{
toValue: 110
}
)
],
{
useNativeDriver: true
}
).start();

How to perform animations in loops

RN does not support loop animations out of the box, so we had to find a way. A 2nd example from Math Warriors game, which is actually the extension of our previous example, is the following: as soon as the user presses the start game button, the logo increases its size and also starts moving up and down in a loop and until the app matches the player with an opponent.

Image 3: Logo movement animation in a loop when start game is clicked in Math Warriors

We performed this by creating a JavaScript interval id. The catch here is that in order to avoid app glitches and waiting time, you need to execute the actual animation twice: the 1st one and all the others that will follow inside a loop (JavaScript interval).

// Animation function that moves an element over y axis over time
const upAndDown = (yPositionPropName, duration, yHighPosition, yLowPosition) => {
Animated.sequence([
Animated.timing(yPositionPropName, {
duration: duration || 1000,
toValue: yHighPosition
}),
Animated.timing(yPositionPropName, {
duration: duration || 1000,
toValue: yLowPosition
})
]).start();
};

// Call and use the function above
// Start up & down animation for the first time before the loop takes over
upAndDown(
this.state.rankImageTopPosition,
this.rankImageMoveDuration,
this.rankImageHighposition,
this.rankImageLowposition
);
// Loop rank image up & down movement. Total interval time
// is (animation duration x 2) time for the whole up & down move
this.rankAnimationIntervalId = setInterval(() => {
upAndDown(
this.state.rankImageTopPosition,
this.rankImageMoveDuration,
this.rankImageHighposition,
this.rankImageLowposition
);
}, this.rankImageMoveDuration * 2);
// Clear the interval id after we match our player with an opponent
clearInterval(this.rankAnimationIntervalId);

How to use animatable components with styled components

For the users of styled components you can convert any of the 4 animatable components (View, Text, Image, ScrollView) to a styled component like that:

const AnimatedImage = styled(Animated.Image)`
.
.
.
`;

Validate animation’s real performance

In order to understand how your animation will behave you need to test it on a real device. Emulators cannot provide this kind of feedback. On the contrary, your animation may run flawlessly in an emulator environment and be sticky on a real device. Only the real device test can reveal if the animation shows at 60 FPS or less, which is when we experience slow breaking animations.

What do you think?

What do you think about these animations’ tips? How do you make your animations work for you? Offer your perspective and ideas in the comments section below.

Do you have a specific subject that you would like me to cover? If I have worked with it, I will be more than happy to share my perspective.

If you enjoyed this article, feel free to hit that clap button 👏 to help others find it.

About me

Hi there, I’m Tasos; a software engineer that currently works a lot with React Native. I’m the Founder of Coded Lines where among other things we create user engaging mobile apps that help companies drive growth to their brands. Check us out or contact me here. Thanks for stopping by :)

Building With React Native

Technical posts about React Native.

Tasos Maroudas

Written by

❤ React Native. Part time blogger. CTO & Founder of Coded Lines Ltd

Building With React Native

Technical posts about React Native. What issues we encountered while developing Math Warriors Android game and how we solved them.

Tasos Maroudas

Written by

❤ React Native. Part time blogger. CTO & Founder of Coded Lines Ltd

Building With React Native

Technical posts about React Native. What issues we encountered while developing Math Warriors Android game and how we solved them.

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store