7

With am <img> (of unknown dimensions) inside a container set to display:flexbox, is it possible to satisfy the following criteria:

  1. If the image's native width is smaller than the container's width, it is horizontally centered within the container.

enter image description here

  1. If the image's native height is smaller smaller than the container's height, it is vertically centered with the container.

enter image description here

  1. If the image's native width is greater than the container's width, the image is scaled to the width of the container.

enter image description here

  1. If the image's native height is greater than the container's height, the image is scaled to the height of the container.

enter image description here

I've noticed big disparities between how Chrome and Firefox treat images that are flexbox children. Chrome gets halfway there, but squashes the image vertically. Firefox gets halfway there, but squashes the image horizontally: Codepen

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

.Container {
  height: 100%;
  width: 100%;
  background: red;
  display: flex;
  justify-content: center;
  align-items: center;
}

img{
  max-height: 100%;
  max-width: 100%;
}
<div class="Container">
    <img src="http://d13rap2ac6f4c9.cloudfront.net/image_assets/quarry_medium.jpg?1410945487">
</div>

This is the closest I can get Codepen, which fails on 4.:

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

.Container {
  height: 100%;
  width: 100%;
  background: red;
  display: flex;
  justify-content: center;
  align-items: center;
}

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

img{
  max-height: 100%;
  max-width: 100%;
  width: 100%;
  height: auto;
}
<div class="Container">
  <div class="ImageWrapper">
    <img src="http://d13rap2ac6f4c9.cloudfront.net/image_assets/quarry_medium.jpg?1410945487">
  </div>
</div>

I know that this kind of behaviour was previously impossible without JavaScript, but I'm surprised that it doesn't seem to be achievable using flexbox.

Note: I'm not looking for a JavaScript solution or a solution that doesn't work in modern browsers yet.

In response to David Mann's answer, here is the previous example using object-fit Codepen.

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

.Container {
  height: 100%;
  width: 100%;
  background: red;
  display: flex;
  justify-content: center;
  align-items: center;
}

img{
  max-height: 100%;
  max-width: 100%;
  object-fit: contain;
}
<div class="Container">
    <img src="http://d13rap2ac6f4c9.cloudfront.net/image_assets/quarry_medium.jpg?1410945487">
</div>

Here is a Codepen using the polyfill. Appears to work OK in IE10 but broken in IE11.

Undistraction
  • 42,754
  • 56
  • 195
  • 331
  • I thin your demo is working well (testing in Chrome), I think the problem is, the images don't scale proportionally on screen resize (the width is not growing proportionally in Chrome) but when you resize the window then let the page reload, it scales fine. Do you really need it to scale when the window is resized? – KiiroSora09 Jul 16 '15 at 11:45
  • @KiiroSora09 Unfortunately the same is not true of Firefox which squashes the image even on Page reload. – Undistraction Jul 16 '15 at 11:54
  • Sorry, I don't have Firefox on me right now. Maybe you could combine flex with object-fit (from David's answer below). So object fit will work on modern browsers, and fallback on flex for IE 11. – KiiroSora09 Jul 16 '15 at 12:00
  • @KiiroSora09 Yep. I think that might be the way to go. – Undistraction Jul 16 '15 at 12:04
  • Apart from the other approaches I [suggested](http://stackoverflow.com/a/31454727/483779) below @Pedr you can set the container to `flex-direction: column;` that [works](http://jsfiddle.net/ka6poxmh/) fine on Chrome, BUT not quite on Firefox (when you make the viewport shorter than the image's intrinsic height, the aspect ratio changes). I've seen similar questions multiple times - for the problems of setting images as direct flex items, yet, no still good answer, this [answer](http://stackoverflow.com/a/28773178/483779) is the closest I have seen, hope it helps. – Stickers Jul 16 '15 at 20:14

2 Answers2

9

CSS is catching up. It doesn't have the greatest support yet, but object-fit: contain does exactly what you want (EDIT 11/17: Edge now has partial support making IE11 the only browser with no support). Just put it in your css rule for img and change max-width: 100% and max-height: 100% to width: 100% and height: 100%. Here's a codepen.

.Container {
  height: 100vh;
  width: 100vw;
  background: red;
  display: flex;
  justify-content: center;
  align-items: center;
}

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

body {
  margin: 0;
}
<div class="Container">
  <img src="https://unsplash.it/2000/1500">
</div>

If IE's lack of support for object-fit is a deal breaker, then there is a javascript polyfill for it. Here's a bit more about it from css-tricks.

You can also get the same effect with background-image but that just feels like cheating a bit.

Just add this to .Container:

background-image: url(http://d13rap2ac6f4c9.cloudfront.net/image_assets/quarry_medium.jpg?1410945487);
background-size: contain;
background-repeat:no-repeat;
background-position: center;

Then completely remove the img tag. The effect is the same, but without an actual element in .Container. It should be noted that with this method display: flex isn't even needed.

Here's another codepen for this method.

.Container {
  height: 100vh;
  width: 100vw;
  background: red;
  background-image: url(https://unsplash.it/2000/1500);
  background-size: contain;
  background-repeat: no-repeat;
  background-position: center;
}

body {
  margin: 0;
}
<div class="Container">
</div>

The support here is much better and there are even more things you can play with. Once again, here's an almanac entry from css-tricks

David Mann
  • 2,074
  • 1
  • 17
  • 17
2

Approach 1: using CSS table cell, see the following demos.

div {
    border: 1px solid red;
    width: 100px;
    height: 100px;
    display: table-cell;
    text-align: center;
    vertical-align: middle;
}
img {
    max-width: 100%;
    max-height: 100%;
    vertical-align: middle;
}
<div><img src="//dummyimage.com/75x50" /></div>
<div><img src="//dummyimage.com/50x75" /></div>
<div><img src="//dummyimage.com/300x200" /></div>
<div><img src="//dummyimage.com/200x300" /></div>
<div><img src="//dummyimage.com/200x200" /></div>

Approach 2: using inline block, and see the notes below.

div {
    border: 1px solid red;
    width: 100px;
    height: 100px;
    display: inline-block;
    text-align: center;
    vertical-align: top;
}
div:before {
    content: "";
    height: 100%;
    display: inline-block;
    vertical-align: middle;
}
img {
    max-width: 100%;
    max-height: 100%;
    vertical-align: middle;
}
<div><img src="//dummyimage.com/75x50" /></div>
<div><img src="//dummyimage.com/50x75" /></div>
<div><img src="//dummyimage.com/300x200" /></div>
<div><img src="//dummyimage.com/200x300" /></div>
<div><img src="//dummyimage.com/200x200" /></div>

Additional notes: set font-size:0; on the parent container when there is empty space inside it, and then reset it to font-size:16px; or so on the inner container if there is text content inside. Follow this for more - https://stackoverflow.com/a/5078297/483779

Community
  • 1
  • 1
Stickers
  • 75,527
  • 23
  • 147
  • 186