From 62034ca2385755aa6dc58a5d122736a44eb40c4f Mon Sep 17 00:00:00 2001 From: Travis CI Date: Thu, 9 Jul 2015 12:53:31 +0000 Subject: [PATCH] update website --- docs/animations.html | 225 +++++++++++++++++++++++++++++++++++-------- 1 file changed, 184 insertions(+), 41 deletions(-) diff --git a/docs/animations.html b/docs/animations.html index f0e4ef894c2..3194051bdf6 100644 --- a/docs/animations.html +++ b/docs/animations.html @@ -1,11 +1,185 @@ -React Native | A framework for building native apps using React

Animations

Fluid, meaningful animations are essential to the mobile user -experience. Animation APIs for React Native are currently under heavy -development, the recommendations in this article are intended to be up -to date with the current best-practices.

requestAnimationFrame #

requestAnimationFrame is a polyfill from the browser that you might be +React Native | A framework for building native apps using React

Animations

Fluid, meaningful animations are essential to the mobile user experience. Like +everything in React Native, Animation APIs for React Native are currently under +development, but have started to coalesce around two complementary systems: +LayoutAnimation for animated global layout transactions, and Animated for +more granular and interactive control of specific values.

Animated #

The Animated library 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. For example, a complete +component with a simple spring bounce on mount looks like this:

class Playground extends React.Component { + constructor(props: any) { + super(props); + this.state = { + bounceValue: new Animated.Value(0), + }; + } + render(): ReactElement { + return ( + <Animated.Image // Base: Image, Text, View + source={{uri: 'http://i.imgur.com/XMKOH81.jpg'}} + style={{ + flex: 1, + transform: [ // `transform` is an ordered array + {scale: this.state.bounceValue}, // Map `bounceValue` to `scale` + ] + }} + /> + ); + } + componentDidMount() { + this.state.bounceValue.setValue(1.5); // Start large + Animated.spring( // Base: spring, decay, timing + this.state.bounceValue, // Animate `bounceValue` + { + toValue: 0.8, // Animate to smaller size + friction: 1, // Bouncier spring + } + ).start(); // Start the animation + } +}

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([ // spring to start and twirl after decay finishes + Animated.decay(position, { // coast to a stop + velocity: {x: gestureState.vx, y: gestureState.vy}, // velocity from gesture release + deceleration: 0.997, + }), + Animated.parallel([ // after decay, in parallel: + Animated.spring(position, { + toValue: {x: 0, y: 0} // return to start + }), + Animated.timing(twirl, { // and twirl + toValue: 360, + }), + ]), +]).start(); // start the sequence group

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({ + 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({ + inputRange: [-300, -100, 0, 100, 101], + outputRange: [300, 0, 1, 0, 0], +});

Which would map like so:

InputOutput
-400450
-300300
-200150
-1000
-500.5
01
500.5
1000
1010
2000

interpolation also supports arbitrary easing functions, many of which are +already implemented in the +Easing +class including quadradic, exponential, and bezier curves as well as functions +like step and bounce. interpolation also has configurable behavior for +extrapolation, the default being 'extend', but 'clamp' is also very useful +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(); +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 maps to gestureState.dx (gestureState is the second +arg passed to the PanResponder handler).

onScroll={Animated.event( + [{nativeEvent: {contentOffset: {y: pan.y}}}] // pan.y = e.nativeEvent.contentOffset.y +)} +onPanResponderMove={Animated.event([ + null, // ignore the native event + {dx: pan.x, dy: pan.y} // extract dx and dy from gestureState +]);

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 +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 +especially useful when layout changes may affect ancestors, for example a "see +more" expansion that also increases the size of the parent and pushes down the +row below which would otherwise require explicit coordination between the +components in order to animate them all in sync.

Note that although LayoutAnimation is very powerful and can be quite useful, +it provides much less control than Animated and other animation libraries, so +you may need to use another approach if you can't get LayoutAnimation to do +what you want.

var App = React.createClass({ + componentWillMount() { + // Animate creation + LayoutAnimation.spring(); + }, + + getInitialState() { + return { w: 100, h: 100 } + }, + + _onPress() { + // Animate the update + LayoutAnimation.spring(); + this.setState({w: this.state.w + 15, h: this.state.h + 15}) + }, + + render: function() { + return ( + <View style={styles.container}> + <View style={[styles.box, {width: this.state.w, height: this.state.h}]} /> + <TouchableOpacity onPress={this._onPress}> + <View style={styles.button}> + <Text style={styles.buttonText}>Press me!</Text> + </View> + </TouchableOpacity> + </View> + ); + } +});

Run this example

This example uses a preset value, you can customize the animations as +you need, see LayoutAnimation.js +for more information.

requestAnimationFrame #

requestAnimationFrame is a polyfill from the browser that you might be familiar with. It accepts a function as its only argument and calls that function before the next repaint. It is an essential building block for -animations that underlies all of the JavaScript-based animation APIs.

JavaScript-based Animation APIs #

These APIs do all of the calculations in JavaScript, then send over -updated properties to the native side on each frame.

react-tween-state #

react-tween-state is a +animations that underlies all of the JavaScript-based animation APIs. In +general, you shouldn't need to call this yourself - the animation API's will +manage frame updates for you.

react-tween-state #

react-tween-state is a minimal library that does exactly what its name suggests: it tweens a value in a component's state, starting at a from value and ending at a to value. This means that it generates the values in between those @@ -51,7 +225,7 @@ your project, you will need to install it with npm i react-tween-state }, });

Run this example

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.

Rebound #

Rebound.js is a JavaScript port of +README.

Rebound #

Rebound.js is a JavaScript port of Rebound for Android. It is similar in concept to react-tween-state: you have an initial value and set an end value, then Rebound generates intermediate values that you can @@ -160,7 +334,7 @@ computationally intensive work until after animations are complete, using the InteractionManager. You can monitor the frame rate by using the In-App Developer Menu "FPS -Monitor" tool.

Navigator Scene Transitions #

As mentioned in the Navigator +Monitor" tool.

Navigator Scene Transitions #

As mentioned in the Navigator Comparison, Navigator is implemented in JavaScript and NavigatorIOS is a wrapper around native functionality provided by UINavigationController, so @@ -188,38 +362,7 @@ make them customizable, React Native exposes a pop: CustomLeftToRightGesture, } });

Run this example

For further information about customizing scene transitions, read the -source.

Native-based Animation APIs #

LayoutAnimation #

LayoutAnimation allows you to globally configure create and update -animations that will be used for all views in the next render cycle.

var App = React.createClass({ - componentWillMount() { - // Animate creation - LayoutAnimation.configureNext(LayoutAnimation.Presets.spring); - }, - - getInitialState() { - return { w: 100, h: 100 } - }, - - _onPress() { - // Animate the update - LayoutAnimation.configureNext(LayoutAnimation.Presets.spring); - this.setState({w: this.state.w + 15, h: this.state.h + 15}) - }, - - render: function() { - return ( - <View style={styles.container}> - <View style={[styles.box, {width: this.state.w, height: this.state.h}]} /> - <TouchableOpacity onPress={this._onPress}> - <View style={styles.button}> - <Text style={styles.buttonText}>Press me!</Text> - </View> - </TouchableOpacity> - </View> - ); - } -});

Run this example

This example uses a preset value, you can customize the animations as -you need, see LayoutAnimation.js -for more information.

AnimationExperimental (Deprecated) #

As the name would suggest, this was only ever an experimental API and it +source.

AnimationExperimental (Deprecated) #

As the name would suggest, this was only ever an experimental API and it is not recommended to use this on your apps. It has some rough edges and is not under active development. It is built on top of CoreAnimation explicit animations.

If you choose to use it anyways, here is what you need to know:

  • You will need to include RCTAnimationExperimental.xcodeproj and add @@ -258,7 +401,7 @@ animate opacity from 1 to 0.5:

    : 1, toValue: 0.5, }, -);

    Pop (Unsupported, not recommended) #

    Facebook Pop "supports spring and +);

Pop (Unsupported, not recommended) #

Facebook Pop "supports spring and decay dynamic animations, making it useful for building realistic, physics-based interactions."

This is not officially supported or recommended because the direction is to move towards JavaScript-driven animations, but if you must use it,