How would I animate the width of a button based on percentage, and the same for it's backgroundColor?

22,792

I would suggest you to read more about interpolation because it is very useful when doing almost any kind of animation in React Native.

Basically with interpolate you can map some animated values to so other values. In your case you want to map number between 40-100 to a percent string like 50%. Second thing you want to do is to map number between 40-100 to color from (as an example) red to blue.

Read interpolate docs fully and experiment with it and ask if you have questions after that :)

So I would do it like this:

class AnimatedButton extends Component {
  constructor(props) {
    super(props);
    this.state = { width: new Animated.Value(100) };
  }

  toggleWidth() {
    const endWidth = this.props.isFullWidth ? 40 : 100;

    Animated.timing(this.state.width, {
      toValue: endWidth,
      duration: 200,
      easing: Easing.linear,
    }).start();
  }

  render() {
    <TouchableOpacity
      style={{
        width: this.state.width.interpolate({
          inputRange: [0, 1],
          outputRange: ['0%', '100%'],
        }),
        backgroundColor: this.state.width.interpolate({
          inputRange: [40, 100],
          outputRange: ['rgba(30, 70, 30, 1.0)', 'rgba(220, 100, 50, 0.8)'],
        }),
      }}
      onPress={this.props.onPress}
    >
      // more stuff
    </TouchableOpacity>;
  }
}
Share:
22,792
VDog
Author by

VDog

Updated on April 14, 2020

Comments

  • VDog
    VDog about 4 years

    I have some <AnimatedButton />

    I want to animate it to go from 100% width to 40% width depending on a prop that is a boolean called isFullWidth.

    I have:

    class AnimatedButton extends Component {
        constructor(props) {
          super(props);
          this.state = { width: new Animated.Value(100) };
        }
        toggleWidth() {
          const endWidth = this.props.isFullWidth ? 40 : 100;
    
          Animated.timing(this.state.width, {
            toValue: endWidth,
            duration: 200,
            easing: Easing.linear,
          }).start();
        }
    
        render() {
          <TouchableOpacity
           style={{ width: `${this.state.width}%` }}
           onPress={this.props.onPress}
          >
           // more stuff
          </TouchableOpacity>
        }
    }
    

    The problem is that it just jumps into the appropriate percentage without animating. I tried setting the width to just this.state.animatedValue and rather than use percentage, just use pixels, e.g. 150 to 400 and back, and it works fine as expected.

    Same question applies for going from say rgba(220, 100, 50, 0.8) to rgba(30, 70, 30, 1.0) and back?

  • wabisabit
    wabisabit almost 5 years
    Shouldn't the input and output range for the width go up to 100 instead of 1?
  • azrahel
    azrahel over 2 years
    also I think onPress should use toggleWidth method, not props.onPress. Right?