Ian Y. Ian Y. - 5 months ago 19
CSS Question

Can flexbox horrizontally and vertically center images without streching them?

I want to make images centered horizontally and vertically inside their containers. And if their width or height is greater than their containers' width or height, then make them automatically shrink while keeping their proportions.

The following CSS code is what I use on the container to try to achieve the goal:

display: -ms-flexbox;
display: -webkit-flex;
display: flex;
-ms-flex-direction: column;
-webkit-flex-direction: column;
flex-direction: column;
-ms-flex-pack: center;
-webkit-justify-content: center;
justify-content: center;


And here are three examples on jsFiddle:


  • The first example is an image whose width is greater than its height. It works fine.

  • The second example is an image whose height is greater than its width. But in this example, its width stretches to container's width, and that isn't what I want.

  • The third example has the same problem as the second example.



Besides, I know the goal can also be achieved by using CSS position and transform. But such a method often creates 1 pixel gap between the resized image and the container's border. That is, the resized image fails to touch the container's border, at where it should do. Therefore I have to resort to CSS flexbox.

Answer

The problem is that the initial value of align-items is stretch and the initial value of align-self is auto, so the images are stretched.

So you need one of the following:

.flex-container {
    align-items: center;
}
.flex-item {
    align-self: center;
}

* {
  margin: 0;
  padding: 0;
}
div {
  display: flex; /* Magic begins */
  flex-direction: column;
  justify-content: center; /* Center in main axis */
  align-items: center; /* Center in cross axis */
  margin: 20px auto;
  width: 300px;
  height: 300px;
  border: 2px solid #000;
}
img {
  max-width: 100%;
  max-height: 100%;
}
<div>
  <img src="http://lorempixel.com/400/200/" alt="" />
</div>
<div>
  <img src="http://lorempixel.com/200/400/" alt="" />
</div>
<div>
  <img src="http://lorempixel.com/50/50/" alt="" />
</div>

But note that the middle image is still a bit stretched. That's because if the images are taller than the container, they will shrink vertically but not horizontally (it would be the opposite with a row layout).

To prevent that, use object-fit (not supported by IE and Edge):

img {
  object-fit: contain;
}

* {
  margin: 0;
  padding: 0;
}
div {
  display: flex; /* Magic begins */
  flex-direction: column;
  justify-content: center; /* Center in main axis */
  align-items: center; /* Center in cross axis */
  margin: 20px auto;
  width: 300px;
  height: 300px;
  border: 2px solid #000;
}
img {
  max-width: 100%;
  max-height: 100%;
  object-fit: contain;
}
<div>
  <img src="http://lorempixel.com/400/200/" alt="" />
</div>
<div>
  <img src="http://lorempixel.com/200/400/" alt="" />
</div>
<div>
  <img src="http://lorempixel.com/50/50/" alt="" />
</div>