In the development of mobile client, smooth and meaningful animation is very important. Objects in real life have certain inertia when they start to move and stop. We can also use animation in the interface to achieve interaction in accordance with physical laws.

When RN was developed, two complementary animation systems were provided: Animated, for creating fine-grained interactive control, and LayoutAnimation, for global LayoutAnimation.

This article focuses on the use of the Animated library

The basic principle of performing an animation is to call an animation function to change the value of a variable that is bound to the properties of the control, such as transparency and position. Animate the control by changing the value through an animation function. So the most basic animation has two properties

  • The difference between Animated.Value and Animated.ValueXY is a single Value and a vector Value.
  • Animation types: Timing, Spring, Decay

Let’s do a quick example

const App = () => {
  const scaleAnim = useRef(new Animated.Value(0)).current;

  const fadeIn = () => {
    // Will change fadeAnim value to 1 in 2 seconds
    Animated.timing(scaleAnim, {
      toValue: 1,
      duration: 2000,
    }).start();
  };

  const fadeOut = () => {
    // Will change fadeAnim value to 0 in 2 seconds
    Animated.timing(scaleAnim, {
      toValue: 0,
      duration: 2000,
    }).start();
  };

  return (
    <View style={styles.container}>
      <Animated.View
        style={[
          styles.fadingContainer,
          {
            transform: [
              {
                scale: scaleAnim,
              },
            ],
          },
        ]}
      />
      <View style={styles.buttonRow}>
        <Button title="Fade In" onPress={fadeIn} />
        <Button title="Fade Out" onPress={fadeOut} />
      </View>
    </View>
  );
};
Copy the code

To write an animation you need the following steps:

  1. Use basic Animated components such as Animated.View, Animated.Image, Animated.Text, and others (wrapped with AnimatedImplementation);
  2. Use Animated.Value to set one or more initialization values, such as location properties, transparency properties, Angle properties, etc.
  3. Bind initialization values to properties of the animation target, such as style;
  4. Set animation parameters through the animation type Api, such as Spring, Decay, timing three animation types;
  5. Call start animation, at the same time you can call back related functions in start;

Animation types Timing, Spring, decay

  1. Animated. Decay () starts at a specified initial speed and then slows down until it stops.

Decay parameter animation export type DecayAnimationConfigSingle = AnimationConfig & {velocity, number, / / starting speed, deceleration required parameters? : number, // speed decay ratio, default 0.997};Copy the code

A typical use case for Decay was something moving at a speed of a component and slowing down, for example, to swipe or fling a card. The finger slides the card with speed, and eventually the card slows down due to resistance and stops. It’s usually used in conjunction with gestures

  1. Animated. Spring () provides a simple spring physics model.

export type SpringAnimationConfig = AnimationConfig & { toValue: number | AnimatedValue | {x: number, y: number} | AnimatedValueXY, overshootClamping? : Boolean, // Boolean indicates whether springs should be clamped rather than bounced. Default is false. restDisplacementThreshold? : number, // The rest displacement threshold, below which the spring should be considered in the rest state. The default value is 0.001. restSpeedThreshold? // The spring speed should be considered at rest in pixels per second. The default value is 0.001. velocity? : number / | {x: number, y: number}, attached to the spring / / the object's velocity. The default is 0 (the object is at rest) delay? : number, // Start animation after delay (ms). The default value is 0. bounciness? : number, // Control elasticity, default 8. speed? : number, // Controls the speed of the animation. The default is 12. tension? : number, // tension, default 40 friction? : number, // The default is 7. : number, // Spring stiffness coefficient, default 100. damping? : number, // defines how to damp the spring due to friction. Default is 10. mass? : number, // The mass of the object is attached to the end of the spring, default 1. }; You cannot specify bounciness, speed, tension, tension, Stiffness, damping, or mass at the same time. You can specify only one of the three data groupsCopy the code
  1. Animated. Timing () moves the values over time using easing.

export type TimingAnimationConfigSingle = AnimationConfig & { toValue: number | AnimatedValue, // easing? : (value: number) => number, // slow function. The default is Easing. InOut (ease). duration? : number, // The duration of the animation in milliseconds. The default value is 500. delay? : number, // The delay before the animation starts (milliseconds). Default is 0.};Copy the code

All three of these have two uncommon parameters in common: isInteraction: Specifies whether this animation is registered in the InteractionManager queue to influence its task scheduling. The default value is true. UseNativeDriver: Enables the native animation driver. This function is disabled by default (false). Note ⚠️ : The animation property translateX cannot be used with other animation properties when starting the native animation driver. Android phones have problems

Combination of animation

Parallel (simultaneous execution), sequence (sequential execution), delay(delayed execution), ‘

1. Use sequence to organize multiple animations so that they are executed in sequence.

Animation 1 -> Animation 2 -> Animation 3 -> Animation 4 -> Animation 5Copy the code

There are two animations in the example. One controls transparency and the other moves a certain distance

const App = () => {
  const fadeAnim = useRef(new Animated.Value(0)).current;
  const translateAnim = useRef(new Animated.Value(0)).current;

  const fadeIn = () => {
    Animated.parallel([
    Animated.timing(fadeAnim, {
      toValue: 1,
      duration: 1000
    }),
    Animated.timing(translateAnim, {
      toValue: 50,
      duration: 1000
    }),
    ]).start();
  };
  return (
    <View style={styles.container}>
      <Animated.View
        style={[
          styles.fadingContainer,
          {
            opacity: fadeAnim, // Bind opacity to animated value
            transform: [
               {
                 translateX: translateAnim,
               }
            ]
          }
        ]}
      >
      </Animated.View>
      <View style={styles.buttonRow}>
        <Button title="Fade In" onPress={fadeIn} />
      </View>
    </View>
  );
}
Copy the code

Using the Parallel method causes a group of animations to start executing at the same time. One of the animations stopped, and all the others stopped. However, you can modify this behavior by changing the stopTogether attribute.

3. Similar to parallel, stagger causes a group of animations to run simultaneously. However, a slight difference is that there is a delay in the start time of each animation.

const fadeIn = () => {
    Animated.stagger(4000,[
    Animated.timing(fadeAnim, {
      toValue: 1,
      duration: 1000
    }),
    Animated.timing(translateAnim, {
      toValue: 50,
      duration: 1000
    }),
    ]).start();
  };
Copy the code

The essence of Animated. Delay is that it creates an Animated. Timing that does nothing but wait. The main purpose is to be used as part of a group of animations in stagger or sequence.

Animated.sequence([
    Animated.delay(300),
    Animated.timing(this._animatedValue, {
        toValue: 100,
        duration: 500
    })
]).start()
Copy the code

It can also be used as a parameter

Animated.timing(this._animatedValue, {
    toValue: 100,
    delay: 300,
    duration: 500
}).start()
Copy the code

These two methods have the same animation effect

The interpolation interpolate

Interpolate helps us interpolate a unit mapping when the value of an animation is not the same unit as the property to be changed. Interpolation has the advantage of interpolating a single animation variable to control multiple parallel animations. It is easy to control. Such as:

transform: [
               {
                 rotateZ: translateAnim.interpolate({
                      inputRange: [0, 1],
                      outputRange:['0deg','60deg']
                    })
               }
            ]
Copy the code

Commonly used method

Animated.Value and Animated.ValueXY are the variable values that we talked about above and that will change as the animation executes.

  • Constructor: a constructor. new Animated.Value(0)
  • SetValue: Sets a new value. Note that this method stops the animation
  • SetOffset: Sets a modifier that will be applied to any subsequent value generated by setValue, an animation, or Animated. Event. It is often used to record a modifier (such as the current finger position and View position) at the beginning of a drag operation.
  • A flattening offset: a flattening offset that is used to combine relative values into values, then a relative value is set to 0 and the final output does not change. Usually called after the drag operation has finished.
  • AddListener: asynchronously listens for changes in animation values
  • RemoveListener: Deletes the specified listener
  • RemoveAllListeners: Deletes all listeners
  • StopAnimation: Stops the animation and returns the current animation value.
  • Interpolate: difference, which maps a value to a new value, more about later.

Animated.ValueXY: Animated.ValueXY: Animated.ValueXY: Animated.ValueXY: Animated.ValueXY: Animated.

  • GetLayout: Converts a {x, y} combination into a usable translation Transform, for example:

    style={{

    transform: this.state.anim.getTranslateTransform()

    }}

  • GetTranslateTransform: Converts a {x, y} combination into a usable translation Transform, for example:

    style={{

    transform: this.state.anim.getTranslateTransform()

    }}

import React, { useRef } from "react"; import { Animated, PanResponder, StyleSheet, View } from "react-native"; const DraggableView = () => { const pan = useRef(new Animated.ValueXY()).current; const panResponder = PanResponder.create({ onStartShouldSetPanResponder: () => true, onPanResponderMove: Animated.event([ null, { dx: pan.x, // x,y are Animated.Value dy: pan.y, }, ]), onPanResponderRelease: () => { Animated.spring( pan, // Auto-multiplexed { toValue: { x: 0, y: 0 } } // Back to zero ).start(); }}); return ( <View style={styles.container}> <Animated.View {... panResponder.panHandlers} style={[pan.getLayout(), styles.box]} /> </View> ); }; Style ={[pan.getLayout(), styles.box]} equal to: style={[styles.box,{transform: [{pan.x: pan}, {translateY: pan.y } ]}]}Copy the code

Effect demonstration:

The example above also uses the Animated. Event method, which matches a set of data to the data required for the animation, and finally calls the setValue method PanResponder on the matched data:

 onPanResponderMove: Animated.event([
   null,                // raw event arg ignored
   {dx: this._pan.x, dy: this._pan.y},    // gestureState arg
 ]),
Copy the code

In the onPanResponderMove method, Animated. Event is called. The argument is an array with two elements. We ignore the value of the first initial event. The second element is gestureState, which provides useful values dx and dy. These two values are how far the user’s finger has moved since touching the screen.

Custom animation createAnimatedComponent

Animated View, Animated.Text, and Animated.Image are supported by default. Sometimes you need some other component to be animated as well. The createAnimatedComponent method does this. It automatically binds properties such as props or state of the component to Animated.Value.

Such as the ScrollView:

let AnimatedScrollView = Animated.createAnimatedComponent(ScrollView)
Copy the code