Animations are an important part of modern UX, and the Animated
library is designed to make them fluid, powerful, and easy to build and
maintain.
The simplest workflow is to create an Animated.Value
, hook it up to one or
more style attributes of an animated component, and then drive updates either
via animations, such as Animated.timing
, or by hooking into gestures like
panning or scrolling via Animated.event
. Animated.Value
can also bind to
props other than style, and can be interpolated as well. Here is a basic
example of a container view that will fade in when it's mounted:
class FadeInView extends React.Component {
constructor(props) {
super(props);
this.state = {
fadeAnim: new Animated.Value(0), };
}
componentDidMount() {
Animated.timing( this.state.fadeAnim, {toValue: 1}, ).start(); }
render() {
return (
<Animated.View style={{opacity: this.state.fadeAnim}}> {this.props.children}
</Animated.View>
);
}
}
Note that only animatable components can be animated. View
, Text
, and
Image
are already provided, and you can create custom ones with
createAnimatedComponent
. These special components do the magic of binding
the animated values to the properties, and do targeted native updates to
avoid the cost of the react render and reconciliation process on every frame.
They also handle cleanup on unmount so they are safe by default.
Animations are heavily configurable. Custom and pre-defined easing
functions, delays, durations, decay factors, spring constants, and more can
all be tweaked depending on the type of animation.
A single Animated.Value
can drive any number of properties, and 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.
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
in the example above like so:
style={{
opacity: this.state.fadeAnim, transform: [{
translateY: this.state.fadeAnim.interpolate({
inputRange: [0, 1],
outputRange: [150, 0] }),
}],
}}>
Animations can also be combined in complex ways using composition functions
such as sequence
and parallel
, and can also be chained together simply
by setting the toValue
of one animation to be another Animated.Value
.
Animated.ValueXY
is handy for 2D animations, like panning, and there are
other helpful additions like setOffset
and getLayout
to aid with typical
interaction patterns, like drag-and-drop.
You can see more example usage in AnimationExample.js
, the Gratuitous
Animation App, and Animations documentation guide.
Note that 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.
Checkout 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.
Methods #
static decay(value: AnimatedValue | AnimatedValueXY, config: DecayAnimationConfig) #
Animates a value from an initial velocity to zero based on a decay
coefficient.
static timing(value: AnimatedValue | AnimatedValueXY, config: TimingAnimationConfig) #
Animates a value along a timed easing curve. The Easing
module has tons
of pre-defined curves, or you can use your own function.
static spring(value: AnimatedValue | AnimatedValueXY, config: SpringAnimationConfig) #
Spring animation based on Rebound and Origami. Tracks velocity state to
create fluid motions as the toValue
updates, and can be chained together.
static add(a: Animated, b: Animated) #
Creates a new Animated value composed from two Animated values added
together.
static multiply(a: Animated, b: Animated) #
Creates a new Animated value composed from two Animated values multiplied
together.
static delay(time: number) #
Starts an animation after the given delay.
static sequence(animations: Array<CompositeAnimation>) #
Starts an array of animations in order, waiting for each to complete
before starting the next. If the current running animation is stopped, no
following animations will be started.
static parallel(animations: Array<CompositeAnimation>, config?: ParallelConfig) #
Starts an array of animations all at the same time. By default, if one
of the animations is stopped, they will all be stopped. You can override
this with the stopTogether
flag.
static stagger(time: number, animations: Array<CompositeAnimation>) #
Array of animations may run in parallel (overlap), but are started in
sequence with successive delays. Nice for doing trailing effects.
static event(argMapping: Array<Mapping>, config?: EventConfig) #
Takes an array of mappings and extracts values from each arg accordingly,
then calls setValue
on the mapped outputs. e.g.
onScroll={Animated.event(
[{nativeEvent: {contentOffset: {x: this._scrollX}}}]
{listener}, )
...
onPanResponderMove: Animated.event([
null, {dx: this._panX}, ]),
static createAnimatedComponent(Component: any) #
Make any React component Animatable. Used to create Animated.View
, etc.
Properties #
Value: AnimatedValue #
Standard value class for driving animations. Typically initialized with
new Animated.Value(0);
ValueXY: AnimatedValueXY #
2D value class for driving 2D animations, such as pan gestures.
class AnimatedValue #
Standard value for driving animations. One Animated.Value
can drive
multiple properties in a synchronized fashion, but can only be driven by one
mechanism at a time. Using a new mechanism (e.g. starting a new animation,
or calling setValue
) will stop any previous ones.
Methods #
constructor(value: number) #
setValue(value: number) #
Directly set the value. This will stop any animations running on the value
and update all the bound properties.
setOffset(offset: number) #
Sets an offset that is applied on top of whatever value is set, whether via
setValue
, an animation, or Animated.event
. Useful for compensating
things like the start of a pan gesture.
flattenOffset() #
Merges the offset value into the base value and resets the offset to zero.
The final output of the value is unchanged.
addListener(callback: ValueListenerCallback) #
Adds an asynchronous listener to the value so you can observe updates from
animations or whathaveyou. This is useful because there is no way to
synchronously read the value because it might be driven natively.
removeListener(id: string) #
stopAnimation(callback?: ?(value: number) => void) #
Stops any running animation or tracking. callback
is invoked with the
final value after stopping the animation, which is useful for updating
state to match the animation position with layout.
interpolate(config: InterpolationConfigType) #
Interpolates the value before updating the property, e.g. mapping 0-1 to
0-10.
animate(animation: Animation, callback: EndCallback) #
Typically only used internally, but could be used by a custom Animation
class.
stopTracking() #
Typically only used internally.
track(tracking: Animated) #
Typically only used internally.
class AnimatedValueXY #
2D Value for driving 2D animations, such as pan gestures. Almost identical
API to normal Animated.Value
, but multiplexed. Contains two regular
Animated.Value
s under the hood. Example:
class DraggableView extends React.Component {
constructor(props) {
super(props);
this.state = {
pan: new Animated.ValueXY(), };
this.state.panResponder = PanResponder.create({
onStartShouldSetPanResponder: () => true,
onPanResponderMove: Animated.event([null, {
dx: this.state.pan.x, dy: this.state.pan.y,
}]),
onPanResponderRelease: () => {
Animated.spring(
this.state.pan, {toValue: {x: 0, y: 0}} ).start();
},
});
}
render() {
return (
<Animated.View
{...this.state.panResponder.panHandlers}
style={this.state.pan.getLayout()}>
{this.props.children}
</Animated.View>
);
}
}
Methods #
constructor(valueIn?: ?{x: number | AnimatedValue; y: number | AnimatedValue}) #
setValue(value: {x: number; y: number}) #
setOffset(offset: {x: number; y: number}) #
stopAnimation(callback?: ?() => number) #
addListener(callback: ValueXYListenerCallback) #
removeListener(id: string) #
getLayout() #
Converts {x, y}
into {left, top}
for use in style, e.g.
style={this.state.anim.getLayout()}
getTranslateTransform() #
Converts {x, y}
into a useable translation transform, e.g.
style={{
transform: this.state.anim.getTranslateTransform()
}}
'use strict';
var React = require('react-native');
var {
Animated,
Easing,
StyleSheet,
Text,
View,
} = React;
var UIExplorerButton = require('./UIExplorerButton');
exports.framework = 'React';
exports.title = 'Animated - Examples';
exports.description = 'Animated provides a powerful ' +
'and easy-to-use API for building modern, ' +
'interactive user experiences.';
exports.examples = [
{
title: 'FadeInView',
description: 'Uses a simple timing animation to ' +
'bring opacity from 0 to 1 when the component ' +
'mounts.',
render: function() {
class FadeInView extends React.Component {
constructor(props) {
super(props);
this.state = {
fadeAnim: new Animated.Value(0), };
}
componentDidMount() {
Animated.timing( this.state.fadeAnim, {
toValue: 1, duration: 2000, },
).start(); }
render() {
return (
<Animated.View style={{
opacity: this.state.fadeAnim, }}>
{this.props.children}
</Animated.View>
);
}
}
class FadeInExample extends React.Component {
constructor(props) {
super(props);
this.state = {
show: true,
};
}
render() {
return (
<View>
<UIExplorerButton onPress={() => {
this.setState((state) => (
{show: !state.show}
));
}}>
Press to {this.state.show ?
'Hide' : 'Show'}
</UIExplorerButton>
{this.state.show && <FadeInView>
<View style={styles.content}>
<Text>FadeInView</Text>
</View>
</FadeInView>}
</View>
);
}
}
return <FadeInExample />;
},
},
{
title: 'Transform Bounce',
description: 'One `Animated.Value` is driven by a ' +
'spring with custom constants and mapped to an ' +
'ordered set of transforms. Each transform has ' +
'an interpolation to convert the value into the ' +
'right range and units.',
render: function() {
this.anim = this.anim || new Animated.Value(0);
return (
<View>
<UIExplorerButton onPress={() => {
Animated.spring(this.anim, {
toValue: 0, velocity: 3, tension: -10, friction: 1, }).start(); }}>
Press to Fling it!
</UIExplorerButton>
<Animated.View
style={[styles.content, {
transform: [ {scale: this.anim.interpolate({
inputRange: [0, 1],
outputRange: [1, 4],
})},
{translateX: this.anim.interpolate({
inputRange: [0, 1],
outputRange: [0, 500],
})},
{rotate: this.anim.interpolate({
inputRange: [0, 1],
outputRange: [
'0deg', '360deg' ],
})},
]}
]}>
<Text>Transforms!</Text>
</Animated.View>
</View>
);
},
},
{
title: 'Composite Animations with Easing',
description: 'Sequence, parallel, delay, and ' +
'stagger with different easing functions.',
render: function() {
this.anims = this.anims || [1,2,3].map(
() => new Animated.Value(0)
);
return (
<View>
<UIExplorerButton onPress={() => {
var timing = Animated.timing;
Animated.sequence([ timing(this.anims[0], {
toValue: 200,
easing: Easing.linear,
}),
Animated.delay(400), timing(this.anims[0], {
toValue: 0,
easing: Easing.elastic(2), }),
Animated.delay(400),
Animated.stagger(200,
this.anims.map((anim) => timing(
anim, {toValue: 200}
)).concat(
this.anims.map((anim) => timing(
anim, {toValue: 0}
))),
),
Animated.delay(400),
Animated.parallel([
Easing.inOut(Easing.quad), Easing.back(1.5), Easing.ease ].map((easing, ii) => (
timing(this.anims[ii], {
toValue: 320, easing, duration: 3000,
})
))),
Animated.delay(400),
Animated.stagger(200,
this.anims.map((anim) => timing(anim, {
toValue: 0,
easing: Easing.bounce, duration: 2000,
})),
),
]).start(); }}>
Press to Animate
</UIExplorerButton>
{['Composite', 'Easing', 'Animations!'].map(
(text, ii) => (
<Animated.View
key={text}
style={[styles.content, {
left: this.anims[ii]
}]}>
<Text>{text}</Text>
</Animated.View>
)
)}
</View>
);
},
},
{
title: 'Continuous Interactions',
description: 'Gesture events, chaining, 2D ' +
'values, interrupting and transitioning ' +
'animations, etc.',
render: () => (
<Text>Checkout the Gratuitous Animation App!</Text>
),
}
];
var styles = StyleSheet.create({
content: {
backgroundColor: 'deepskyblue',
borderWidth: 1,
borderColor: 'dodgerblue',
padding: 20,
margin: 20,
borderRadius: 10,
alignItems: 'center',
},
});