Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Imperatively request interpolated value from a React Native animation

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()?

like image 507
Ian Haggerty Avatar asked Feb 10 '18 22:02

Ian Haggerty


People also ask

How do I get the value of animation in React Native?

To get the current value of Animated. Value with React Native, we call addListener on the animated value object. to call spinValue. addListener with a callback to get the current value of the animated value from the value property.

How do you check if animation is finished React Native?

The updated answer should be: . start(({finished}) => { if (finished) { console. log('animation ended!) } })


1 Answers

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

  • Since you're trying to update a prop that is not a style prop your best bet is to use the addListener and setNativeProps methods https://facebook.github.io/react-native/docs/animations#setnativeprops
  • The interpolation has a __getValue method to check the current value. This is the function you want to call in the listener to check the current interpolated value https://github.com/facebook/react-native/blob/master/Libraries/Animated/src/nodes/AnimatedInterpolation.js#L327
  • Colors cannot be passed to props like that when set from setNative props, it has to be passed through processColor https://github.com/facebook/react-native/blob/master/Libraries/StyleSheet/processColor.js

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.

like image 67
jeev Avatar answered Oct 12 '22 13:10

jeev