16

I inherited the following component which worked well with former versions of react-native, displaying an opaque scrolling progress bar on buttons displayed by other components.

Recently, when I upgraded to react-native 0.62.2 I got an error requesting to add useNativeDriver. When adding it and setting it to 'yes', I got an error saying that "style property 'width' is not supported by native animated module".

When setting useNativeDriver to false I don't get an error but the animation doesn't work (at least not on android. Didn't test on iOS).

Any suggestion on how to modify the code to make it work with the newest version of react-native?

import React, { Component } from 'react';
import { Animated, Easing } from 'react-native';
import { connect } from 'react-redux';

class ProgressBar_Module extends Component {
  constructor(props) {
    super(props);

    this.state = {
      progress: 0
    }
    this.d_isMounted = false;
    this.d_initialTime = 0;
    this.d_animation_width = new Animated.Value(1);
    this.d_animation_progressTime = 3000;
  }

  componentDidMount() {
    this.d_initialTime = Date.now();
    this.d_isMounted = true;
    this.progressCount();
  }

  componentDidUpdate(prevProps) {
    if(prevProps.timerStart < this.props.timerStart) {
      this.setState({
        animation_width: new Animated.Value(1),
        animation_progressTime: 3000
      });
      this.progressCount(true)
    }
  }

  componentWillUnmount() {
    this.d_isMounted = false;
  }

  progressCount = (reset) => {
    if( reset ) {
      this.d_animation_width = new Animated.Value(1);
      this.d_animation_progressTime = 3000;
    }
    Animated.timing(this.d_animation_width, {
      toValue: 175,
      duration: this.d_animation_progressTime,
      easing: Easing.linear
    }).start(() => {
      if(this.props.timer && this.d_animation_width.__getValue() === 175) {
        this.props.onPress();
      }
    })
  }

  render() {

    return (
      <Animated.View
        style={[
          {
            position: 'absolute',
            left: 0,
            height: '150%',
            width: 0,
            zIndex: 1,
            backgroundColor: 'black',
          },
          { width: this.props.timer !== false ?
              this.d_animation_width : 175 }
        ]}
        onPress={this.props.onPress}
      >
      </Animated.View>
    )
  }
}

const mapStateToProps = state => {
  return {
    timer: state.get('ui').get('timer').get('timer'),
    reset: state.get('ui').get('resetTimer').get('resetTimer'),
    timerStart: state.get('ui').get('resetTimer').get('timerStart')
  }
};

export const ProgressBar = connect(mapStateToProps)(ProgressBar_Module);
Yossi
  • 5,577
  • 7
  • 41
  • 76

4 Answers4

34

Try doing this.

useNativeDriver: false

It worked for me.

gbe
  • 1,003
  • 1
  • 7
  • 23
  • 9
    This will cause the animation to be run on the JS thread instead of the UI thread though, which can lead to dropped frames. – Thijs Sep 08 '21 at 12:53
19

Instead of animating the width, try using transform and scaleX. Here is some reference to react native transforms: https://reactnative.dev/docs/transforms

YaNuSH
  • 927
  • 9
  • 10
  • 1
    This is, indeed, the way to go. I have posted another answer with details. – Yossi Sep 22 '20 at 04:58
  • Wha t should I do if the initial width and height is starting from 0? – Suresh Murali May 26 '21 at 01:52
  • @SureshMurali, I think it's better to set the initial width to the maximum value it can get in your app. This way, when you use transform, you know that scaleX:0 will be width 0, scaleX:0.5 will be width/2 etc... – YaNuSH May 26 '21 at 05:20
  • But as per my design, I would like it make it invisible by setting width:0. Is there any other solution? – Suresh Murali May 26 '21 at 09:01
  • I can't understand why can't you set scaleX to 0 instead of width to 0 – YaNuSH Jun 02 '21 at 14:52
7

Following @YaNuSH advice, I just needed to replace

   width: animatedValue

with:

transform: [{ scaleX: scaling }],

Full details:

Before:

Animated.timing(this.d_animation_width, {
      toValue: 175,
      duration: this.d_animation_progressTime,
      easing: Easing.linear
    }).start(()

After:

Animated.timing(this.d_animation_width, {
      toValue: 175,
      duration: this.d_animation_progressTime,
      easing: Easing.linear,
      useNativeDriver: true
    }).start(()

before:

return (
      <Animated.View
        style={[{
          position: 'absolute',
          left: 0,
          height: '150%',
          width: 0,
          zIndex: 1,
          backgroundColor: 'black',
        }, 
        {
          width: this.props.timer !== false ?
             this.d_animation_width : 175
        }]}
        onPress={this.props.onPress}
      >
      </Animated.View>
    )

after:

const scaling = this.props.timer !== false ? this.d_animation_width : 175;

return (
  <Animated.View
    style={[
      {
        position: 'absolute',
        left: 0,
        height: '150%',
        width: 2,
        zIndex: 1,
        backgroundColor: 'black',
      },
      {
        transform: [
          {
            scaleX: scaling
          },
        ],
      },
    ]}
    onPress={this.props.onPress}
  >
  </Animated.View>
)
Yossi
  • 5,577
  • 7
  • 41
  • 76
0
Animated.timing(this.loadingValue.width, {
        toValue: widthEnd,
        duration: 400,
        useNativeDriver: false //add this line into the library
      }).start();

      Animated.timing(this.loadingValue.borderRadius, {
        toValue: borderRadiusEnd,
        duration: 400,
        useNativeDriver: false //add this line into the library
      }).start();

      Animated.timing(this.loadingValue.opacity, {
        toValue: opacityEnd,
        duration: 300,
        useNativeDriver: false //add this line into the library
      }).start();

that's all... enjoy your coding...

Mahendren Mahisha
  • 1,072
  • 11
  • 9