4

So I have an img box, and the user can drop an image on it. The CSS currently ensure whatever the images original size is it will be displayed as a 300x300 image using this CSS

So an image of 200x200 would be increased to fit exactly and an image 400x400 would be decreased to fit exactly in the 300x300 img element.

.largeartwork img {
    max-width:300px;
    max-height:300px;
    min-width:300px;
    min-height:300px;
    border: 3px solid black;
}

This works correctly, but what I want to do for non square images is fill the space as best as possible but preserve the aspect ratio

For example an image of 300x200 (300 pixels wide, 200 pixels height)should only fill the width up but not the height, the same with an image of 600x400 etcetera.

Note on possible duplicate The answers on linked post are outdated and not as good

Paul Taylor
  • 13,411
  • 42
  • 184
  • 351
  • 5
    Use `object-fit`: https://css-tricks.com/almanac/properties/o/object-fit/. The reason why min/max width does not help to preserve aspect ratio is because it is a paradox itself: which dimension should a non-square image prioritize? – Terry Mar 28 '18 at 14:10
  • @PeterB that answer does not seem to have object-fit as answer which seems to me to be the best answer. – Paul Taylor Mar 28 '18 at 15:15
  • A duplicate is not about the possible answers, it's about the question :-) – Peter B Mar 28 '18 at 15:19
  • @PeterB link says, This question may already have an answer here, but the main answers are outdated, so what would be advantage of closing this question with the better answers. – Paul Taylor Mar 28 '18 at 15:23
  • Then open a bounty on the target question asking for new answers – TylerH Mar 28 '18 at 17:42
  • @TylerH - Im very generous giving out bounties but why would I do that since I already the correct answer on this question, and even if better answers were given they would not beat the top answer with 493 likes ! – Paul Taylor Mar 28 '18 at 17:48
  • @PaulTaylor I'm simply letting you know the proper course of action based on your complaint. As for the score, that's irrelevant; a 3-score answer might solve your problem where a 300-score answer doesn't. This doesn't (and shouldn't) stop people from posting new answers. – TylerH Mar 28 '18 at 17:51
  • @TylerH I'm not going to do something if it doesn't make sense to me, if this question was closed then a somebody asking the same question as me would find the other question and be most likely to take one of the first answers, which IMO are not not very good. Especially in IT the right answer in 2012 is unlikely to be the right answer in 2018 so better to re-ask the question and get more currently pertinent answers – Paul Taylor Mar 28 '18 at 19:22
  • @PaulTaylor No, that is *not* better, it is worse. We are telling you explicitly how the system is designed to handle this kind of situation where you have the same question as someone else but none of the existing answers are adequate / up-to-date. There is a specific pro-forma bounty description *for that exact reason*. In the future if someone is instructing you on something and it doesn't make sense, it would be better to ask why and try to understand rather than blindly refuse. – TylerH Mar 28 '18 at 19:40
  • @TylerH implicitly I was asking you why, so why don't you give your reasons why that is better rather than simply telling me it is better as if it was a statement of fact rather than your opinion. – Paul Taylor Mar 28 '18 at 20:30

2 Answers2

7

As per my comment, using min/max widths and height do not guarantee preserving the image aspect ratio, because when resizing an image the browser needs to decide which dimension (horizontal or vertical) to give priority over. Since it cannot decide in this case, any non-square images will be squashed or stretched because their aspect ratio don't match the ones you have specified.

There are two solutions to your conundrum: one is to use object-fit, which is rather widely supported but unfortunately not in Internet Explorer. A sample markup will look like this:

<div class="largeartwork">
  <img src="/path/to/image" />
</div>

And your CSS will be:

.largeartwork img {
    width: 300px;
    height: 300px;
    border: 3px solid black;
    object-fit: contain;
}

See proof-of-concept code snippet below:

.largeartwork img {
    width: 300px;
    height: 300px;
    border: 3px solid black;
    object-fit: contain;
}
<!-- Landscape -->
<div class="largeartwork">
  <img src="https://via.placeholder.com/350x150" />
</div>

<!-- Portrait -->
<div class="largeartwork">
  <img src="https://via.placeholder.com/150x350" />
</div>

<!-- Small square image -->
<div class="largeartwork">
  <img src="https://via.placeholder.com/100x100" />
</div>

<!-- Large square image -->
<div class="largeartwork">
  <img src="https://via.placeholder.com/400x400" />
</div>

An alternative solution will be to use background-image instead. You will have to rely on JS to inject the image source as the element's background. The advantage is that background-size: contain is very widely supported, even in Internet Explorer 9. Example code:

<div class="largeartwork" style="background-image: url(/path/to/image);"></div>

And your CSS:

.largeartwork {
    width: 300px;
    height: 300px;
    border: 3px solid black;
    background-size: contain;
    background-repeat: no-repeat;
    background-position: center center;
}

See proof-of-concept code below:

.largeartwork {
  width: 300px;
  height: 300px;
  border: 3px solid black;
  background-size: contain;
  background-repeat: no-repeat;
  background-position: center center;
}
<div class="largeartwork" style="background-image: url('https://via.placeholder.com/350x150')"></div>

<div class="largeartwork" style="background-image: url('https://via.placeholder.com/150x350')"></div>

<div class="largeartwork" style="background-image: url('https://via.placeholder.com/100x100')"></div>

<div class="largeartwork" style="background-image: url('https://via.placeholder.com/400x400')"></div>
Boris D. Teoharov
  • 2,319
  • 4
  • 30
  • 49
Terry
  • 63,248
  • 15
  • 96
  • 118
  • I've learned something new here from this question and your answer. Good stuff. Led me to this article on a polyfill for IE/Edge regarding object-fit: https://medium.com/@primozcigler/neat-trick-for-css-object-fit-fallback-on-edge-and-other-browsers-afbc53bbb2c3 – wlh Mar 28 '18 at 14:39
  • @Terry , thanks the object-fit works perfectly and is simple to do so that is good enough for me, IE support doesnt really concern me. – Paul Taylor Mar 28 '18 at 15:11
1

When max and min value of the width or height is the same it means fixed value like this:

.largeartwork img {
    width:300px;
    height:300px;
}

In your case, You can solve your problem with this method:

When the user dropped the image file you can check which side of the image is bigger and you can add the correct class to the image tag. for example:

create CSS class like this:

.largeartwork img.biggerwidth {
    width: 300px;
    height: auto;
}
.largeartwork img.biggerheight {
    width: auto;
    height:300px;
}

and when the user dropped the image like 200x500 pixels, you can create your image like this:

<img src="..." class="biggerheight">

Why is this better than background-image and object-fit?

On those methods, If the image size doesn't cover all of the element area, you will get white space on the element. Since you are using border on the element, those methods always create border around the max size you specified.

ICE
  • 1,667
  • 2
  • 21
  • 43
  • 1
    Thankyou good solution, I understand your concerns about object-fit however the white space border is not an issue for me and use of object-fit is alot simpler than me having to work out the image size in Javascript and change CSS. So to keep my code succinct Im going to stick with object-fit – Paul Taylor Mar 28 '18 at 15:13