react-native-percentage-circle icon indicating copy to clipboard operation
react-native-percentage-circle copied to clipboard

The solution to fix wrong progress when percentage is less than 50

Open hendiko opened this issue 7 years ago • 1 comments

Env:

Wrong Example on Android: android

Normal Example on iOS: ios

When percentage is less than 50, the circle is wrong.

I thought it might be caused by overflow on Android, and I found an known issue in [email protected] described as below:

overflow样式在Android默认为hidden而且无法更改 这是Android本身的渲染机制所致。我们没有实现这一特性,因为这是个大工程,而且我们还有很多其他重要的任务。 Android的overflow:hidden还有另外一个问题:如果父容器有borderRadius圆角边框样式,那么即便开启了overflow:hidden也仍然无法把子视图超出圆角边框的部分裁切掉。这个问题只存在于Android上,iOS并没有这个问题(子视图的内容不会超出父容器的圆角边框)。你可以在这里看到问题的演示,以及在这里查看这个问题的报告以及后续进展。

So the implementation of overflow differs in Android and iOS, that's why the issue only occurs on Android in my project.

I have tried the solutions which I can found here, unfortunately none of them works.

So I have to dig into it and this is my solution:

Solution

Step 1

line 74 in method constructor:

    if (percent >= 50) {
      rightTransformerDegree = '180deg';
      leftTransformerDegree = (percent - 50) * 3.6 + 'deg';
    } else {
      rightTransformerDegree = percent * 3.6 + 'deg';
      leftTransformerDegree = '0deg'; 
    }

changed into

    if (percent >= 50) {
      rightTransformerDegree = '180deg';
      leftTransformerDegree = (percent - 50) * 3.6 + 'deg';
    } else {
      rightTransformerDegree = percent * 3.6 + 'deg';
      leftTransformerDegree = '180deg';   // line 74: changed here
    }

Step2

line 94 in method componentWillReceiveProps

    if (percent >= 50) {
      rightTransformerDegree = '180deg';
      leftTransformerDegree = (percent - 50) * 3.6 + 'deg';
    } else {
      rightTransformerDegree = percent * 3.6 + 'deg';
    }

insert a line:

    if (percent >= 50) {
      rightTransformerDegree = '180deg';
      leftTransformerDegree = (percent - 50) * 3.6 + 'deg';
    } else {
      rightTransformerDegree = percent * 3.6 + 'deg';
      leftTransformerDegree = "180deg";   // insert a line here
    }

Step 3

Adjust content between line 123 and 152:

        <View style={[styles.leftWrap,{
          width: this.props.radius,
          height: this.props.radius * 2,
          left:0,
        }]}>
          <View style={[styles.loader,{
            left: this.props.radius,
            width:this.props.radius,
            height: this.props.radius*2,
            borderTopLeftRadius:0,
            borderBottomLeftRadius:0,
            backgroundColor:this.props.color,
            transform:[{translateX:-this.props.radius/2},{rotate:this.state.leftTransformerDegree},{translateX:this.props.radius/2}],
          }]}></View>
        </View>
        <View style={[styles.leftWrap,{
          left:this.props.radius,
          width: this.props.radius,
          height: this.props.radius * 2,
        }]}>
          <View style={[styles.loader,{
            left:-this.props.radius,
            width:this.props.radius,
            height: this.props.radius*2,
            borderTopRightRadius:0,
            borderBottomRightRadius:0,
            backgroundColor: this.props.color,
            transform:[{translateX:this.props.radius/2},{rotate:this.state.rightTransformerDegree},{translateX:-this.props.radius/2}],
          }]}></View>
        </View>

Put View#rightTransformerDegree before View#leftTransformerDegree, and change backgroundColor of View#leftTransformerDegree

        <View style={[styles.leftWrap,{
          left:this.props.radius,
          width: this.props.radius,
          height: this.props.radius * 2,
        }]}>
          <View style={[styles.loader,{
            left:-this.props.radius,
            width:this.props.radius,
            height: this.props.radius*2,
            borderTopRightRadius:0,
            borderBottomRightRadius:0,
            backgroundColor: this.props.color,
            transform:[{translateX:this.props.radius/2},{rotate:this.state.rightTransformerDegree},{translateX:-this.props.radius/2}],
          }]}></View>
        </View>
        <View style={[styles.leftWrap,{
          width: this.props.radius,
          height: this.props.radius * 2,
          left:0,
        }]}>
          <View style={[styles.loader,{
            left: this.props.radius,
            width:this.props.radius,
            height: this.props.radius*2,
            borderTopLeftRadius:0,
            borderBottomLeftRadius:0,
            backgroundColor:percent >= 50 ? this.props.color : this.props.bgcolor,    // changed this line
            transform:[{translateX:-this.props.radius/2},{rotate:this.state.leftTransformerDegree},{translateX:this.props.radius/2}],
          }]}></View>
        </View>

Conclusion

I have forked this project and rewritten the code, I hope it could be merged into this project, or you can visit at My forked repo

Some changes in forked repo:

  1. Fixed the issue when percentage is less than 50.
  2. Fixed textStyle prop which is applied in Text in View#innerCircle.
  3. Added new prop rotate to support customizing the start position of progress circle.

Warning

I have tested my solution only in environments below:

  • Android:

    • Device: XiaoMi 5
    • OS: Android 6.0.1 MXB48T
    • react: 15.4.1
    • react-native: 0.39.2
  • iOS:

    • Device: iPhone 6s
    • OS: iOS 11.4
    • react: 15.3.1
    • react-native: 0.33.1

hendiko avatar Jun 15 '18 09:06 hendiko

@hendiko thank you for the contribution. Please merge this fork

luischavezpozo avatar Nov 27 '18 04:11 luischavezpozo