Animations are very important to create a great user experience.
+Stationary objects must overcome inertia as they start moving.
+Objects in motion have momentum and rarely come to a stop immediately.
+Animations allow you to convey physically believable motion in your interface.
React Native provides two complementary animation systems:
+Animated for granular and interactive control of specific values, and
+LayoutAnimation for animated global layout transactions.
Animated API #
The Animated API is designed to make it very easy to concisely express a wide variety of interesting animation and interaction patterns in a very performant way.
+Animated focuses on declarative relationships between inputs and outputs, with configurable transforms in between, and simple start/stop methods to control time-based animation execution.
Animated exports four animatable component types: View, Text, Image, and ScrollView, but you can also create your own using Animated.createAnimatedComponent().
For example, a container view that fades in when it is mounted may look like this:
import React, { Component } from 'react';
+import {
+ Animated,
+} from 'react-native';
+
+class FadeInView extends Component {
constructor(props) {
super(props);
this.state = {
- bounceValue: new Animated.Value(0),
- };
- }
- render() {
- return (
- <Animated.Image source={{uri: 'http://i.imgur.com/XMKOH81.jpg'}}
- style={{
- flex: 1,
- transform: [ {scale: this.state.bounceValue}, ]
- }}
- />
- );
+ fadeAnim: new Animated.Value(0), };
}
componentDidMount() {
- this.state.bounceValue.setValue(1.5); Animated.spring( this.state.bounceValue,
bounceValue is initialized as part of state in the constructor, and mapped
-to the scale transform on the image. Behind the scenes, the numeric value is
-extracted and used to set scale. When the component mounts, the scale is set to
-1.5 and then a spring animation is started on bounceValue which will update
-all of its dependent mappings on each frame as the spring animates (in this
-case, just the scale). This is done in an optimized way that is faster than
-calling setState and re-rendering. Because the entire configuration is
-declarative, we will be able to implement further optimizations that serialize
-the configuration and runs the animation on a high-priority thread.
Core API #
Most everything you need hangs directly off the Animated module. This
-includes two value types, Value for single values and ValueXY for vectors,
-three animation types, spring, decay, and timing, and three component
-types, View, Text, and Image. You can make any other component animated with
-Animated.createAnimatedComponent.
The three animation types can be used to create almost any animation curve you
-want because each can be customized:
spring: Simple single-spring physics model that matches Origami.friction: Controls "bounciness"/overshoot. Default 7.tension: Controls speed. Default 40.
decay: Starts with an initial velocity and gradually slows to a stop.velocity: Initial velocity. Required.deceleration: Rate of decay. Default 0.997.
timing: Maps time range to easing value.duration: Length of animation (milliseconds). Default 500.easing: Easing function to define curve. See Easing module for several
-predefined functions. iOS default is Easing.inOut(Easing.ease).delay: Start the animation after delay (milliseconds). Default 0.
Animations are started by calling start. start takes a completion callback
-that will be called when the animation is done. If the animation is done
-because it finished running normally, the completion callback will be invoked
-with {finished: true}, but if the animation is done because stop was called
-on it before it could finish (e.g. because it was interrupted by a gesture or
-another animation), then it will receive {finished: false}.
Composing Animations #
Animations can also be composed with parallel, sequence, stagger, and
-delay, each of which simply take an array of animations to execute and
-automatically calls start/stop as appropriate. For example:
Animated.sequence([
You can then use your FadeInView in place of a View in your components, like so:
render() {
+ return (
+ <FadeInView style={{width: 250, height: 50, backgroundColor: 'powderblue'}}>
+ <Text style={{fontSize: 28, textAlign: 'center', margin: 10}}>Fading in</Text>
+ </FadeInView>
+ )
+}

Let's break down what's happening here.
+In the FadeInView constructor, a new Animated.Value called fadeAnim is initialized as part of state.
+The opacity property on the View is mapped to this animated value.
+Behind the scenes, the numeric value is extracted and used to set opacity.
When the component mounts, the opacity is set to 0.
+Then, an easing animation is started on the fadeAnim animated value,
+which will update all of its dependent mappings (in this case, just the opacity) on each frame as the value animates to the final value of 1.
This is done in an optimized way that is faster than calling setState and re-rendering.
Because the entire configuration is declarative, we will be able to implement further optimizations that serialize the configuration and runs the animation on a high-priority thread.
Configuring animations #
Animations are heavily configurable. Custom and predefined easing functions, delays, durations, decay factors, spring constants, and more can all be tweaked depending on the type of animation.
Animated provides several animation types, the most commonly used one being Animated.timing().
+It supports animating a value over time using one of various predefined easing functions, or you can use your own.
+Easing functions are typically used in animation to convey gradual acceleration and deceleration of objects.
By default, timing will use a easeInOut curve that conveys gradual acceleration to full speed and concludes by gradually decelerating to a stop.
+You can specify a different easing function by passing a easing parameter.
+Custom duration or even a delay before the animation starts is also supported.
For example, if we want to create a 2-second long animation of an object that slightly backs up before moving to its final position:
Animated.timing(
+ this.state.xPosition,
+ {
+ toValue: 100,
+ easing: Easing.back,
+ duration: 2000,
+ }
+).start();
Take a look at the Configuring animations section of the Animated API reference to learn more about all the config parameters supported by the built-in animations.
Composing animations #
Animations can be combined and played in sequence or in parallel.
+Sequential animations can play immediately after the previous animation has finished,
+or they can start after a specified delay.
+The Animated API provides several methods, such as sequence() and delay(),
+each of which simply take an array of animations to execute and automatically calls start()/stop() as needed.
For example, the following animation coasts to a stop, then it springs back while twirling in parallel:
Animated
.sequence([ Animated
.decay(position
, { velocity
: {x
: gestureState
.vx
, y
: gestureState
.vy
}, deceleration
: 0.997,
@@ -71,78 +78,117 @@ automatically calls start/stop as appropriate. For example:
: 360,
}),
]),
-]).start();
By default, if one animation is stopped or interrupted, then all other
-animations in the group are also stopped. Parallel has a stopTogether option
-that can be set to false to disable this.
Interpolation #
Another powerful part of the Animated API is the interpolate function. It
-allows input ranges to map to different output ranges. For example, a simple
-mapping to convert a 0-1 range to a 0-100 range would be
value.interpolate({
+]).start();
If one animation is stopped or interrupted, then all other animations in the group are also stopped.
+Animated.parallel has a stopTogether option that can be set to false to disable this.
You can find a full list of composition methods in the Composing animations section of the Animated API reference.
Combining animated values #
You can combine two animated values via addition, multiplication, division, or modulo to make a new animated value.
There are some cases where an animated value needs to invert another animated value for calculation.
+An example is inverting a scale (2x --> 0.5x):
const a = Animated.Value(1);
+const b = Animated.divide(1, a);
+
+Animated.spring(a, {
+ toValue: 2,
+}).start();
Interpolation #
Each property can be run through an interpolation first.
+An interpolation maps input ranges to output ranges,
+typically using a linear interpolation but also supports easing functions.
+By default, it will extrapolate the curve beyond the ranges given, but you can also have it clamp the output value.
A simple mapping to convert a 0-1 range to a 0-100 range would be:
value.interpolate({
inputRange: [0, 1],
outputRange: [0, 100],
-});
interpolate supports multiple range segments as well, which is handy for
-defining dead zones and other handy tricks. For example, to get an negation
-relationship at -300 that goes to 0 at -100, then back up to 1 at 0, and then
-back down to zero at 100 followed by a dead-zone that remains at 0 for
-everything beyond that, you could do:
value.interpolate({
+});
For example, you may want to think about your Animated.Value as going from 0 to 1,
+but animate the position from 150px to 0px and the opacity from 0 to 1.
+This can easily be done by modifying style from the example above like so:
style={{
+ opacity: this.state.fadeAnim, transform: [{
+ translateY: this.state.fadeAnim.interpolate({
+ inputRange: [0, 1],
+ outputRange: [150, 0] }),
+ }],
+ }}
interpolate() supports multiple range segments as well, which is handy for defining dead zones and other handy tricks.
+For example, to get an negation relationship at -300 that goes to 0 at -100, then back up to 1 at 0, and then back down to zero at 100 followed by a dead-zone that remains at 0 for everything beyond that, you could do:
value.interpolate({
inputRange: [-300, -100, 0, 100, 101],
outputRange: [300, 0, 1, 0, 0],
-});
Which would map like so:
| Input | Output |
|---|
| -400 | 450 |
| -300 | 300 |
| -200 | 150 |
| -100 | 0 |
| -50 | 0.5 |
| 0 | 1 |
| 50 | 0.5 |
| 100 | 0 |
| 101 | 0 |
| 200 | 0 |
interpolate also supports mapping to strings, allowing you to animate colors as well as values with units. For example, if you wanted to animate a rotation you could do:
value.interpolate({
+});
Which would map like so:
Input | Output
+------|-------
+ -400| 450
+ -300| 300
+ -200| 150
+ -100| 0
+ -50| 0.5
+ 0| 1
+ 50| 0.5
+ 100| 0
+ 101| 0
+ 200| 0
interpolate() also supports mapping to strings, allowing you to animate colors as well as values with units. For example, if you wanted to animate a rotation you could do:
value.interpolate({
inputRange: [0, 360],
outputRange: ['0deg', '360deg']
-})
interpolation also supports arbitrary easing functions, many of which are
-already implemented in the
-Easing
-class including quadratic, exponential, and bezier curves as well as functions
-like step and bounce. interpolation also has configurable behavior for
-extrapolating the outputRange. You can set the extrapolation by setting the extrapolate,
-extrapolateLeft or extrapolateRight options. The default value is
-extend but you can use clamp to prevent the output value from exceeding
-outputRange.
Tracking Dynamic Values #
Animated values can also track other values. Just set the toValue of an
-animation to another animated value instead of a plain number, for example with
-spring physics for an interaction like "Chat Heads", or via timing with
-duration: 0 for rigid/instant tracking. They can also be composed with
-interpolations:
Animated.spring(follower, {toValue: leader}).start();
+})
interpolate() also supports arbitrary easing functions, many of which are already implemented in the
+Easing module.
+interpolate() also has configurable behavior for extrapolating the outputRange.
+You can set the extrapolation by setting the extrapolate, extrapolateLeft, or extrapolateRight options.
+The default value is extend but you can use clamp to prevent the output value from exceeding outputRange.
Tracking dynamic values #
Animated values can also track other values.
+Just set the toValue of an animation to another animated value instead of a plain number.
+For example, a "Chat Heads" animation like the one used by Messenger on Android could be implemented with a spring() pinned on another animated value, or with timing() and a duration of 0 for rigid tracking.
+They can also be composed with interpolations:
Animated.spring(follower, {toValue: leader}).start();
Animated.timing(opacity, {
toValue: pan.x.interpolate({
inputRange: [0, 300],
outputRange: [1, 0],
}),
-}).start();
ValueXY is a handy way to deal with 2D interactions, such as panning/dragging.
-It is a simple wrapper that basically just contains two Animated.Value
-instances and some helper functions that call through to them, making ValueXY
-a drop-in replacement for Value in many cases. For example, in the code
-snippet above, leader and follower could both be of type ValueXY and the x
-and y values will both track as you would expect.
Input Events #
Animated.event is the input side of the Animated API, allowing gestures and
-other events to map directly to animated values. This is done with a structured
-map syntax so that values can be extracted from complex event objects. The
-first level is an array to allow mapping across multiple args, and that array
-contains nested objects. In the example, you can see that scrollX maps to
-event.nativeEvent.contentOffset.x (event is normally the first arg to the
-handler), and pan.x and pan.y map to gestureState.dx and gestureState.dy,
-respectively (gestureState is the second arg passed to the PanResponder handler).
onScroll={Animated.event(
- [{nativeEvent: {contentOffset: {x: scrollX}}}]
-)}
-onPanResponderMove={Animated.event([
- null,
The leader and follower animated values would be implemented using Animated.ValueXY().
+ValueXY is a handy way to deal with 2D interactions, such as panning or dragging.
+It is a simple wrapper that basically contains two Animated.Value instances and some helper functions that call through to them,
+making ValueXY a drop-in replacement for Value in many cases.
+It allows us to track both x and y values in the example above.
Tracking gestures #
Gestures, like panning or scrolling, and other events can map directly to animated values using Animated.event.
+This is done with a structured map syntax so that values can be extracted from complex event objects.
+The first level is an array to allow mapping across multiple args, and that array contains nested objects.
For example, when working with horizontal scrolling gestures,
+you would do the following in order to map event.nativeEvent.contentOffset.x to scrollX (an Animated.Value):
onScroll={Animated.event(
+ [{ nativeEvent: {
+ contentOffset: {
+ x: scrollX
+ }
+ }
+ }]
+ )}
When using PanResponder, you could use the following code to extract the x and y positions from gestureState.dx and gestureState.dy.
+We use a null in the first position of the array, as we are only interested in the second argument passed to the PanResponder handler,
+which is the gestureState.
onPanResponderMove={Animated.event(
+ [null, {dx: pan.x, dy: pan.y}
-]);
Responding to the Current Animation Value #
You may notice that there is no obvious way to read the current value while
-animating - this is because the value may only be known in the native runtime
-due to optimizations. If you need to run JavaScript in response to the current
-value, there are two approaches:
spring.stopAnimation(callback) will stop the animation and invoke callback
-with the final value - this is useful when making gesture transitions.spring.addListener(callback) will invoke callback asynchronously while the
-animation is running, providing a recent value. This is useful for triggering
-state changes, for example snapping a bobble to a new option as the user drags
-it closer, because these larger state changes are less sensitive to a few frames
-of lag compared to continuous gestures like panning which need to run at 60fps.
Future Work #
As previously mentioned, we're planning on optimizing Animated under the hood to
-make it even more performant. We would also like to experiment with more
-declarative and higher level gestures and triggers, such as horizontal vs.
-vertical panning.
The above API gives a powerful tool for expressing all sorts of animations in a
-concise, robust, and performant way. Check out more example code in
-UIExplorer/AnimationExample. Of course there may still be times where Animated
-doesn't support what you need, and the following sections cover other animation
-systems.
LayoutAnimation #
LayoutAnimation allows you to globally configure create and update
+])}
Responding to the current animation value #
You may notice that there is no obvious way to read the current value while animating.
+This is because the value may only be known in the native runtime due to optimizations.
+If you need to run JavaScript in response to the current value, there are two approaches:
spring.stopAnimation(callback) will stop the animation and invoke callback with the final value. This is useful when making gesture transitions.spring.addListener(callback) will invoke callback asynchronously while the animation is running, providing a recent value.
+This is useful for triggering state changes,
+for example snapping a bobble to a new option as the user drags it closer,
+because these larger state changes are less sensitive to a few frames of lag compared to continuous gestures like panning which need to run at 60 fps.
Animated is designed to be fully serializable so that animations can be run in a high performance way, independent of the normal JavaScript event loop.
+This does influence the API, so keep that in mind when it seems a little trickier to do something compared to a fully synchronous system.
+Check out Animated.Value.addListener as a way to work around some of these limitations,
+but use it sparingly since it might have performance implications in the future.
Using the native driver #
The Animated API is designed to be serializable.
+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.
Using the native driver for normal animations is quite simple.
+Just add useNativeDriver: true to the animation config when starting it.
Animated.timing(this.state.animatedValue, {
+ toValue: 1,
+ duration: 500,
+ useNativeDriver: true,}).start();
Animated values are only compatible with one driver so if you use native driver when starting an animation on a value,
+make sure every animation on that value also uses the native driver.
The native driver also works with Animated.event.
+This is specially useful for animations that follow the scroll position as without the native driver,
+the animation will always run a frame behind the gesture due to the async nature of React Native.
<Animated.ScrollView scrollEventThrottle={1} onScroll={Animated.event(
+ [{ nativeEvent: { contentOffset: { y: this.state.animatedValue } } }],
+ { useNativeDriver: true } )}
+>
+ {content}
+</Animated.ScrollView>
You can see the native driver in action by running the UIExplorer sample app,
+then loading the Native Animated Example.
+You can also take a look at the source code to learn how these examples were produced.
Caveats #
Not everything you can do with Animated is currently supported by the native driver.
+The main limitation is that you can only animate non-layout properties:
+things like transform, opacity and backgroundColor will work, but flexbox and position properties will not.
+When using Animated.event, it will only work with direct events and not bubbling events.
+This means it does not work with PanResponder but does work with things like ScrollView#onScroll.
Additional examples #
The UIExplorer sample app has various examples of Animated in use:
LayoutAnimation API #
LayoutAnimation allows you to globally configure create and update
animations that will be used for all views in the next render/layout cycle.
This is useful for doing flexbox layout updates without bothering to measure or
calculate specific properties in order to animate them directly, and is
@@ -184,132 +230,12 @@ what you want.
Note that in order to get this to work on Android
}
}
This example uses a preset value, you can customize the animations as
you need, see LayoutAnimation.js
-for more information.
The most obvious way to animate from one value to another is linearly:
-you subtract the end value from the start value and divide the result by
-the number of frames over which the animation occurs, and then add that
-value to the current value on each frame until the end value is reached.
-Linear easing often looks awkward and unnatural, so react-tween-state
-provides a selection of popular easing functions
-that can be applied to make your animations more pleasing.
This library does not ship with React Native - in order to use it on
-your project, you will need to install it with npm i react-tween-state
---save from your project directory.
Here we animated the opacity, but as you might guess, we can animate any
-numeric value. Read more about react-tween-state in its
-README.
Notice that Rebound animations can be interrupted - if you release in
-the middle of a press, it will animate back from the current state to
-the original value.
You can also clamp the spring values so that they don't overshoot and
-oscillate around the end value. In the above example, we would add
-this._scrollSpring.setOvershootClampingEnabled(true) to change this.
-See the below gif for an example of where in your interface you might
-use this.