The building blocks of React Native animations

08 Mar 2017

Cover image: The building blocks of React Native animations

It is hard to imagine a mobile app that does not involve animations. Whereas on the web, animations are typically pretty simple, if existent at all — a new page can replace the existing one; mobile applications demand a different level of attention.

CSS3 has a relatively simple API that can let you make some pretty basic animations. But you don't have that on React Native. It probably wouldn't be enough either way.

So, if you work with React Native, and need to implement meaningful animations, what do you need to know?

React Native has Animated, but it does look scary... at least at first.

So here are three "clusters of things" or "blocks," if you will, that I think of when working with animations.

Block 0: Need for change

Animations represent a transition from one state to another:

  • From being hidden to being shown.
  • From a circle to a square.

Think what needs to be shown both before and after an animation.

Consider how the transition should go, and think of which styles need to change for that:

  • Should a thing appear by changing opacity, or should it "fall down" from the top?
  • Should a circle just morph into a square, or should it become a triangle mid-way?

Block 1: Visual state aka Animated.Value

Logically, some component can be either shown or hidden — true or false; it can't really be somewhere halfway. In your state, the status of whether something is shown will be either true or false.

In a good UI, things aren't hidden right now and suddenly shown the next moment. They "appear" gradually, to help the user make sense of the interface.

So it's clear the visual state can be somewhere between just true and false.

How come?

We can introduce another variable to represent the visual state of the slider. We want it to be a number because no matter what the logical state is, numbers allow us to represent the states in-between.

this._shown = new Animated.Value(0);

While the logical state can be binary (i.e. either true or false, 1 or 0), the visual state is floating-point.

Block 2: Transitions aka Animated.timing

Say some component is hidden: it means the logical state of its visibility will be false, and visual will be 0.0 too. But what happens as we want to show the component? The logical state should immediately become a true, whereas the visual state should go to 0.1, 0.2, ... and all the way to 1.0.

For that, we need a way to tell the visual state to go to 1.0.

And there is. Actually, a few.

The simplest way to do it is basically:

Animated.timing(this._shown, {
  toValue: 1,
  duration: 300,
}).start();

Here, we tell Animated to change _shown up to 1.0 in the span of 300ms.

There are other transitions and ways to orchestrate multiple transitions, but we can keep it to Animated.timing for now.

Block 3: The Pixels aka Animated.View and interpolate

Our transitions of _shown between 0.0 and 1.0 are all for nothing is we can't see them. So how can we see them?

We should somehow be able to use _shown to set the opacity of the child component.

Suppose we had this code before we attempting with animations:

<View style={{ opacity: this.state.shown ? 1 : 0 }}>
  <SomeComponent />
</View>

We were setting the opacity to 0 when hidden, and 1 when shown.

Can we use this Animated.Value we have, _shown, to animate it between 0 and 1?

Animate the styles

We can use any animated value to influence the style.

We just have to change View for Animated.View, and now we can have this:

const opacity = this._shown; // This is an Animated.Value
<Animated.View style={{ opacity: opacity }}>
  <SomeComponent />
</Animated.View>;

Isn't this a post on ANIMATIONS? How come there hasn't been a single picture up until now?

One more thing: interpolation

This may sound like a scary word, but the idea is pretty simple: it allows us to keep the visual state between 0 and 1, but kind of "map" to something else, should we need to.

Say instead of just making a child component appear; we wanted also to make it "fall down" from the top a bit. And we would do that by putting the hidden component 40px higher, and animate to its normal position as we make it visible.

We can "map" our 0-to-1 values to minus 40-to-0 using a simple interpolate call:

const top = this._shown.interpolate({
  inputRange: [0, 1],
  outputRange: [-40, 0],
});

This will create a new animated value that will be between -40 and 0.

In other words, it will be -40 when _shown is 0, it will be -20 when _shown is 0.5, and it will be 0 when _shown is 1.0.

Dark secret: you can also output colors and degrees from interpolate.

Recap

  • Visual state is numeric, as it needs to be able to represent a transition from one style to another.
  • Animated.Value allows representing numeric visual state.
  • Animated.timing can be used to transition an Animated.Value to another number.
  • Animated.Value can be used for styling if you swap View with Animated.View.
  • Interpolations allow to "map" one range of Animated.Value into another: e.g. go from "between 0 and 1" to "between 5 and 25", or even to "between green and black."

To help, I've prepared a cheatsheet featuring the building blocks along with quick and to-the-point descriptions:

Resources

This is meant as a quick introduction to the animation primitives in React Native to give a basic idea. Here are additional useful resources that go deeper:

Think your friends would dig this article, too?

Google+
If you need a mobile app built for your business or your idea, there's a chance I could help you with that.
Leave your email here and I will get back to you shortly.