Animation Types in React Native
In real life, objects usually follow non-linear movements. A ball for example won’t fall at the same speed from the first second to the last, it will change over time.
To mimic real and natural animations in React Native, you need to learn about how they can be configured to get this organic feeling. In today’s chapter, let’s learn animation types.
This was first published as a video for the How To Animated series. If you learn better through audio/image, I highly recommend it.
This article is part of a series. If you are unfamiliar with the basics of animations (Animated.Value
, Animated.timing
), the previous chapter should help you to understand what’s next.
Animation types
Animated.timing
As we’ve seen in the previous chapter, Animated.timing
updates an animated value over time. Of course, there is more to this since you can configure animations for different use cases.
For example, you can decide to delay an animation so that it starts two seconds later:
import React, { useEffect, useRef } from 'react';
import { Animated } from 'react-native';export default () => {
const translation = useRef(
new Animated.Value(0)
).current;
useEffect(() => {
Animated.timing(translation, {
toValue: 100,
delay: 2000,
useNativeDriver: true,
}).start();
}, []);
return (
<Animated.View
style={{
width: 100,
height: 100,
backgroundColor: 'orange',
transform: [{ translateX: translation }],
}}
/>
);
};
You can also set the animation duration, which is quite common, to a given time
Animated.timing(translation, {
toValue: 100,
delay: 2000,
duration: 1000,
useNativeDriver: true,
}).start();
Both delay and duration are in milliseconds so remember that one second is a thousand milliseconds, two seconds is two thousand, etc.
By default, you might have noticed that timed animations accelerate in the beginning and slows down before it ends:
That is because they follow what we call an easing function.
Easing functions
As it was put out on the internet:
Easing functions specify the rate of change of a parameter over time.
This means that given a timeframe you can know how fast or slow a value will change at every single moment.
For example, the default easing function with timed animations is the well-known ease-in-out which slows down the value growth when it starts and also when it ends.
You can compare this to fade-in and fade-out effects in a video when it slowly uncovers an image to then fade out to black:
With an ease-in-out type of easing, your animation value will slowly increase then steadily go towards the target value, to then reach its final value.
Of course, the point of easing functions is that you might want your value to grow differently over time.
React Native comes with a pack of predefined functions that you can import from Easing:
import { Animated, Easing} from 'react-native';
Since the default easing is already an ease-in-out, let’s give our animated object a “drop effect”, using Easing.bounce
for example:
useEffect(() => {
Animated.timing(translation, {
toValue: 100,
easing: Easing.bounce,
useNativeDriver: true,
}).start();
}, []);return (
<Animated.View
style={{
// ...
transform: [{ translateY: translation }],
}}
/>
);
If you’re curious, you should experiment with the different examples and easing types in the documentation.
What matters is that you understand how an animation feeling is correlated to how it moves over time. Pick the right function to bring the right feeling to someone.
Animated.spring
Besides timed animations, you can also find spring ones. Without getting into details too much, a spring animation can be seen as this little cart moving between two springs:
You can tell it isn’t linear at all and have a real organic feeling to it.
Just like timed animations, you can play with different parameters such as the friction, speed, or even bounciness of the movement.
Code-wise, it follows the same API as Animated.timing
, this time using Animated.spring
:
Animated.spring(translation, {
toValue: 100,
useNativeDriver: true,
}).start();
It’s important to note that the animation isn’t timed so you have to change the parameters to either slow the animation or make it faster.
Using Animated.timing
and .spring
, you are able to pretty much reproduce any animation you see in your every-day applications.
Now that you know how to create animated values, how to configure them, you might want to create bigger animations such as a series of them, or two animations rolling together.
So let’s see how you can compose animations.
Animation composition
At some point, you will probably need to run one animation after another or at the same time. To do so, you can use Animated.sequence
and .parallel
which both do exactly what they say!
To animate in sequence, all you need is to pass a list of animations objects to Animated.sequence
:
const translation = useRef(
new Animated.ValueXY({ x: 0, y: 0 })
).current;useEffect(() => {
Animated.sequence([
Animated.spring(translation.x, {
toValue: -100,
useNativeDriver: true,
}),
Animated.spring(translation.y, {
toValue: -100,
useNativeDriver: true,
}),
]).start();
}, []);return (
<Animated.View
style={{
width: 100,
height: 100,
backgroundColor: 'orange',
transform: [
{ translateX: translation.x },
{ translateY: translation.y },
],
}}
/>
);
- Instead of calling
start()
on the animations we had so far, just keep the whole part before since it represents an animation. - Create a couple and pass it to
Animated.sequence
which will run each one in a sequence. - Then, you still need to call
start
but this time on the sequence you have just created.
Animated.parallel
works just the same to run multiple animations all at once:
Animated.parallel([
Animated.spring(translation.x, {
toValue: -100,
useNativeDriver: true,
}),
Animated.spring(translation.y, {
toValue: -100,
useNativeDriver: true,
}),
]).start();
You can actually combine sequenced and parallel animations together:
Animated.sequence([
Animated.spring(translation.x, {
toValue: -100,
useNativeDriver: true,
}),
Animated.parallel([
Animated.spring(translation.x, {
toValue: 100,
useNativeDriver: true,
}),
Animated.spring(translation.y, {
toValue: -100,
useNativeDriver: true,
}),
]),
]).start();
One last interesting way of composing animations is by staggering them. The idea is to run one animation after the other while pausing X milliseconds between each.
So, if you have a series of blocks that you want to nicely fade-in from top to bottom, you could use Animated.stagger
which we’ll try right now.
I will just create three blocks, and each one will have its animated value to change its opacity one by one. We’ll start at zero since we want to hide them first, and then update them to one so they show up one after the other:
export default () => {
const firstOpacity = useRef(
new Animated.Value(0)
).current;
const secondOpacity = useRef(
new Animated.Value(0)
).current;
const thirdOpacity = useRef(
new Animated.Value(0)
).current;
return (
<>
<Animated.View
style={{
width: 100,
height: 100,
backgroundColor: 'orange',
marginBottom: 10,
opacity: firstOpacity,
}}
/>
<Animated.View
style={{
width: 100,
height: 100,
backgroundColor: 'orange',
marginBottom: 10,
opacity: secondOpacity,
}}
/>
<Animated.View
style={{
width: 100,
height: 100,
backgroundColor: 'orange',
opacity: thirdOpacity,
}}
/>
</>
);
};
At this point, I will start with a one-second staggering effect, which will run each animation after one second:
useEffect(() => {
Animated.stagger(1000, [
Animated.timing(firstOpacity, {
toValue: 1,
useNativeDriver: true,
}),
Animated.timing(secondOpacity, {
toValue: 1,
useNativeDriver: true,
}),
Animated.timing(thirdOpacity, {
toValue: 1,
useNativeDriver: true,
}),
]).start();
}, []);
If I lower that one second from 1000 to 125 milliseconds, the animation will be much smoother:
Animated.stagger(125, [
Pretty simple, isn’t it? Okay, now that you know about different types of animations and how to compose them, let’s wrap this up.
Recap
If there are three things to remember today, here they are:
- There are different types of animations with Animated, timed and spring being the ones you will need for most use cases. Both can be delayed using the
delay
property, and timed animations can follow a fixedduration
if given. - These same timed animations use easing functions to know how to animate a value over time. There is a long list of easing functions you can choose from, each one bringing a different touch.
- Animations can be easily composed, using Animated sequence, parallel, and stagger. All of them can be combined so you can create very complex animations.
Alright, that’s it for now. In the next chapter, we will talk about interpolation, which turns out to be very useful in React Native animations.
New chapters will be released on a weekly-basis. Remember to follow me if you don’t want to miss any.