Course Menu

Numbers And Interpolates on Interpolates

Flip Values

Building off of our interpolations on interpolations we can also flip numbers. inputRange is only able to accept values that move in an increasing fashion. However you may be constructing reversed animations that would require you to do an inputRange of [1, 0].

To accommodate this we can interpolate to an outputRange that flips in reverse for us so that our animation is moving forward from 0 => 1 the interpolate will flip it to be animating from 1 => 0, which then we can interpolate on our second animation in the correct direction [0, 1] but it will actually be animating in reverse.

const animatedInterpolate = this.state.animation.interpolate({
  inputRange: [0, 1],
  outputRange: [1, 0],
});

const reversedDirection = animatedInterpolate.interpolate({
  inputRange: [0, 1],
  outputRange: [1, 0.5],
});

This can be confusing to understand, but just knowing that it's a thing will come in handy when you realize you need it.

Just interpolating numbers to different numbers is going to be your primary use case of using interpolate. This may be mapping values up, down, or any direction depending on what your animation requires.

Scaling values up would look something like this. We have an inputRange that will take an animation that is animating from 0 to 1.

this.state.animation.interpolate({
  inputRange: [0, 1],
  outputRange: [0, 100],
});

Now if we were to kick off an animation

Animated.timing(this.state.animation, {
  toValue: 1,
  duration: 1000,
}).start();

The output over the course of 1000ms would churn out values like so.

input: 0 => 0ms => output: 0
input: .1 => 100ms => output: 10
input: .2 => 200ms => output: 20
input: .3 => 300ms => output: 30
....
input: 1 => 1000ms => output: 100

The same goes for the opposite direction. We can scale numbers down from larger => smaller

this.state.animation.interpolate({
  inputRange: [0, 100],
  outputRange: [0, 1],
});

Kicking off an animation

Animated.timing(this.state.animation, {
  toValue: 1,
  duration: 1000,
}).start();

Would produce values

input: 0 => 0ms => output: 0
input: 10 => 100ms => output: .1
input: 20 => 200ms => output: .2
input: 30 => 300ms => output: .3
....
input: 100 => 1000ms => output: 1

These are examples where the inputRange is producing a linear outputRange, but the inputRange is the only thing that needs to be moving in an increasing fashion. The outputRange could be anything. The only requirement is that the inputRange and outputRange have the same number of items in their respective arrays.

Lets look at an interpolation

this.state.animation.interpolate({
  inputRange: [0, 30, 50, 80, 100],
  outputRange: [0, -30, -50, 0, 200],
});

Lets take a look at what values would be produced with the animation

Animated.timing(this.state.animation, {
  toValue: 1,
  duration: 1000,
}).start();
input: 0 => 0ms => 0
input 15 => 150ms => -15
input 30 => 300ms => -30
input 50 => 500ms => -50
input 65 => 650ms => -25
input 80 => 800ms => 0
input 90 => 900ms => 100
input 100 => 100ms => 200

As you can see our animated value only ever increased, but our outputRange intermediate values were interpolated correctly based upon the steps in between each range and it's targeted output range.

Understanding all the intermediate values can be necessary however, for many animations it's not critical. Interpolate will figure out the steps correctly based upon your duration, or spring.

Now realize that interpolate is returning a new Animated.Value so what that means is you can interpolate on an interpolate.

Lets look at an example of this.

export default class animations extends Component {
  state = {
    animation: new Animated.Value(0),
  };
  startAnimation = () => {
    Animated.timing(this.state.animation, {
      toValue: 1,
      duration: 1500,
    }).start(() => {
      Animated.timing(this.state.animation, {
        toValue: 2,
        duration: 300,
      }).start();
    });
  };

  render() {
    const animatedInterpolate = this.state.animation.interpolate({
      inputRange: [0, 1, 2],
      outputRange: [0, 300, 0],
    });

    const interpolatedInterpolate = animatedInterpolate.interpolate({
      inputRange: [0, 300],
      outputRange: [1, 0.5],
    });

    const animatedStyles = {
      transform: [{ translateY: animatedInterpolate }],
      opacity: interpolatedInterpolate,
    };
    return (
      <View style={styles.container}>
        <TouchableWithoutFeedback onPress={this.startAnimation}>
          <Animated.View style={[styles.box, animatedStyles]} />
        </TouchableWithoutFeedback>
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    alignItems: "center",
    justifyContent: "center",
  },
  box: {
    width: 150,
    height: 150,
    backgroundColor: "tomato",
  },
});

Lets focus on the important piece.

Our animation is only going to 2, however our interpolateInterpolate is referencing the values that are in the outputRange of our first animated interpolate. This can be a powerful tool when passing around animated interpolations.

const animatedInterpolate = this.state.animation.interpolate({
  inputRange: [0, 1, 2],
  outputRange: [0, 300, 0],
});

const interpolatedInterpolate = animatedInterpolate.interpolate({
  inputRange: [0, 300],
  outputRange: [1, 0.5],
});

If you do not want to expose the original animated value, or your interpolation only operates across a specific range. You can interpolate an interpolate before passing it to something that will interpolate it.

Quick example, if we had an animation that went from 0 to 300, but something required a range from 0 to 1. We can map our desired inputRange to an outputRange that will feed into our second animation inputRange and derive our desired outputRange.

const animatedInterpolate = this.state.animation.interpolate({
  inputRange: [0, 300],
  outputRange: [0, 1],
  extrapolate: "clamp",
});

const interpolatedInterpolate = animatedInterpolate.interpolate({
  inputRange: [0, 1],
  outputRange: [1, 0.5],
});

Interpolating interpolations can be challenging to wrap your head around but can be extremely powerful in practice.

Live Demo