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.
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
thisin 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.
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
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.
When I started making animations I found myself creating an
Animated.Value instance per style property I wanted to animate. Then using
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.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.
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
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.
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
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
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
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
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
falseto avoid calling
- 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
Enjoy your animations!