What the React Native docs forgot to tell you about animations

React Native is great. It’s pretty awesome to be able to build a native app with only JavaScript knowledge. One of the things I like most is making the animations, it can add so much to the experience of an app. Unfortunately React Native’s documentation about animations is not so great. Some things are explained rather vaguely and other things are plain missing. That’s why I wanted to give you a quick overview of the things I think need a bit of extra explanation. There’s still more out there, but these are the things you’ll probably need most.

One last thing to note before I begin, I write this as an addition to the React Native documentation. You should check that first if you haven’t already. Here I’ll try not to repeat too much from the original docs.

Animating

The Animated library has 3 functions to animate things on screen, Animated.timing, Animated.spring and Animated.decay. How to use them is pretty clear from the documentation, but when to use which function can be confusing.

Animated.timing gives you the most control. You define the duration and you set the easing. Especially if you’ve got a specific easing from the motion design, this could be your easiest option. However, if you don’t have a predefined easing, it can be hard to give the animation the right feel. Then Animated.spring is your better option. This function provides a spring physics model, it’s based on movements from the real world.

There are 3 option combinations to modify the spring motion: friction/tension, bounciness/speed and damping/stiffness and mass. The last one gives you the most control, the mass of the others is automatically 1. All three have different sensitivities, so it’s good to play around with them a bit to see which one gives you the best results. Here I’ll explain what changing the value of one of the options would do to your animation.


Friction
The resistance that one surface or object encounters when moving over another. A lower friction value gives more overshoot (the amount it moves beyond the toValue) and bounces more often.

Tension
Tension is like a rope pulling on a box, the force that is transmitted through the rope is tension. This controls the speed on an animation, a higher tension gives a faster-moving animation.


Example of a high bounciness

Bounciness
Does what it says, an increased bounciness will increase the overshoot and the number of bounces. An easy way to create a crazy bouncing object.

Speed
The speed of the animating object, has no influence on the overshoot or the number of bounces.


Animations with damping, stiffness and mass are based on a Damped Harmonic Oscillator. Each bounce is described as on oscillation.

Damping
Reducing the damping value reduces the energy loss with each oscillation and there will be more overshoot. Increasing the value increases the energy loss with each oscillation, there will be fewer and smaller oscillations and the duration will be shorter.

Stiffness
A higher stiffness increases the overshoot and the speed of the motion.

Mass
Adapting the mass value will change the momentum of the animation. An increased mass, a heavier object, has a slower start and a more rounded bounce. A higher mass also makes the duration of the animation longer.


The last animation function is Animated.decay() and has some specific use cases. It requires you to set an initial velocity. Based on that and a deceleration value, it calculates what the end value is and how long the animation should take. For example, if you use it with a translate, a high velocity will move the animating element off screen and a lower velocity only moves it a few pixels. It gives you little control over the results, but it can work for a user gesture initiated animation. You could, for example, use it for a momentum based slider.

One important thing to add in case you missed it in the React Native docs, try to add useNativeDriver: true as often as possible. It will greatly improve the smoothness of your animations. As the docs say:

“By using the native driver, we send everything about the animation to native before starting the animation, allowing native code to perform the animation on the UI thread without having to go through the bridge on every frame. Once the animation has started, the JS thread can be blocked without affecting the animation.”

Easing

React native provides you with a list of predefined easing functions, but not much guidance about what to do with them. The easing functions can be wrapped by Easing.in, Easing.out or Easing.inOut, but you can’t use these on their own. Ease-in is the default mode, so you don’t necessarily need to wrap your functions with Easing.in.

{
...animatedTimingOptions,
easing: Easing.in(Easing.quad)
}
// is the same as 
{
...options,
easing: Easing.quad
}

Next to the predefined functions, you can make your own easing function. It is pretty simple if you look at the source code. For example, a cubic easing function can be added as the following, where t is the elapsed time.

{
...animatedTimingOptions,
easing: (t) => t * t * t
}

Or you can use the Cubic bezier like you would in css.

{
...animatedTimingOptions,
easing: Easing.bezier(0.645, 0.045, 0.355, 1)
}

Two of their functions remain a mystery to me though, the step0 and step1 functions. The first one returns 1 for any positive value of n and the second returns 1 if n is greater than or equal to 1. I couldn’t figure out any use case for that, so if you have any idea, I’d really like to hear about it in the comments.

The hidden helper functions

setValue

When a variable has been set with new Animated.Value(), it has the setValue function exposed to it. It gives you control over the value without having to animate it. One of the cases this can be useful for is to set an animating variable to its original value when animating it multiple times.

this.animatedValue = new Animated.Value(0);
this.animatedValue.setValue(1);
this.animatedValue = new Animated.ValueXY({x: 15, y: 30});
this.animatedValue.setValue({x: 0, y: 0});

getTranslateTransform

This method with transform your {x: 10, y: 200} value to a directly usable transform.

this.animatedValue  = new Animated.ValueXY({x: 10, y: 200});
<Animated.Value style={{
transform: this.animatedValue.getTranslateTransform()
}} />
// is short for 
<Animated.View style={{
transform: [
{translateX: this.animatedValue.x},
{translateY: this.animatedValue.y}
]
}} />

stopAnimation

Stops any running animation. It has a callback with the final value after the animation has stopped.

this.animatedValue.stopAnimation((finalValue) => {
console.log('finalValue: '+ finalValue)
})

resetAnimation

Stops any running animation and sets the value back to its original value. Other than that it works the same as stopAnimation.

Conclusion

I hope this makes working with React Native animations a little more clear. Maybe even some more fun. In the end just experimenting with different functions and options will give you the best results. It might take some time to get used to the Animated api, but you will get the hang of it.