Jagged "border" showing due to background colour on wrapper element with border-radius: 50%;
Asked Answered
P

4

7

As I was in the process of trying to make an animated figure (transitions on hover), I found out that the background of my <figure> is showing near the edges when I apply border-radius: 50% to it, even though my image should be taking up all available space.

For a quick demo that illustrates the problem, please look at http://codepen.io/anon/pen/KwMMKz

HTML

<figure>
  <img src="http://placehold.it/400x400" alt>
  <figcaption>Demo</figcaption>
</figure>

CSS

figure {
  background-color: red;
  width: 400px;
  height: 400px;
  border-radius: 50%;
  overflow: hidden;
  position: relative; /* For caption */

}

img {
  border-radius: 50%; /* Forced on image for smooth transition */
  width: 100%;
  transition: opacity 1s ease-out;
}

figcaption {
  position: absolute;
  top: 100%;
  left: 0;
  width: 100%;
  color: hotpink;
  text-align: center;
  transition: top 1s ease-out;
}
figure:hover img {
  opacity: 0;
}
figure:hover figcaption {
  top: 50%;
}

Please note: I know that placing the background-color on figure:hover is a work-around, but I am more interested in the reason why this "jagged border"-like look is appearing.

My guess is that it has to do with AA rendering (or something related) of the browser and that it treats the <figure> element differently than a media element such as <img>, but I can't find any proof of this online. Is this a bug, is it a "feature", or is it something I can actually fix?

Lastly, I also know that I could have used transform: translateY(); here for the animation, but that's not part of my question so please don't provide it as an answer.

UPDATE 17/12 14:03

It appears that this issue is not exclusive to border-radius: 50%. The issue can occur when any wrapping element uses border-radius in combination with overflow: hidden, when the wrapper contains content that is equal or bigger than the wrapper's dimensions.

UPDATE 17/12 14:14

Neither the usage of overflow: hidden on the wrapper element, nor the usage of border-radius on the contained image (or any other child element) seem to be the cause of this as they can be interchanged and the pixelated edge will still appear.

This seems to indicate that this issue is solely caused by 2 DOM elements being in exactly the same place, when any sort of border-radius is applied to the wrapper element and the visible area of the child is limited to that of the parent's.

Prismoid answered 17/12, 2014 at 12:50 Comment(5)
this seems to be the same issue as this post : Pixelated edge around a CSS Circle with overflow: hidden;Respect
Is your question about the very light (1px red border) or about that on hover the grey area is a square (while fading out) instead of a circle?Temper
@Respect it is probably related to the same core issue, but I can't see any reply in that post that explains why it is happening and if it is a bug or not. Why does this only occur this pixelated effect only occur on border-radius: 50% when 2 elements should meet at the same position? I can't think of any good reason why this is occuring.Prismoid
@marczking: my question is related to the very light red border.Prismoid
@SimonV. yes, I understand that and that is why I didn't close this question as a duplicate. Issue is the same but question is different.Respect
U
1

I've been having same issue and ended up using pseudo element instead of background, kinda like that:

figure::before {
  content: '';
  display: block;
  background-color: red;
  width: 400px;
  height: 400px;
  transform: scale(0.997);
  border-radius: 50%;
}

This allowed me to create 'pseudo background' which I later shrinked a little bit with transform: scale(0.997); so it will be just the same size but a bit below visible edge. Of course in your case you would also need to position image absolutely so it is not pushed below by this ::before.

Uniformed answered 29/7, 2015 at 15:20 Comment(0)
N
0

It appears that it is indeed a "feature" of how the browser handles border-radius to give a smooth edge to the rounded corners of a container. The image background is anti-aliased in the same way (but as it is transparent has no effect) as can be seen by setting the img background color.

When the border is anti-aliased it "bleeds" into the background to soften the edges and so you are seeing that around the image as a "jaggy" ring in much the same way you would see a corona around the moon during a full solar eclipse.

the issue is always there, whether the anti-aliased object is covered or not, if you were to draw a circle then anti-alias it, you would see the circle is marginally narrower than the anti-aliased version. Most anti-aliasing algorithms aggregate the surrounding pixels of the object rather than those contained within it.

To overcome it, you'd either need to make your image large enough to cover the space taken up by the anti-aliased edge or reduce the container such that the anti-aliased area is smaller than the image.

Nibble answered 17/12, 2014 at 13:14 Comment(4)
The OP explicitly states that ------> "I know that placing the background-color on figure:hover is a work-around, but I am more interested in the reason why this "jagged border"-like look is appearing", which means the OP is looking for a reason not a solution.Tullus
Thanks, but I don't see how adding a background color to the hover state would work, surely it would have to be on the image. Updated anyway to explain whats going on.Nibble
@Mauro: that's not true. If you add figure:hover { background-color: red; } instead of defining it on figure {}, you will work around the jagged edges. Having said that, this is not relevant to my question as I am asking WHY it is happening, not for workarounds as I can think of a few myself.Prismoid
@SimonV. removed the hack. Hope the explanation stands up to scrutiny now.Nibble
T
0

http://codepen.io/marczking/pen/KwMgaR

So after playing around (used background-image and pseudo-elements, changes nothing...) you notice that this light border is only visible if you apply round corners. So I am assuming here it has to do how the Browser renders the CSS, nothing wrong with the CSS-rules ^^)

<figure>
  <figcaption>Demo</figcaption>
</figure>

figure {
  background-color: red;
  width: 400px;
  height: 400px;
  border-radius: 100px;
  position: relative; /* For caption */

}
figure::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0;
  right: 0;
  bottom: 0;
  background: url("http://placehold.it/400x400") no-repeat;
  border-radius: 100px; /* Forced on image for smooth transition */
  transition: opacity 1s ease-out;
}

figcaption {
  position: absolute;
  top: 100%;
  left: 0;
  width: 100%;
  color: hotpink;
  text-align: center;
  transition: top 1s ease-out;
}
figure:hover::before {
  opacity: 0;
}
figure:hover figcaption {
  top: 50%;
}
Temper answered 17/12, 2014 at 13:45 Comment(5)
marczking: I am aware that it only occurs when using 50% for the border-radius and that it probably is related to browser rendering. I tested this in all relevant browsers and they all display the same result. It's just suspicious that all browsers would be doing the same "mistake", so it would be good if someone could at least confirm it is indeed related to browser rendering.Prismoid
well in my pen above it also occurs when using 100px and not 50%, the size of the border-radius does not seem to matterTemper
marczking: you seem to be right about this. So basically, it occurs when any wrapping element uses border-radius in combination with overflow: hidden ... Is this just something browsers are not supporting properly?Prismoid
also in my example I do not use overflow: hidden just to reduce the possibilities that it has nothing to do with a combination of rulesTemper
Again, you seem to be right. Either the overflow: hidden on the wrapper has to stay (to only display the visible area) or the border-radius on the contained elements needs to stay. So basically, it has nothing to do with either, but more about 2 elements being in exactly the same position with a curved border. I updated the issue accordingly.Prismoid
E
0

You could add a new tag with an opacity of 0 then have that fade in with the image fading out.

figure {
  width: 400px;
  height: 400px;
  border-radius: 50%;
  overflow: hidden;
  position: relative; /* For caption */
}

background {
  background-color: red;
  width: 400px;
  height: 400px;
  border-radius: 50%;
  overflow: hidden;
  opacity: 0;
  position: fixed;
  z-index: 5;
  transition: opacity 1s ease-out;
}

img {
  border-radius: 50%; /* Forced on image for smooth transition */
  width: 100%;
  transition: opacity 1s ease-out;
  position: relative;
  z-index: 100;
}

figcaption {
  position: absolute;
  top: 100%;
  left: 0;
  width: 100%;
  color: hotpink;
  text-align: center;
  transition: top 1s ease-out;
  z-index: 10000;
}
figure:hover img {
  opacity: 0;
}
figure:hover background {
  opacity: 1;
}
figure:hover figcaption {
  top: 50%;
}
<figure>
  <background></background>
  <img src="http://placehold.it/400x400" alt>
  <figcaption>Demo</figcaption>
</figure>

Notice I added the background tag and removed background-color from figure

Erickericka answered 13/1, 2015 at 19:25 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.