React Native Animations Done Right

Animations in react native are great! They’re simple and at the same time powerful. Still, there are some important concepts to take into account and common pitfalls.

Introduction

Before we start digging into animations in react native you may ask, why do we need a special set of tools to create animations? We could simply use setState to trigger render as many times as we need, right? Yes, simple and effective, this will create the frame sequence we want.

It’s a good practice to bind methods to this in the constructor.

While this solution will work well for simple cases, rendering all the view hierarchy could be expensive. But more important, because react native provides a set of specific tools (animation types, interpolation, etc) to make things simpler, clearer and more performant.

Two alternatives

React native offers two types of animations:

  • LayoutAnimation: Automatically animates the difference between the current layout and a future one, generated after a new state is set. This animation happens natively, which is great for performance, but can be hard to deal with given that all properties are going to be animated between states.
In order to get this to work on Android you need to set UIManager.setLayoutAnimationEnabledExperimental(true)
  • Animated: Allow us to create declarative animations by binding an animated object to a style property. It runs on javascript by using requestAnimationFrame and setNativeProps, which could be slow depending on how we’re animating. The benefits are plenty: flexibility, control and integration with gestures.

As you can see an Animated animation work based on Animated.Value associated/bound to a style property. Then, the only thing yo need to do is start the animation, making the animated value change over time, updating the style.

Main Animated

When I started making animations I found myself creating an Animated.Value instance per style property I wanted to animate. Then using Animated.parallel and Animated.sequence I was able to combine them.

This approach works well for simple transitions but it becomes hard to maintain when we start having multiple animations that should work synchronously.

This doesn’t mean that you should never use Animated.parallel and Animated.sequence, but that in general you don’t need to use them.

If you look closely to the animations you’ll find that you can extract one of them and base the others on that one. That’s when Animated.interpolate comes in place.

Interpolation

Animated.interpolate creates a new animation based on an existing one by mapping its values to a different range. This simple but powerful concept allow us to create a Animated.Value hierarchy based on a root Animated.Value.

To read more about interpolation (and extrapolation) take a look at the references.

This concept is amazing and simplifies the code a lot but has a small catch. If the root animation range changes, all the child animations will be affected. For this problem there is also a simple and elegant solution.

Normalized range

I found that having our main animation normalized (ranging between 0 and 1) helps keeping interpolated animations simple given the input range for all of them will always be the same

Gestures

Under certain circumstances we want the user to ‘control’ the animation by performing a gesture like scroll or pan. Such situations don’t vary too much from what we’ve seen so far. The idea behind this is to map one or more values in the gesture’s event to one or more animated values. For that Animated provides a handy function called event.

If we look closer to event(…) you’ll realize that it’s just returning a function with matches the signature of onPanResponderMove. This means that we can actually create our own custom version of the mapping.

Gestures with custom mapping

Animated.event is great for simple mapping but not for complex scenarios. There are situations in which we want to make some calculation with the gesture parameters before setting it to an animated value. We can achieve that by writing a custom handler and setting animated value manually with Animated.Value.setValue.

Conclusions

Layout animations are simple but not as powerful as Animated.

While working with Animated, always have a root animated value to represent the overall transition. Based on it, create as many interpolated animations as you need.

Keep the root animated value range normalized.

Gestures plays really well with Animated.

If you find that you’re missing some frames while animating check:

  • Changing the state triggers render: There are situations in which a new state / properties shouldn’t change the component layout. In these situations we can use shouldComponentUpdate returning false to avoid calling render.
  • Avoid animating height and width, use scaleX and scaleY instead: When the result is not the same it’s much faster. This is because changing the size of the view with height / width forces re-rendering the whole view hierarchy, which takes a lot more time. As a general rule use transform.

Enjoy your animations!

References