8

I have a position:relative green ring with a position:absolute red clone :before and a position:absolute white clone :after it covering both (since they are on the same place and have the same size).

The problem is: it renders bad on both browsers tested (Chrome and Firefox) where I still can see the green/red rings under the white mask. Let the green ring with overflow:hidden partially fixes the problem removing the outer bleed; but the inner bleeding border stills there.

Why does it happens and how can I totaly hide the lower circles ?

Codepen

body {
  background: lavender;
}

#ring {
  position: relative;
  width: 100px;
  height: 100px;
  border-radius: 50%;
  border: 50px solid green;
}

#ring:before {
  content: '';
  position: absolute;
  top: -50px;
  left: -50px;
  width: 100px;
  height: 100px;
  border: 50px solid red;
  border-radius: 50%;
}

#ring:after {
  content: '';
  position: absolute;
  top: -50px;
  left: -50px;
  width: 100px;
  height: 100px;
  border-radius: 50%;
  border: 50px solid white;
}
<div id=ring></div>

Update: here's a link of the complete (not minimal) scenario: Codepen which is only working on the updated Google Chrome at the moment;

web-tiki
  • 99,765
  • 32
  • 217
  • 249
L777
  • 7,719
  • 3
  • 38
  • 63
  • Here is 2 possible duplicates, 1: http://stackoverflow.com/questions/35984449/css-wrong-appearance-of-border-radius-on-a-nested-div ... 2: http://stackoverflow.com/questions/2932146/math-problem-determine-the-corner-radius-of-an-inner-border-based-on-outer-corn ... – Asons Mar 29 '16 at 09:09
  • @LGSon on these examples the outer border is sightly bigger than the inner div... here in the current scenario its supposed to do not have any border at all (since the objects have the exactly same size). – L777 Mar 29 '16 at 09:27
  • I just saw that, this one is better: http://stackoverflow.com/questions/27526047/jagged-border-showing-due-to-background-colour-on-wrapper-element-with-border – Asons Mar 29 '16 at 09:44
  • This, yes, even in a different scenario (where there is no pseudo elements), seems to be the same issue; but looks like there is no solution for this problem yet; – L777 Mar 29 '16 at 09:55
  • 1
    what you trying to achieve (can't understand)? – dippas Mar 29 '16 at 10:30
  • 1
    The solution is to not keep the one's beneath visible until they are needed, as it is coded now, they shouldn't show anyway (if everything were "correct"). – Asons Mar 29 '16 at 10:38
  • LGSon and @dippas they are needed at the same time (in the complete scenario) [**check this link**](http://codepen.io/freestock/pen/jqLEwB?editors=1100) only workin on updated chrome atm. [**screenshot**](http://i.gyazo.com/f46842940e289467a9d0754ecf972e1e.png) – L777 Mar 29 '16 at 10:49
  • On the complete scenario the green is 'tomato' colored and it is needed. I just changed it's color on here to let the minimal example easier to work with; – L777 Mar 29 '16 at 11:04

3 Answers3

6

In your radial progress bar scenario, you can use the approach described here : Circular percent progress bar. Using inline svg and animating the stroke-dasharray attribute for the progress bar.
Adapted to your use case, it can look like this:

body{background:lavender;}
svg{width:200px;height:200px;}
<svg viewbox="-2.5 -2.5 105 105">
  <circle cx="50" cy="50" r="40" fill="transparent" stroke-width="25" stroke="#fff"/>
  <path fill="none" stroke-width="25" stroke="tomato"  stroke-dasharray="251.2,0" d="M50 10 a 40 40 0 0 1 0 80 a 40 40 0 0 1 0 -80">
    <animate attributeName="stroke-dasharray" from="0,251.2" to="251.2,0" dur="5s"/>           
  </path>
</svg>

Note that in this example the animation is made with SMIL. But you can also do it with JS as descirbed in the radial progress bar answer.


Previous answer:
If your aim is to remove the bleed, one solution would be to hide it by making the pseudo elements border wider.
Depending on your actual use case this solution can be apropriate.

Here is an example :

body{background:lavender}
#ring {
  position: relative;
  width: 100px;
  height: 100px;
  border-radius: 50%;
  border: 50px solid green;
}
#ring:before {
  content: '';
  position: absolute;
  top: -51px;
  left: -51px;
  width: 98px;
  height: 98px;
  border: 52px solid red;
  border-radius: 50%;
}
#ring:after {
  content: '';
  position: absolute;
  top: -52px;
  left: -52px;
  width: 96px;
  height: 96px;
  border-radius: 50%;
  border: 54px solid white;
}
<div id="ring"></div>
Community
  • 1
  • 1
web-tiki
  • 99,765
  • 32
  • 217
  • 249
  • Thanks for the answer it's a valid workaround for the current minimal example but unfortunately I can't apply it on the [complete scenario](http://codepen.io/freestock/pen/jqLEwB?editors=1100) (only working on chrome atm) as you see on this screenshot: [**result**](http://i.gyazo.com/9c38af2f91f7ce9acb8c988b1f54894a.png) – L777 Mar 29 '16 at 11:29
  • @freestock.tk I see. I added another approach in my answer. – web-tiki Mar 29 '16 at 12:10
  • 2
    Not just a neat animation but a shorter code aswell that's really great; – L777 Mar 29 '16 at 12:13
  • You could remove the `` since it isn't covering anything plus add `repeatCount="indefinite"` in the `` tag to let it rolling forever like the OP example. @vals gave the best explanation of the problem and you bring the best solution. Unfortunately I cant mark both as the right answer so +1 and thank you very much for you two guys! – L777 Jan 06 '17 at 23:25
4

The reason for the problem is antialiasng in the limiting (border) pixels. To make the border of the circle less pixelated, the pixels that are only half-way inside the circle are rendered semitransparents.

The problem is that the circle under the top one is also rendering semitransparent pixels. (in another color, of course). So, half transparent white is rendered on top of semitransparent red (that is rendered on top of semitransparent green).

The net result is that the pixel is not pure white.

To solve the root of the problem, you would need to turn off antialiasing, that AFAIK is not posible with borders (only text, and images under develoopment). Besides, such a solution would make the circle quite ugly

To mitigate it, you can do several hacks, with sizes, shadows, or whatever.

For another way to solve your original issue in CSS, (besides the excellent one that you already have using SVG) see this answer.

Community
  • 1
  • 1
vals
  • 61,425
  • 11
  • 89
  • 138
  • Good explanation, +1. The svg approach also has the antialiasing issue, it just isn't visible because the of the colors used. – web-tiki Mar 30 '16 at 08:18
  • I found the following answer to disable the antialiasing on images: [**Link**](http://stackoverflow.com/questions/14068103/disable-antialising-when-scaling-images) and the following answer to disable it on text [**Link 2**](http://stackoverflow.com/questions/10538570/is-it-possible-to-disable-font-smoothing-in-css) but found nothing to disable it on radial borders; – L777 Mar 30 '16 at 09:13
  • I found this [**Link 3**](https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/shape-rendering) to disable the anti-aliasing on svg shapes; – L777 Mar 30 '16 at 13:18
  • You gave the best explanation of the problem and @web-tiki has bring the best solution. Unfortunately I cant mark both as the right answer so +1 and thank you very much for you two guys! – L777 Jan 06 '17 at 23:29
1

On the right side of the following snippet is the result of the @web-tiki svg ring with 2 clones of the same size/place (but different colors) with the anti-aliasing disabled (shape-rendering="crispEdges") just like @vals have mentioned:

body {
  background: lavender;
  margin: 0;
  overflow: hidden;
}

div {
  width: 200px;
  height: 200px;
  display: inline-block;
  position: relative;
}

svg {
  width: 200px;
  height: 200px;
  position: absolute;
  top: 0;
  left: 0;
}

span {
  font-family: arial, sans-serif;
  text-align: center;
  position: absolute;
  left: 0;
  right: 0;
  margin: auto;
  top: 45%;
}
<div>
<svg viewbox="-2.5 -2.5 105 105">
  <circle cx="50" cy="50" r="40" fill="transparent" stroke-width="25" stroke="green"/>        
</svg>

<svg viewbox="-2.5 -2.5 105 105">
  <circle cx="50" cy="50" r="40" fill="transparent" stroke-width="25" stroke="tomato"/>        
</svg>

<svg viewbox="-2.5 -2.5 105 105">
  <circle cx="50" cy="50" r="40" fill="transparent" stroke-width="25" stroke="#fff"/>        
</svg><span><small>shape-rendering="auto"</small></span>
</div><div>
<svg viewbox="-2.5 -2.5 105 105" shape-rendering="crispEdges">
  <circle cx="50" cy="50" r="40" fill="transparent" stroke-width="25" stroke="green"/>        
</svg>

<svg viewbox="-2.5 -2.5 105 105" shape-rendering="crispEdges">
  <circle cx="50" cy="50" r="40" fill="transparent" stroke-width="25" stroke="tomato"/>        
</svg>

<svg viewbox="-2.5 -2.5 105 105" shape-rendering="crispEdges">
  <circle cx="50" cy="50" r="40" fill="transparent" stroke-width="25" stroke="#fff"/>        
</svg><span><small>shape-rendering="crispEdges"</small></span>
</div>

Since the OP example uses the :before and :after pseudo-elements, I have tried to apply this same shape-rendering="crispEdges" on the svg clip-path, but had no success in any browser: Link

Source: MDN

Community
  • 1
  • 1
L777
  • 7,719
  • 3
  • 38
  • 63