22
votes

I have a DIV which is scaled to available height using CSS flexbox. In this DIV is an image which I would like to scale along with the DIV in both dimensions. That means it should be scaled keeping its aspect ratio and the dimensions which is less than the respective DIV dimension should be centered.

I can make the image follow the width of the DIV, but not the height. Therefore, portrait images escape from the DIV bounds.

Here is a jsFiddle to demonstrate the problem.

html, body {
  height: 100%;
  margin: 0;
}
.container {
  display: flex;
  flex-direction: column;
  height: 100%;
}
.box {
  flex: 1 1 auto;
  display: flex;
  justify-content: center;
  align-items: center;
}
.box img {
  width: auto;
  height: auto;
  max-width: 90%;
  max-height: 90%;
}
<div class="container">
  <div class="box"></div>
  <div class="box" style="background: pink;">
    <img src="//dummyimage.com/300" />
  </div>
  <div class="box"></div>
</div>
5
do you want to make the image fit the height even if it's naturally smaller? (You are using a rather large picture in your example, so I'm not sure) - Ben Philipp
First thing I'd like to achieve is that it scales down as required. - languitar
Nope, sorry, the image shall retain its original aspect ration. If this one differs from the one of the container, the image should be centered along the axis that is smaller than the one of the container after resizing. - languitar
Thanks. Rephrased. Using a background image could be a last resort, since the images are the main content of the site and that should usually be represented via HTML and not as the style. But if nothing else works out... - languitar
Was the flexbox layout implemented because of the image scaling problem this pertains to, or for other reasons? (I.e.: would it be an option to change to block DIVs?) - Ben Philipp

5 Answers

15
votes

When you specify the height as a percentage value, that is a percentage with respect to the height of the parent element. It is also true with the <img> tag.

In this unknown height flexbox layout, you can use the position tricks to make the image to fit both the width and height of the flex item, and use transform tricks to do the centering.

jsFiddle

html, body {
  height: 100%;
  margin: 0;
}
.container {
  height: 100%;
  display: flex;
  flex-direction: column;
}
.box {
  flex: 1 1 auto;
  display: flex;
  justify-content: center;
  align-items: center;
  position: relative;
}
.box:nth-child(2) {
  background: pink;
}
.box img {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  width: auto;
  height: auto;
  max-width: 100%;
  max-height: 100%;
}
<div class="container">
  <div class="box"></div>
  <div class="box">
    <img src="//dummyimage.com/300" />
  </div>
  <div class="box"></div>
</div>

You can also use background image, that can make it much easier, the key is to use the value contain. See the simplified demo below.

jsFiddle

html, body {
  height: 100%;
  margin: 0;
}
.container {
  height: 100%;
  display: flex;
  flex-direction: column;
}
.box {
  flex: 1 1 auto;
}
.box:nth-child(2) {
  background: pink url(//dummyimage.com/300) no-repeat center center / contain;
}
<div class="container">
  <div class="box"></div>
  <div class="box"></div>
  <div class="box"></div>
</div>
7
votes

if you can change from flex to block:

https://jsfiddle.net/svArtist/ug6eoxfs/

as @janfoeh pointed out, using object-fit: contain makes it possible:

body, html {
    height: 100%;
    margin: 0;
    padding: 0;
    overflow:hidden;
    position:relative;
}

.container {
    width: 100%;
    max-width: 100%;
    height: 100%;
    max-height: 100%;
}

.box {
    background: yellow;
    width: 100%;
    padding: 0 5%;
    box-sizing:border-box;
    max-width: 100%;
    height: 100%;
    max-height:100%;
    position:relative;
}

.box img {
    height:100%;
    max-height: 100%;
    width: auto;
    max-width: 100%;
    margin: auto;
    position:absolute;
    top:0%;
    bottom:0%;
    left:0%;
    right:0%;
    display:block;
    object-fit: contain;
}

If the Flex Layout is needed, as a last resort you might consider using a background-image, which makes the whole thing really easy: https://jsfiddle.net/svArtist/e1c2tLme/

    background: url(http://placehold.it/300) no-repeat center center;
    background-size: contain;

Other than that, I can't find a way that doesn't involve scripting.

5
votes

Uses flexbox! (JSFiddle)

body, html {
    height: 100%;
    margin: 0;
    padding: 0;
}

.container {
    display: flex;
    flex-flow: column;
    width: 100%;
    height: 100%;
}

.box {
    flex: 1 1 auto;
    background: yellow;
    display: flex;
    justify-content: center;
    align-items: stretch;
}

.box img {
    width: 100%;
    object-fit: contain;
}

The key is to use object-fit: contain; to maintain the aspect ratio, and align-items: stretch; to ensure the image is not cut off on the left and right (might this be a bug?).

1
votes

Based on @darrylyeo answer.

.container {
  display: flex;
  flex-direction: row;
  justify-content: center;
  align-items: stretch;

  width: 100%;
  height: 100%;

  border-radius: 4px;
  background-color: hsl(0, 0%, 96%);
}

.box {
  border-radius: 4px;
  display: flex;
}

.box img {
  width: 100%;
  object-fit: contain;
  border-radius: 4px;
}
-1
votes

Delete the image tag and set it as the background of the .box div with background-size:cover;
jsfiddle 1

or, if you want to avoid cropping:

Delete the image tag and set it as the background of the .box div with background-size:contain;
jsfiddle 2