13

I am building a single page application. In one of the views I want to show an image which must take as much available space as possible:

  • most important: it must keep the aspect ratio
  • it must not be cropped
  • it must be stretched horizontally and/or vertically (without changing aspect ratio) to cover the maximum possible space
  • the size of the image and viewport are not known
  • it must be centered
  • no js must be used
  • the element must be an img element and no background must be used - I already have a background (in the container)

For example, let's say that the image is 100px x 100px, and that we have a container of 500px x 300px. The image would then be stretched to 300px x 300px, and be horizontally centered so that 100px are left as padding on both sides.

Is this possible?

Here is my non-finished code of what I am trying to accomplish.

.container1 {
  width: 500px;
  height: 300px;
  border: 2px;
  border-color: red;
  border-style: solid;
}
.container2 {
  width: 300px;
  height: 500px;
  border: 2px;
  border-color: blue;
  border-style: solid
}
.fill-vertical {
  border: 2px;
  border-style: solid;
  border-color: green;
  display: block;
  max-width: 100%;
}
.fill-horizontal {
  width: 100%;
  border: 2px;
  border-style: solid;
  border-color: green;
}
 <h1>Container 1, 500px x 300px, not filled</h1>
<div class="container1">
  <img src="https://dummyimage.com/100x100/000/fff">
</div>
<h1>Container 1, filled vertically (should be horizontally centered)</h1>

<div class="container1">
  <img class="fill-vertical" src="https://dummyimage.com/100x100/000/fff">
</div>

<h1>Container 300px x 500px, not filled</h1>

<div class="container2">
  <img class="fillIt" src="https://dummyimage.com/100x100/000/fff">
</div>

<h1>Container 300px x 500px, filled horizontally, should be vertically centered</h1>

<div class="container2">
  <img class="fill-horizontal" src="https://dummyimage.com/100x100/000/fff">
</div>

In that code I am forced to use a different class for the image depending on whether I want to stretch vertically or horizontally, but actually I want CSS to do this automatically: just one stretch class must be defined.

In short what I want CSS to do is: stretch width and/or height to fit available space, keeping aspect ratio

Jeff
  • 4,285
  • 15
  • 63
  • 115
blueFast
  • 41,341
  • 63
  • 198
  • 344

3 Answers3

16

#This can be achieved in CSS with a few changes#

The required changes are:

  • Create a new .centerImage css rule. overflow: hidden; ensures that the image does not spill out of the container. position: relative; is required as the child img will need to be positioned absolutely relative to the container.
  • Create a new .centerImage img css rule. max-height: 100%; and max-width: 100% ensures the aspect ratio is kept intact. Setting bottom, left, right and top to 0 and margin: auto; centers the image.
  • Add the centerImage class to the containing divs.
  • Change .fill-vertical to height: 100%; which will make the img fill the vertical space.
  • Change .fill-horizontal to width: 100%; which will make the img fill the horizontal space.

.container1 {
    border: 2px;
    border-color: red;
    border-style: solid;
    height: 300px;
    width: 500px;
}
.container2 {
    border: 2px;
    border-color: blue;
    border-style: solid;
    height: 500px;
    width: 300px;
}
.fill-vertical {
    height: 100%;
}
.fill-horizontal {
    width: 100%;
}
.centerImage {
    display: block;
    overflow: hidden;
    position: relative;
    text-align: center;
}
.centerImage img {
    bottom: 0;
    left: 0;
    margin: auto;
    max-height: 100%;
    max-width: 100%;
    position: absolute;
    right: 0;
    top: 0;
}
<h1>Container 1, 500px x 300px, not filled</h1>
<div class="container1 centerImage">
    <img src="https://picsum.photos/500/500">
</div>
<h1>Container 1, filled vertically (should be horizontally centered)</h1>
<div class="container1 centerImage">
    <img class="fill-vertical" src="https://picsum.photos/500/500">
</div>
<h1>Container 300px x 500px, not filled</h1>
<div class="container2 centerImage">
    <img src="https://picsum.photos/500/500">
</div>
<h1>Container 300px x 500px, filled horizontally, should be vertically centered</h1>
<div class="container2 centerImage">
    <img class="fill-horizontal" src="https://picsum.photos/500/500">
</div>

http://jsbin.com/bupuwohica/2/

#Further to the above changes, it is possible to achieve this with the CSS property object-fit#

To do this you need to:

  • Add object-fit: contain; to the image
  • Set height and width to 100%

The only caveat to this is browser support as while Firefox, Chrome, Safari and Opera have supported this for some time IE and Edge do not and will require either a polyfill or fallback.

.container {
  border: 2px solid red;
  height: 200px;
  overflow: hidden;
  resize: both;
  width: 300px;
}
img {
  object-fit: contain;
  height: 100%;
  width: 100%;
}
<p>The below div is resizable, drag the bottom right corner to see how the image scales</p>
<div class="container">
  <img alt="" src="https://picsum.photos/500/500" />
</div>

https://jsfiddle.net/efyey801/

AbdelghanyMh
  • 314
  • 2
  • 8
Hidden Hobbes
  • 13,893
  • 3
  • 38
  • 64
  • Thx. This is interesting, but is not complete: I want CSS to know *automatically* if stretching on the vertical or horizontal direction must be done. I do not know the sizes of the container/image, so I can not manually decide if fill-vertical or fill-horizontal is appropiate. I wand to use a generic "fill" class. – blueFast Feb 11 '15 at 09:30
  • A couple of years late but I've added an additional way of achieving what I believe you were after @delavnog. The only downside is that there is no support in IE/Edge. – Hidden Hobbes Jan 20 '17 at 14:31
  • 2
    `object-fit` is great and a lot easier, plus [browser compatibility](http://caniuse.com/#feat=object-fit) reaches a point where it becomes useable. – TheThirdMan Jun 21 '17 at 13:29
2

Here's one way to do it, relying on background-size. This does use img tags, as required, but the visible graphic is loaded as background to take advantage of available css rules.

.container {
  background-color: #edc;
  position: relative;
  margin: 2em;
}

.container img {
  display: block;
  width: 100%;
  height: 100%;

  background-image: url(http://www.clipartbest.com/cliparts/yck/eXb/yckeXboMi.png);
  background-size: contain;
  background-repeat: no-repeat;
  background-position: center;
}

#c1 {
  width: 400px;
  height: 100px;
}


#c2 {
  width: 400px;
  height: 600px;
}
<div id="c1" class="container">
  <img src="">
</div>

<div id="c2" class="container">
  <img src="">
</div>

Here's some more information on background-size: https://developer.mozilla.org/en-US/docs/Web/CSS/background-size

recursive
  • 83,943
  • 34
  • 151
  • 241
  • This CSS won't tell him how to calculate the center of the screen...just saying, if you're going to provide an answer please be more complete! – user1789573 Feb 10 '15 at 18:50
  • @user1789573: I've included a rule to center the background. – recursive Feb 10 '15 at 18:52
  • Well, unfortunately you didn't complete the answer. Please, refer to the rules of the community on StackOverflow. – user1789573 Feb 10 '15 at 18:53
  • @user1789573: Thanks for your feedback. I have reviewed the guidelines at http://stackoverflow.com/help/answering and made sure my answer is in compliance with them. – recursive Feb 10 '15 at 18:56
  • @jeckyll2hide: Ok, I understand. I think it may be possible to adapt this answer to accommodate that requirement. If I am able to do it, I will update this answer. – recursive Feb 10 '15 at 19:06
  • @jeckyll2hide: I've updated the answer to include `img` tags. – recursive Feb 10 '15 at 19:19
  • @recursive: interesting hack. You are giving a background do the img, but no image src ... Why is img src not working? This will not work for me, because the image must be shown *on top* of an existing background, which is already stretched in both directions to cover the container. The image must be stretched in one of the directions (keeping aspect ratio) to occupy the container, but shown on top of the background. I am upvoting, but not accepting since this is not solving my problem – blueFast Feb 11 '15 at 08:28
  • I will see if I can find a solution that accommodates these requirements. However, this technique doesn't have any problem with a background in the container. Both backgrounds can co-exist with no problem. – recursive Feb 11 '15 at 16:47
0

Yes it's entirely possible. You just need to make sure you have "Bounds" or limits on your possible max size for X or horizontal and Y or vertical limits. Once you know the max dimensions then you can make those constants to compare to. The easiest method i know of is to compare X and Y as Vectors so you can see their change. To get it centered is also easy at this point:

(((horizontalScreenLimit - widthOfImage)/2), ((verticalScreenLimit - heightOfImage)/2)')
user1789573
  • 515
  • 2
  • 8
  • 23
  • 2
    I am looking for a CSS-only solution. The rendering engine has all the data: size of the container and size of the image. How can I tell, using CSS: "stretch the image horizontally and/or vertically to fully occupy the container on at least one of the directions, and center the image in the other direction". I expect that CSS (or the rendering engine?) can perform the necessary calculations to render the image in the requested way. – blueFast Feb 10 '15 at 19:08