React Native

React-native Animated API Basic Example

#Introduction

Animations are finally solved in React? That's a bold claim, but lets explore the new Animated API in react-native. This won't apply to React for the web, however there is also react-motion also released at react-europe.

{% img http://i.imgur.com/JlX4nV0.gif Final Animation Result %}

<!-- more -->

#Resources

#How It Works

The Animated API does not depend on calling setState, it is accomplished by calling setNativeProps. The Animated API exports a few components Animated.View, Animated.Text, and Animated.Image. The Animated API will adjust the components in the native Objective-C world. This will bypass the diff and reconciliation in the JS world so you get fluent, and performance animatons. Ultimately all you need to know is that it will interpolate numbers and update the native view components.

#Cool Examples

Simple Move Around the Screen Example

We are just going to move a square around the edges of the screen.

<--<           
|  |          
V--^         

Setup

Dependencies

var React = require('react-native');
var Dimensions = require('Dimensions');

var {
  width,
  height
} = Dimensions.get('window');

var {
  AppRegistry,
  StyleSheet,
  View,
  Animated
} = React;

var SQUARE_DIMENSIONS = 30;

Styles

var styles = StyleSheet.create({
  container: {
    flex: 1
  },
  square: {
    width: SQUARE_DIMENSIONS,
    height: SQUARE_DIMENSIONS,
    backgroundColor: 'blue'
  } 
});

Basic Code

var AnimatedSquare = React.createClass({
  getInitialState: function() {
    return {
        pan: new Animated.ValueXY()
    };
  },
  getStyle: function() {
    return [
              styles.square, 
              {
                transform: this.state.pan.getTranslateTransform()
              }
            ];
  },
  render: function() {
    return (
      <View style={styles.container}>
        <Animated.View style={this.getStyle()} />
      </View>
    );
  }
});

Few things to call out here.

Notice our state that is created is an instantiation of Animated.ValueXY. This will save us some code, and let the Animated API take care of interpolating both our X, and Y values.

Our getStyle will return an array, our base square class and a transform. Once again we'll use a the getTranslateTransform helper from the Animated API to return the appropriate structure for the transform style.

It returns [{ translateX: xValue}, {translateY: yValue}], where xValue and yValue are the interpolated values from the Animated.ValueXY we set on our pan state variable.

Finally we will use the Animated.View which is a convenience element to say "Hey React this is going to be an animated thing".

Move It

We're now going to move it from the top corner. x = 0, y = 0 to the bottom left corner x = 0, y = (phoneHeight - square Height).

var SPRING_CONFIG = {tension: 2, friction: 3}; //Soft spring

//...
  componentDidMount: function() {
    Animated.spring(this.state.pan, {
          ...SPRING_CONFIG,
          toValue: {x: 0, y: height - SQUARE_DIMENSIONS}                        // return to start
    }).start();
  },

So on mount we'll start a spring. This will animate the this.state.pan to the bottom left corner like we want it to.

We'll also setup the SPRING_CONFIG with a soft spring, not much tension or friction. So it'll get to the corner and just bounce a little bit and stay there.

Move It, Move It, and Move It again

We can queue up sequences of animations. These will happen one after the other. The sequence call is one of the means of composing animations. There is also parallel which allows for declaration of animations to happen at the same time.

  componentDidMount: function() {
    Animated.sequence([
      Animated.spring(this.state.pan, {
            ...SPRING_CONFIG,
            toValue: {x: 0, y: height - SQUARE_DIMENSIONS} //animate to bottom left
      }),
      Animated.spring(this.state.pan, {
          ...SPRING_CONFIG,
          toValue: {x: width - SQUARE_DIMENSIONS, y: height - SQUARE_DIMENSIONS} // animated to bottom right
      }),
      Animated.spring(this.state.pan, {
            ...SPRING_CONFIG,
            toValue: {x: width - SQUARE_DIMENSIONS, y: 0} //animate to top right
      }),
      Animated.spring(this.state.pan, {
          ...SPRING_CONFIG,
          toValue: {x: 0, y: 0} // return to start
      })
    ]).start();
  }

We define 4 spring configrations like discussed before. The comments in the code explain each movement.

Move and Repeat

The call to start takes a callback. This callback will be invoked once the animation is completed. In our case the animation is complete once we get back to the start. We can then restart the animation.

  componentDidMount: function() {
    this.startAndRepeat();
  },
  startAndRepeat: function() {
    this.triggerAnimation(this.startAndRepeat);
  },
  triggerAnimation: function(cb) {
    Animated.sequence([
      Animated.spring(this.state.pan, {
            ...SPRING_CONFIG,
            toValue: {x: 0, y: height - SQUARE_DIMENSIONS} //animate to bottom left
      }),
      Animated.spring(this.state.pan, {
          ...SPRING_CONFIG,
          toValue: {x: width - SQUARE_DIMENSIONS, y: height - SQUARE_DIMENSIONS} // animated to bottom right
      }),
      Animated.spring(this.state.pan, {
            ...SPRING_CONFIG,
            toValue: {x: width - SQUARE_DIMENSIONS, y: 0} //animate to top right
      }),
      Animated.spring(this.state.pan, {
          ...SPRING_CONFIG,
          toValue: {x: 0, y: 0} // return to start
      })
    ]).start(cb);
  }

We just make a call that triggers the animation and calls itself on complete.

#Full/Live Code

https://rnplay.org/apps/QlPJ2Q

var React = require('react-native');
var Dimensions = require('Dimensions');

var {
  width,
  height
} = Dimensions.get('window');

var {
  AppRegistry,
  StyleSheet,
  View,
  Animated
} = React;

var SQUARE_DIMENSIONS = 30;
var SPRING_CONFIG = {tension: 2, friction: 3}; //Soft spring

var AnimatedSquare = React.createClass({
  getInitialState: function() {
    return {
        pan: new Animated.ValueXY()
    };
  },
  componentDidMount: function() {
    this.startAndRepeat();
  },
  startAndRepeat: function() {
    this.triggerAnimation(this.startAndRepeat);
  },
  triggerAnimation: function(cb) {
    Animated.sequence([
      Animated.spring(this.state.pan, {
            ...SPRING_CONFIG,
            toValue: {x: 0, y: height - SQUARE_DIMENSIONS} //animate to bottom left
      }),
      Animated.spring(this.state.pan, {
          ...SPRING_CONFIG,
          toValue: {x: width - SQUARE_DIMENSIONS, y: height - SQUARE_DIMENSIONS} // animated to bottom right
      }),
      Animated.spring(this.state.pan, {
            ...SPRING_CONFIG,
            toValue: {x: width - SQUARE_DIMENSIONS, y: 0} //animate to top right
      }),
      Animated.spring(this.state.pan, {
          ...SPRING_CONFIG,
          toValue: {x: 0, y: 0} // return to start
      })
    ]).start(cb);
  },
  getStyle: function() {
    return [
              styles.square, 
              {
                transform: this.state.pan.getTranslateTransform()
              }
            ];
  },
  render: function() {
    return (
      <View style={styles.container}>
        <Animated.View style={this.getStyle()} />
      </View>
    );
  }
});

var styles = StyleSheet.create({
  container: {
    flex: 1
  },
  square: {
    width: SQUARE_DIMENSIONS,
    height: SQUARE_DIMENSIONS,
    backgroundColor: 'blue'
  } 
});

AppRegistry.registerComponent('AnimatedSquare', () => AnimatedSquare);

Liked this content?

Get notified more about React Native!

No Spam! We Promise!

Related Content

Immutable Data with Immer and React setState

In this lesson we'll show the traditional method of updating state with a spread operator and then transform it into using the Immer produce functionality. This will allow us to achieve immutability on our data with simple mutations We'll then show how Immer can use currying to create updater functions that can be passed directly to setState.

Custom Handling the Android Back Button with React Navigation

In this lesson we'll explore setting up a stack navigator in React Navigation. We'll create a custom component that uses withNavigation to allow us to listen to navigation transitions. Using the BackHandler from React Native we can ask the screen if custom needs are required when the hardware back button is pressed. Finally we'll prompt the user if they want to navigate.

Immutable Data with Immer and React setState

In this lesson we'll show the traditional method of updating state with a spread operator and then transform it into using the Immer produce functionality. This will allow us to achieve immutability on our data with simple mutations We'll then show how Immer can use currying to create updater functions that can be passed directly to setState.

Test Successful Async Form Submissions with React Router

In this lesson we'll explore how to test that an async form submission successfully happened using Jest. We'll also take advantage of react-testing-library to render our react components to a fake dom. We'll use MemoryRouter to mock what a browser would normally do in the event of a transition, and create a fake component route to confirm that the submission successfully redirected us to the url we wanted.