3

i have following problem with the styled components. I want to hide the background with a new circle, but the one in the front is smaller, even if I have the same values for size. Here is my code example:

import React from "react";
import { StyleSheet, View } from "react-native";

const SIZE = 50;

export default function App() {
  return (
    <View style={styles.container}>
      <View style={styles.layer1} />
      <View style={styles.layer2} />
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: "white",
    alignItems: "center",
    justifyContent: "center",
  },
  layer1: {
    width: SIZE,
    height: SIZE,
    borderWidth: 3,
    borderRadius: SIZE / 2,
    borderColor: "black",
    position: "absolute",
  },
  layer2: {
    width: SIZE,
    height: SIZE,
    borderWidth: 3,
    borderRadius: SIZE / 2,
    borderColor: "white",
    position: "absolute",
  },
});

And here the screenshot:

Does someone know why the overlay is smaller than the background layer?

best regards and thanks!!!

user2675468
  • 150
  • 3
  • 16
  • They have the exact same size, it's just the back view black border antializing that appears there. Why don't you just hide the back circle view when you display the second one at the top? – Christos Lytras Apr 16 '20 at 13:38
  • Because i want to build a progress circle. – user2675468 Apr 16 '20 at 18:26
  • There is already a component for that you can use and save yourself much time; it's [react-native-progress-circle](https://www.npmjs.com/package/react-native-progress-circle) and it's quite popular. – Christos Lytras Apr 16 '20 at 18:28

3 Answers3

1

It seems to me to be because of css box-sizing.
With the default on most browsers box-sizing: content-box;, padding and border-width are added to the height and width..
but with box-sizing: border-box;, padding and border-width are contained within the set width and height.
so..
you probably want to add box-sizing: border-box;.

I usually add it to my whole document with

html {
  box-sizing: border-box;
}
*, *:before, *:after {
  box-sizing: inherit;
}

Edit (after screenshot added)

The problem is not that one circle is smaller... it's just that the darker border is peeking through from underneath.

Here is example where you can change the colour and visibility to make what is wrong obvious. Run the snippet and check the second checkbox to quickly see it.

function colourChange(e) {
  var target = e.target.getAttribute('data-target');
  var style = e.target.getAttribute('data-style');
  var col = e.target.value;
  document.getElementById(target).style[style] = col;
}

function visChange(e) {
  var target = e.target.getAttribute('data-target');
  document.getElementById(target).style.display = (e.target.checked) ? 'block' : 'none';
}

function toFront(e) {
 var other = 'layer1';
 if (e.target.value == 'layer1') {
   var other = 'layer2';
  }
  var otherz = document.getElementById(other).style.zIndex;
 var layer = document.getElementById(e.target.value);
 layer.style.zIndex = parseInt(otherz) + 1;
}

document.getElementById('border-col1').addEventListener('change', colourChange);
document.getElementById('border-col2').addEventListener('change', colourChange);
document.getElementById('background-col1').addEventListener('change', colourChange);
document.getElementById('background-col2').addEventListener('change', colourChange);
document.getElementById('vis1').addEventListener('change', visChange);
document.getElementById('vis2').addEventListener('change', visChange);
document.getElementById('front1').addEventListener('change', toFront);
document.getElementById('front2').addEventListener('change', toFront);
html {
  box-sizing: border-box;
}
*, *:before, *:after {
  box-sizing: inherit;
}

#container {
    flex: 1;
    background-color: white;
    align-items: center;
    justify-content: center;
  }
  #layer1 {
    width: 50px;
    height: 50px;
    border-width: 3px;
    border-radius: 25px;
    border-color: black;
    position: absolute;
    border-style: solid;
  }
  #layer2 {
    width: 50px;
    height: 50px;
    border-width: 3px;
    border-radius: 25px;
    border-color: white;
    position: absolute;
    border-style: solid;
  }
  
#inputs {
  position: absolute;
  top: 0px;
  right: 0px;
  padding: 10px;
}
<div id="container">
  <div id="layer1" style="z-index:1;"></div>
  <div id="layer2" style="z-index:2;"></div>
</div>
<div id="inputs">
  <div>
    border colours  
  </div>
  <input type="color" id="border-col1" data-target="layer1" data-style="borderColor" value="#000000">
  <input type="color" id="border-col2" data-target="layer2" data-style="borderColor" value="#ffffff">
  <div>
    background colours  
  </div>
  <input type="color" id="background-col1" data-target="layer1" data-style="backgroundColor" value="#ffffff">
  <input type="color" id="background-col2" data-target="layer2" data-style="backgroundColor" value="#ffffff">
  <div>
    visibility<br/>(display block / none)  
  </div>
  <input type="checkbox" id="vis1" data-target="layer1" checked>
  <input type="checkbox" id="vis2" data-target="layer2" checked>
  <div>
    in front  
  </div>
  <input type="radio" id="front1" name="front" value="layer1">
  <input type="radio" id="front2" name="front" value="layer2">
</div>
2pha
  • 9,798
  • 2
  • 29
  • 43
  • I am using flex box and got the error, box.sizing isn't a valid prop. – user2675468 Apr 13 '20 at 17:28
  • And I also found "React Native styles all views as if box-sizing: border-box is set" (https://stackoverflow.com/questions/38503451/does-it-exist-an-equivalent-of-box-sizing-border-box-in-flexbox-for-react-nativ). So maybe some other ideas? :/ – user2675468 Apr 13 '20 at 17:29
  • extremely hard to help you as it would be just a guess as we don't know the what the surrounding styles are. – 2pha Apr 13 '20 at 22:08
  • OK, I've updated my code now. Its just a clean react app (created with expo init) and than with the default styling libary. Nothing external added. – user2675468 Apr 14 '20 at 07:02
  • I updated my answer to show that the circles are in fact the same size, just that the darker border from underneath is peeking through. Run the snippet to see. – 2pha Apr 14 '20 at 08:29
  • I saw you post and tries the demo, but don't really get it. And I also looked what I Have in React native. There is for display just "none" and "flex". Both doesn't help me... :/ – user2675468 Apr 14 '20 at 09:38
  • The problem you are having has nothing to do with React or React Native. The problem is that you have a darker border behind a white border and the darker one is showing through on the edges. Hiding the one behind will fix your problem. I tried to make the problem as clear as I could, so made the snippet. But If you don't "get" my explanation, I don't know what else I can do to help you. – 2pha Apr 14 '20 at 11:13
  • No I got it, just don't know how to hide it. I also tried it with the z index but without success... – user2675468 Apr 14 '20 at 16:47
0

it's the way that render engine paint two overlapped path. they just kind of antializing. you can solve it by increasing the width and height of the front circle just a little to tackle the issue or you can hide the one which is behind

TheEhsanSarshar
  • 2,677
  • 22
  • 41
0

See this code,, you can do it without using any external library : https://medium.com/@0saurabhgour/react-native-percentage-based-progress-circle-no-external-library-e25b43e83888

import React, {Component} from 'react';
import PropTypes from 'prop-types';
import {StyleSheet, View, ViewPropTypes, I18nManager} from 'react-native';

// compatability for react-native versions < 0.44
const ViewPropTypesStyle = ViewPropTypes
  ? ViewPropTypes.style
  : View.propTypes.style;
let direction = I18nManager.isRTL ? 'right' : 'left';
const styles = StyleSheet.create({
  outerCircle: {
    justifyContent: 'center',
    alignItems: 'center',
  },
  innerCircle: {
    overflow: 'hidden',
    justifyContent: 'center',
    alignItems: 'center',
  },
  leftWrap: {
    position: 'absolute',
    top: 0,
    [`${direction}`]: 0,
  },
  halfCircle: {
    position: 'absolute',
    top: 0,
    left: 0,
    borderTopRightRadius: 0,
    borderBottomRightRadius: 0,
  },
});

function percentToDegrees(percent) {
  return percent * 3.6;
}

export default class CircularProgress extends Component {
  static propTypes = {
    color: PropTypes.string,
    shadowColor: PropTypes.string,
    bgColor: PropTypes.string,
    radius: PropTypes.number.isRequired,
    borderWidth: PropTypes.number,
    percent: PropTypes.number.isRequired, // eslint-disable-line react/no-unused-prop-types
    children: PropTypes.node,
    containerStyle: ViewPropTypesStyle,
    outerCircleStyle: ViewPropTypesStyle,
  };

  static defaultProps = {
    color: '#f00',
    shadowColor: '#999',
    bgColor: '#e9e9ef',
    borderWidth: 2,
    children: null,
    containerStyle: null,
  };

  constructor(props) {
    super(props);
    this.state = this.getInitialStateFromProps(props);
  }

  UNSAFE_componentWillReceiveProps(nextProps) {
    this.setState(this.getInitialStateFromProps(nextProps));
  }

  getInitialStateFromProps(props) {
    const percent = Math.max(Math.min(100, props.percent), 0);
    const needHalfCircle2 = percent > 50;
    let halfCircle1Degree;
    let halfCircle2Degree;
    // degrees indicate the 'end' of the half circle, i.e. they span (degree - 180, degree)
    if (needHalfCircle2) {
      halfCircle1Degree = 180;
      halfCircle2Degree = percentToDegrees(percent);
    } else {
      halfCircle1Degree = percentToDegrees(percent);
      halfCircle2Degree = 0;
    }

    return {
      halfCircle1Degree,
      halfCircle2Degree,
      halfCircle2Styles: {
        // when the second half circle is not needed, we need it to cover
        // the negative degrees of the first circle
        backgroundColor: needHalfCircle2 ? props.color : props.shadowColor,
      },
    };
  }

  renderHalfCircle(rotateDegrees, halfCircleStyles) {
    const {radius, color} = this.props;
    const key = I18nManager.isRTL ? 'right' : 'left';
    return (
      <View
        style={[
          styles.leftWrap,
          {
            width: radius,
            height: radius * 2,
          },
        ]}>
        <View
          style={[
            styles.halfCircle,
            {
              width: radius,
              height: radius * 2,
              borderRadius: radius,
              overflow: 'hidden',
              transform: [
                {translateX: radius / 2},
                {rotate: `${rotateDegrees}deg`},
                {translateX: -radius / 2},
              ],
              backgroundColor: color,
              ...halfCircleStyles,
            },
          ]}
        />
      </View>
    );
  }

  renderInnerCircle() {
    const radiusMinusBorder = this.props.radius - this.props.borderWidth;
    return (
      <View
        style={[
          styles.innerCircle,
          {
            width: radiusMinusBorder * 2,
            height: radiusMinusBorder * 2,
            borderRadius: radiusMinusBorder,
            backgroundColor: this.props.bgColor,
            ...this.props.containerStyle,
          },
        ]}>
        {this.props.children}
      </View>
    );
  }

  render() {
    const {
      halfCircle1Degree,
      halfCircle2Degree,
      halfCircle2Styles,
    } = this.state;
    return (
      <View
        style={[
          styles.outerCircle,
          {
            width: this.props.radius * 2,
            height: this.props.radius * 2,
            borderRadius: this.props.radius,
            backgroundColor: this.props.shadowColor,
            ...this.props.outerCircleStyle,
          },
        ]}>
        {this.renderHalfCircle(halfCircle1Degree)}
        {this.renderHalfCircle(halfCircle2Degree, halfCircle2Styles)}
        {this.renderInnerCircle()}
      </View>
    );
  }
}
Neelam Soni
  • 1,251
  • 8
  • 15