Imperatively request interpolated value from a React Native animation

6.8k views Asked by At

I'm trying to retrieve the current color from a react-native animation. It's mapped through interpolate to a set of color strings.

class IconTransition extends React.Component<Props, State> {
  protected _param: number = 0;

  constructor(props: Props) {
    super(props);

    this.state = {
      param: new Animated.Value(0)
    };

    this.state.param.addListener(param => {
      this._param = param.value;
    });
  }

  componentDidMount() {
    Animated.spring(this.state.param, {
      mass: 1,
      stiffness: 10,
      damping: 10,
      toValue: 1
    });
  }

  componentWillReceiveProps() {
    // I want to do something like this. Would be awesome
    // if I could avoid the listener in the constructor.
    //
    // const currentColor = Animated.interpolate.get({
    //   currentInput: this._param,
    //   outputRange: ["#FFFFFF", "#000000"]
    // });
  }

  render() {
    return (
      <AnimatedIcon
        {...this.props}
        color={this.state.param.interpolate({
          inputRange: [0, 1],
          outputRange: ["#FFFFFF", "#000000"]
        })}
      />
    );
  }
}

I want to retrieve the color, as interpolated, should the animation not finish. I'm aware I could probably use an external library such a chroma-js (in particular, the chroma.mix function) to achieve this - but there are different ways to interpolate through two different colors and I'd rather not depend on an external library if I can avoid it.

So... the greater question remains, how can I imperatively request an output value from the interpolation API? Can we not listen on interpolated values, just as we do with Animated.Value()?

1

There are 1 answers

2
jeev On

I was trying to do the same for a while now and there's a few things you need to keep in mind:

If you put that all together you can get somthing like the following, which worked in my case:

import React from 'react';
import {View, processColor} from 'react-native';
import LinearGradient from 'react-native-linear-gradient';

class BackgroundColorLinearGradientText extends React.Component {

    /**
     * Class constructor.
     */
    constructor(props, context) {
        super(props, context);
        this.backgroundColor = new Animated.Value(0);
        this.backgroundColor.addListener(i => {
            let interpolated = this.backgroundColor.interpolate({
                inputRange: [0, 1],
                outputRange: ['#FF0000', '#00FF00'],
            }).__getValue();
            if (this.background) {
                this.background.setNativeProps({colors: [processColor(interpolated), processColor(this.background.props.colors[1])]})
            }
        });
    }

    componentDidMount() {
        Animated.timing(this.backgroundColor, {
            toValue: 1,
            duration: 3000,
        }).start();
    }

    render() {
        return (
            <LinearGradient ref={i => this.background = i} colors={['red', 'blue']} style={{flex: 1}}>
                <View style={{
                    flex: 1,
                    justifyContent: 'center',
                    alignItems: 'center',
                }}>
                    Content
                </View>
            </LinearGradient>
        );
    }
}

This will create a screen which has a red to blue gradient background, transitioning to green to blue in three seconds.