Animations are no new concept, they have been present for many (many) years. We’ve actually seen them present in flipbooks, cartoons, movies and games. As technology advances more and more each day, animated graphics have become better and better, now giving us the ability to see really impressive works of art in the form of games and movies (“Spider-Man: Into the Spider-Verse”)
But it’s not only games and movies where we get to see cool animations. Now pretty much every app (web or mobile) has them too. If we look closely into the apps we use on a daily basis, we will realize they all have their fair share of animations. It can go from what happens when you like a photo, when you swipe between Instagram stories, when you transition between screens, to really cool animated graphics embedded in them.
Animations have become a really important part in giving people a great user experience when they interact with an app. Not only do they make the UI more attractive, but they can also help us improve the overall experience – such as entertaining the user whenever a long operation is going on. You can use them to make the hiding or showing of our UI elements more smooth and meaningful, as well as showing the user how to use the app (like a mini-onboarding).
Of course, when implementing animations within our app, we have to be careful they don’t affect performance! We don’t want to end up with a pretty cool app that is not functional, is really slow, or lacks usability.
With that said, let’s start diving into the possibilities that React Native offers us to add animations into a mobile app.
To start building an animation, we first need a cool animation idea (or perhaps as in my case, an awesome UX designer, that has them for you), good logic and a choice between using LayoutAnimation or Animated.API
As its name suggests, what layout animation does is animate well, the layout. LayoutAnimations “listens” for any change on an element in the UI. Whether an element is created, updated or deleted, LayoutAnimation will be in charge of animating it to their new position when the next layout happens.
Now how does this magic happen? Let’s take a look at some code.
As we can see in our code below, the dynamic of this component is simple: it renders a red square when our ‘displaySquare’ value is true, and displays nothing when it’s false. Below our square we have a button to toggle with the square being visible. If we forget about LayoutAnimation for a moment, and test our component, we will see the square displays and hides, but it’s not smooth at all.
In order to make our layout changes run more smoothly, the first thing we need to do is import LayoutAnimation from React Native. Then we would need to call the method where all the magic happens:
LayoutAnimation’s more important method is called configureNext, which takes an object of configuration as a first parameter (required). This object can have the following keys: duration, create, update, delete.
These keys are also objects that contain configuration for our animation. As their names suggests, “create” is for when an element is appearing, “update” is for when an element has been modified, and “delete” for when an element no longer exists in our UI. It also accepts a second parameter (optional) which is a “callback” for when the animation has ended, although this is only available for iOS.
What we are doing in our code snippet is basically telling LayoutAnimation, that when an element appears in our UI, it should be animated by its scale. And that when an element is removed from our UI, then it should be animated by its opacity. The duration of each animation would be 800ms. Now let’s see how this looks now:
Notice how the blue button doesn’t move as smoothly as the rest of the elements? Well, if you take a look at the code, I didn’t pass the update key, did I? That means that if an element is updated (like our button) it is not going to be included in the animation.
Now to be able to make this happen, we called configureNext right before our setState method. This is the recommended approach, since most changes on our UI are due to a change in values of our state. You can take a quick look of the complete code in here:
Well, that was pretty simple, right? We do not need to add any kind of logic, or tell the UI what elements to animate (it animates the complete layout). LayoutAnimations also has some default or predefined configuration values called Presets.
As you can see, our animation has turned into only one line, and that linear Preset already has values in it for the duration, create, update and delete keys.
Now as far as limitations go, the first one is that we can’t really decide which element is animated in our layout. So it might be better to create the last small component and only animate that one if possible. Another limitation is that LayoutAnimation only allows you to animate the opacity and scale property of an element (at least for now). Also, be careful with how many times you use or call the configureNext method within your components, or they will override the previous one, thus generating a warning and probably not getting your desired app animation.
Animated.API is one of my favorite ways of animating in React Native. It is very complete, has a bunch of really useful methods, and sometimes requires a bit of logic.
Animated is a complete library exported by React Native that helps us make fluid and powerful animations. It mainly focuses on creating an animated variable (with the help of Animated.Value) and hooking it up to one or more style attributes of an animated component.
When I say “animated components” I mean the components exported by Animated which include: Image, View, ScrollView, Text, FlatList, SectionList . So in order to be able to hook up an animation to a component’s style, instead of using simply I would need to use
If we had a custom component or any other component not included in the above list, we can always use: createAnimatedComponent
Animated also offers methods to control how your values are animated: Animated.timing and Animated.Spring. To fire these animations, both of the methods have functions: .start() and .stop()
In order to compose animations, we have some handy methods like
And to combine animated values we have:
We’ll now take a closer look into each of them.
The animated.timing method basically allows you to define an animation that takes a certain defined amount of time. This method accepts two parameters:
Let’s take a look at some code so we can get a better understanding:
The first thing is to declare in our constructor is the variable that will carry the animated value. For that we use new Animated.Value(0), which initializes our animated value at 0.
Then we hook it up to the element in our component we want to animate (our red square). We do this by tying the transform: translateX to our animated value.
Now, on the button tap event handler, we make the animation happen by calling the start function of Animated.timing. Basically what’s happening is that we are moving the position X of our red square from 0 to 200, this will last 600ms.
Now let’s see the same animation with spring instead of timing. Animated.spring uses a physics model that instead of time (duration) uses speed to make the movement happen.
If we take a look at the code below, you see that it is really similar to the timing method, but instead of having the duration and easing key, we have speed and bounciness (instead of this combination we could’ve use friction/tension). So you’ll see that now our red square is moving but unlike our previous example, there is a rebound effect at the end.
You can see the full code here:
Like I stated before, Animated.API offers methods for both composing and combining values. Let’s say we wanted to animate the logo in our app and have it disappear after it rotates. For that, Animated.sequence comes in handy.
Animated.sequence animates well in a sequence, each animation happens right after the other one has finished.
Animated.parallel on the other hand, fires all animations at the same time (though they might not all end at the same time, that would depend on the duration of each one). Both sequence and parallel receive an array of animations as parameters – that array might include Animated.timing or Animated.spring, or even Animated.parallel or Animated.sequence themselves. It will depend on the logic we have planned for our animation.
Animated.delay takes on a numeric value that means the time in ms that the animation is going to wait (think of it as a setTimeout).
Animated.loop, as its name suggests, loops our animation; meaning it will restart it as soon as it reaches the end. This method receives two parameters: the first one is the animation we want to loop (timing, spring, parallel, sequence) and the second parameter is a configuration object that has an iterations key with the number of times the animation should loop. If we do not pass this second parameter then the method will assume we want to loop it infinitely.
If we wrap multiple animations in a sequence, loop or parallel method, the one in charge of calling start to fire our animation will be the one at the highest level.
Animated.add, substract, multiply and division give us the possibility to do mathematical operations with our animated values (remember they are an instance of Animated.Value).
Let’s take a look at the animation we have on the right. What we want to accomplish is to have two planets: the earth and mars (admittedly, I could have probably picked a better mars image); rotating around one point in the middle.
When my UX designer presented me with this animation idea, I spent several hours trying to come up with how I could make an element rotate within its x, y and z index.
And that’s when it hit me. Sometimes an animation is making the user believe he or she is seeing something, although this might not be exactly what’s going on.
In my UX designer’s perspective, what’s going on is something similar to the image above, but in my perspective it is actually something a bit different. Let me try explaining that better:
First, if we take a close look to what we are trying to accomplish, you see it can be divided in several parts. It starts with a fade in (you see opacity getting clearer), after it’s 100% visible, you see the planets both scaling and rotating. Once they have reached their original position, you see they start rotating (perhaps around the sun) and they do this two times.
This gives us some key words on what Animated methods we can use. Let’s start with the fade in, rotation and scaling of our planets (this will be Phase 1).
First, let’s take a look at the render method in our component:
You can see how we’ve contained both planets in one View, since it will be easier to make that rotation and scaling animation when they enter the screen.
Next, let’s declare all the animation variables we’ll be needing:
Like we explained before, at the beginning of the animation there are some things happening in a sequence.
Now, let’s keep in mind that rotation is a string value indicating degrees. So then how can we make that happen if Animated.Value only takes numeric values? That’s when interpolation comes in handy. If we take a look at the render method code from above we see there’s a call to the .interpolate method from Animated.Value. This allows us to map our range of numeric values (0-1) to a range of values the rotation property can actually read ([‘0deg’ -‘360deg’])
Now let’s put this into code:
There we have “Phase 1” ready!
Now let’s move into “Phase 2”.
Phase 2 is where we trick the eye into making it think the planets are rotating when well, they are not.
As seen in step 1, we have two planets. Now let’s forget about Mars for a while (step 2), and focus on Earth. We see that when the earth is “rotating”, it is going from left to right, but if we only do that, then it’ll be too linear. To achieve that effect, we must play with the scale of our element. Since the translation (left to right) of the earth is a linear movement and we know the duration of it (let’s say 20 seconds), in half that time (10 seconds) the earth would be positioned in the middle of our screen. That’s when it should look bigger (step 3), and then it should go back to its original size when it has reached the left position. That should give the impression of earth coming closer to the screen and then going away.
The same logic applies for Mars (only backwards), so instead of going to the right, it goes from right to left (negatives values). Also, instead of making it bigger when it reaches the middle of the screen, it should be smaller, since it’s supposed to be behind the earth at that point.
Let’s keep in mind two things:
Let’s take a look at some code:
We now have both Earth and Mars exchanging positions and looking like they are coming closer and pulling away. But that’s not it, since they need to return to their original position. But now it will be Mars which is the one getting bigger, and Earth becoming smaller. Let’s take a look at that:
That is basically the same logic from above, but now we are “resetting” their position to 0 so they can be positioned in their original place, and toggling the zIndex again so it returns to starting position.
That will make a complete round of our planets. To make it happen twice, all we need to do is wrap out animations into Animated.loop and have it iterate two times.
The complete code of the component can be found here:
Of course there might be multiple ways to achieve the same animation or things we could add to make this run smoother, there’s always room for improvement! But that should give you a basic idea on how to get started and add some cool animations to your app. There are more things we could do with both Animated and LayoutAnimations, but that will be material for next time!
And finally, these are examples with class components, and yes, we can make the same with hooks, but then again, that will be material for next time!