27

I want to show an image with rounded corners. So the image must stretch to the container but doesn't crop any part, like object-fit: contain. However, border-radius applies to image element, not the picture content. Here is an example (also JSFiddle):

body {
  width: 100vw;
  height: 100vh;
  margin: 0;
}

div {
  width: 100%;
  height: 100%;
}

img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  border-radius: 20%;
}
<div>
  <img src="https://cdn.pixabay.com/photo/2015/04/23/22/00/tree-736885__480.jpg">
</div>

You can check how it works when you resize the viewport.

So, is there a way to make the image element resize it's borders in both directions to adjust to the container, just like object-fit does?

Or maybe a way to apply a "crop-by-rounded-rect filter" on the image content?

frangulyan
  • 3,580
  • 4
  • 36
  • 64
  • I'm not clear on how you want it to function. What should happen when the viewport is a different aspect ratio than the image? – showdev Mar 30 '18 at 01:05
  • by saying "the image" do you mean the picture content or the html IMG element borders? I don't care about html structure, I just want to achieve the same result as in the example, but with rounded corners. Similar to what will happen if I remove border-radius from the above code and the image JPG content itself would come with rounded corners cropped. – frangulyan Mar 30 '18 at 01:09
  • The answer here - https://stackoverflow.com/a/9994936/951745 - says that "there is no CSS way to do this in both directions" meaning filling the IMG element just like object-fit does it with the content. Otherwise I would do that and apply border radius on that IMG. – frangulyan Mar 30 '18 at 01:12

4 Answers4

9

I've also had this problem and I've found a way to do it. If you set the height and width to auto the img element maintains its aspect ratio and the image touches the borders. You can then use max-width and max-height instead of width and height.

img {
  width: auto;
  height: auto;
  max-width: 100%;
  max-height: 100%;
  object-fit: contain;
  border-radius: 20%;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
}

You may also have to center the img on the parent div as now if it's smaller than the maximum size it will move to the top left.

Mestik 78
  • 103
  • 1
  • 3
  • 1
    As I wrote in the question - "the image must stretch to the container". You mentioned yourself that the image "will move to the top left", which is not what I was looking for. Even if you center it, it will not take up the whole container. You can see it yourself when you paste your css snippet into the JSFiddle in my question. – frangulyan Jan 09 '22 at 20:21
  • 2
    I had to add `margin: auto` to retain the `border-radius`. – evolross Oct 26 '22 at 21:38
3

After some research it seems like this is not possible in pure CSS. The answer here also confirms that.

In the other answer of this question the image view is not growing to "touch" the parent container thus leaving empty area around it in all 4 directions and staying small somewhere centered in the container. Which means it doesn't behave the same way, as the code in the question with img element taking the whole parent area and then picture content "stretched" to touch the closest borders with object-fit: contain.

frangulyan
  • 3,580
  • 4
  • 36
  • 64
2

Here is a solution that will fit the image when the container is smaller:

div {
  display: flex;
  align-items: center;
  justify-content: center;
}

img {
  border-radius: 16px;
  max-width: 100%;
  max-height: 100%;
}

If the container is bigger than the image it will just center it. Note that you probably don't wanna stretch the image at this point or it will look bad

D0m3
  • 1,471
  • 2
  • 13
  • 19
2

While it seems impossible, SVG filter might be able to get the job done, example here:

/* Wrapper is optional, it is just to illustrate the container size */
.wrap {
  background-color: #eee;
  width: 100px;
  height: 100px;
}

.bg {
  background-image: url(https://placekitten.com/1000/400);
  background-size: contain;
  background-repeat: no-repeat;
  background-position: center;
  width: 100px;
  height: 100px;
  filter: url('#filter-radius');
}

.img {
  object-fit: contain;
  width: 100px;
  height: 100px;
  filter: url('#filter-radius');
}
<div class="wrap"><div class="bg"></div></div>
<hr />
<div class="wrap">
  <img class="img" src="https://placekitten.com/1000/400" />
</div>

<!-- Magic for border radius -->
<svg style="visibility: hidden" width="0" height="0">
  <defs>
    <filter id="filter-radius">
      <!-- Create a blur of 4px radius from the original image -->
      <!-- (Transparent pixels are ignored, thus the blur radius starts at the corner of the image) -->
      <feGaussianBlur in="SourceGraphic" stdDeviation="4" result="blur" />
      <!-- Filter out the pixels where alpha values that are too low, in this case the blurred corners are filtered out -->
      <feColorMatrix
        in="blur"
        mode="matrix"
        values="1 0 0 0 0  0 1 0 0 0  0 0 1 0 0  0 0 0 100 -50"
        result="mask"
      />
      <!-- As the final result is now blurred, we need to use the mask we obtained from previous step to cut from the original source -->
      <feComposite in="SourceGraphic" in2="mask" operator="atop" />
    </filter>
  </defs>
</svg>
AngYC
  • 3,051
  • 6
  • 20