Scale image to fit a bounding box

359,618

Solution 1

Note: Even though this is the accepted answer, the answer below is more accurate and is currently supported in all browsers if you have the option of using a background image.

Edit 2: In the modern age, using object-fit might be an even better solution: https://developer.mozilla.org/en-US/docs/Web/CSS/object-fit

No, there is no CSS only way to do this in both directions. You could add

.fillwidth {
    min-width: 100%;
    height: auto;
}

To the an element to always have it 100% width and automatically scale the height to the aspect ratio, or the inverse:

.fillheight {
    min-height: 100%; 
    width: auto;
}

to always scale to max height and relative width. To do both, you will need to determine if the aspect ratio is higher or lower than it's container, and CSS can't do this.

The reason is that CSS does not know what the page looks like. It sets rules beforehand, but only after that it is that the elements get rendered and you know exactly what sizes and ratios you're dealing with. The only way to detect that is with JavaScript.


Although you're not looking for a JS solution I'll add one anyway if someone might need it. The easiest way to handle this with JavaScript is to add a class based on the difference in ratio. If the width-to-height ratio of the box is greater than that of the image, add the class "fillwidth", else add the class "fillheight".

$('div').each(function() {
  var fillClass = ($(this).height() > $(this).width()) 
    ? 'fillheight'
    : 'fillwidth';
  $(this).find('img').addClass(fillClass);
});
.fillwidth { 
  width: 100%; 
  height: auto; 
}
.fillheight { 
  height: 100%; 
  width: auto; 
}

div {
  border: 1px solid black;
  overflow: hidden;
}

.tower {
  width: 100px;
  height: 200px;
}

.trailer {
  width: 200px;
  height: 100px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="tower">
  <img src="http://placekitten.com/150/150" />
</div>
<div class="trailer">
  <img src="http://placekitten.com/150/150" />
</div>

Solution 2

Thanks to CSS3 there is a solution !

The solution is to put the image as background-image and then set the background-size to contain.

HTML

<div class='bounding-box'>
</div>

CSS

.bounding-box {
  background-image: url(...);
  background-repeat: no-repeat;
  background-size: contain;
}

Test it here: http://www.w3schools.com/cssref/playit.asp?filename=playcss_background-size&preval=contain

Full compatibility with latest browsers: http://caniuse.com/background-img-opts

To align the div in the center, you can use this variation:

.bounding-box {
  background-image: url(...);
  background-size: contain;
  position: absolute;
  background-position: center;
  background-repeat: no-repeat;
  height: 100%;
  width: 100%;
}

Solution 3

Here's a hackish solution I discovered:

#image {
    max-width: 10%;
    max-height: 10%;
    transform: scale(10);
}

This will enlarge the image tenfold, but restrict it to 10% of its final size - thus bounding it to the container.

Unlike the background-image solution, this will also work with <video> elements.

Interactive example:

 function step(timestamp) {
     var container = document.getElementById('container');
     timestamp /= 1000;
     container.style.left   = (200 + 100 * Math.sin(timestamp * 1.0)) + 'px';
     container.style.top    = (200 + 100 * Math.sin(timestamp * 1.1)) + 'px';
     container.style.width  = (500 + 500 * Math.sin(timestamp * 1.2)) + 'px';
     container.style.height = (500 + 500 * Math.sin(timestamp * 1.3)) + 'px';
     window.requestAnimationFrame(step);
 }

 window.requestAnimationFrame(step);
 #container {
     outline: 1px solid black;
     position: relative;
     background-color: red;
 }
 #image {
     display: block;
     max-width: 10%;
     max-height: 10%;
     transform-origin: 0 0;
     transform: scale(10);
 }
<div id="container">
    <img id="image" src="https://upload.wikimedia.org/wikipedia/en/7/7d/Lenna_%28test_image%29.png">
</div>

Solution 4

Today, just say object-fit: contain. Support is everything but IE: http://caniuse.com/#feat=object-fit

Solution 5

html:

    <div class="container">
      <img class="flowerImg" src="flower.jpg">
    </div>

css:

.container{
  width: 100px;
  height: 100px;
}


.flowerImg{
  width: 100px;
  height: 100px;
  object-fit: cover;
  /*object-fit: contain;
  object-fit: scale-down;
  object-position: -10% 0;
  object-fit: none;
  object-fit: fill;*/
}
Share:
359,618
Thomas Guillory
Author by

Thomas Guillory

Full stack web-dev I code in Ruby, Rails, Angular.js, Backbone.js... and I try some other things as soon as I have time. Lead Developper &amp; Product Manager at altagem.com

Updated on January 28, 2022

Comments

  • Thomas Guillory
    Thomas Guillory over 2 years

    Is there a css-only solution to scale an image into a bounding box (keeping aspect-ratio)? This works if the image is bigger than the container:

    img {
      max-width: 100%;
      max-height: 100%;
    }
    

    Example:

    But I want to scale up the image until a dimension is 100% of the container.

  • Khan
    Khan about 12 years
    Try to be a little more clear with your expectations before down-voting someone's answer. Especially when they've answered your question before you've cleared up your question with edits.
  • Misha Reyzlin
    Misha Reyzlin about 12 years
    Also, try to be more polite. No one is going to help you if you talk that way.
  • ericosg
    ericosg about 12 years
    @Artimuz My apologies if I miss-guided you, but it seemed unclear to me that you wished to keep aspect-ratio, which is why I commented on that specifically. Also, you'll notice that I pasted a snippet of your code from the examples. I believe keeping height: auto; will do that for you if you specify a width.
  • Thomas Guillory
    Thomas Guillory about 12 years
    Actually there is a solution: setting CSS3 background-size to contain. See my answer.
  • Stephan Muller
    Stephan Muller about 12 years
    Nice one, although my answer isn't incorrect (you were asking about the img specifically) this is a great solution and I should've thought of that. The only problem is that IE 8 is still so widely used that I wouldn't want to rely on this yet, but nice catch nonetheless.
  • Thomas Guillory
    Thomas Guillory about 12 years
    Yes you're right, your answer is not incorrect: I will mark it as accepted and integrate this specific solution in the question for those who google that.
  • arxpoetica
    arxpoetica over 11 years
    Set it to "cover" if you don't want any letterboxing: background-size: contain;
  • Andrey Mikhaylov - lolmaus
    Andrey Mikhaylov - lolmaus over 10 years
    It should be noted that you can inject image URLs with HTML: <div class=image style="background-image: url('/images/foo.jpg');"></div>. All other styles should be applied with CSS.
  • animuson
    animuson over 10 years
    I would say this is a terrible approach. By changing it to a background-image, you're removing the semantic meaning of the image in the HTML.
  • Stephan Muller
    Stephan Muller over 10 years
    You're totally right. Even though it's one and a half year later now, I edited my post.
  • Danny Dulai
    Danny Dulai over 10 years
    also it seems you lose the ability to put a border on the image if you do this.
  • duncanwilcox
    duncanwilcox over 10 years
    I'd posit that this is the correct answer for the question asked, which is what's the CSS for the img tag. This is relevant because semantically the img tag is content, the image as background to a div isn't. While some circumstances make this impractical (if you don't know image vs. container ratio), for others it's just right. Thanks.
  • Christian Wattengård
    Christian Wattengård over 10 years
    @animuson: It still helped me as I'm using HTML/CSS for printed reports, and couldn't give a quack about semantics :)
  • Forza
    Forza about 10 years
    What if I would like to change the image src using javascript? Would that still work if we use image: url?
  • Salman A
    Salman A over 9 years
    Would it be better if you put the image tag inside the container (and hide it somehow). And use this trick as well.
  • ndm13
    ndm13 over 9 years
    I love this solution. Not only does it actually answer the question (I know, what a thought), it works beautifully in all modern browsers without having to resort to Javascript!
  • Colton McCormack
    Colton McCormack over 9 years
    OP specifically asked for a css-only approach.
  • cyberwombat
    cyberwombat about 9 years
    There is an IE8 polyfill here github.com/louisremi/background-size-polyfill
  • cyberwombat
    cyberwombat about 9 years
    I'd love to see a fiddle of that. In my test of placing an img in a 400x400 container the img just gets cropped in the corner
  • XwipeoutX
    XwipeoutX almost 9 years
    This answer only works when the box is taller than it is wide.
  • XwipeoutX
    XwipeoutX almost 9 years
    Nice answer! @Yashua You need to add transform-origin: top left to stop the cropping. /Zombie
  • Codemonkey
    Codemonkey about 8 years
    Brilliant. My only concern would be a possible loss of image quality in a poorly-coded browser, but in theory it should work fine!
  • MattK
    MattK almost 8 years
    Interesting, but doesn't work on Chrome. Results in a cropped row of the image.
  • Andrey Mikhaylov - lolmaus
    Andrey Mikhaylov - lolmaus over 7 years
    Great trick, but how do I center the image within the container?
  • Zhang
    Zhang over 7 years
    IE only has about 16% market share now at the time of writing this (09/12/2016 10:56am) and continues to dwindle, so this is best answer for me :D
  • Glenn Maynard
    Glenn Maynard over 7 years
    Don't edit other people's answers to fix things other than typos or broken links. I wouldn't say something childish like "There is a polyfill for crap browsers" in an SO answer, and I don't appreciate my answer being edited to make it look like I said that. if you want to use your words, put them in your own answer.
  • BairDev
    BairDev almost 7 years
    I'd say, that not IE is the problem, but Edge. According to caniuse object-fit is still under development for it.
  • fgblomqvist
    fgblomqvist almost 7 years
    This should be one of the top-voted answers 2017 and onwards... According to the caniuse link above, 90% of all users globally now use a browser that supports this
  • gabrielmaldi
    gabrielmaldi over 6 years
    I just posted an answer which works with and without object-fit: stackoverflow.com/a/46456673/474031
  • Andrey Mikhaylov - lolmaus
    Andrey Mikhaylov - lolmaus over 5 years
    @MastaBaba, margin: auto only works for horizontal centering. It may work for vertical if container's width is preset, but that's not always the case.
  • MastaBaba
    MastaBaba over 5 years
    Right. I thought you were only referring to horizontal centering.
  • Konstantin
    Konstantin over 4 years
    I tried it without scale, just like that: max-width: 90%; max-height: 90% and it also worked. Why would you use scale, anyway?
  • Vladimir Panteleev
    Vladimir Panteleev almost 4 years
    @cyberwombat Five years too late, but I added a runnable snippet.
  • Vladimir Panteleev
    Vladimir Panteleev almost 4 years
    @Konstantin Scaling is only needed if you also want to enlarge the image (when it is smaller than the container's either width or height).
  • Timo
    Timo over 3 years
    A practical example of how the semantics matters is that, if anyone wanted to print your web page then at least Chrome by default seems to ignore background images like these from printing.. which, you know, semantically makes sense.. better to use that object-fit approach in the world today. Hardly any browsers out there not supporting it.
  • Max Waterman
    Max Waterman about 3 years
    How do you get the dimensions of the image after scaling?
  • lxhom
    lxhom almost 3 years
    To everyone who found this on Google like me: You can use object-fit to do that with pure CSS. developer.mozilla.org/en-US/docs/Web/CSS/object-fit
  • Stephan Muller
    Stephan Muller almost 3 years
    Thanks @lxhom, added that info too :)
  • Elikill58
    Elikill58 over 2 years
    Can you edit your answer to explain why your post is different/better than other ?