3

I'm trying to center and scale an image inside a container. In the following diagrams, the pink box is a 16:9 container and the blue box is the image.

enter image description here If the image is wider than the container, it will scale to fit.

enter image description here If the image is taller than the container, it will also scale to fit.

enter image description here If the image fits in the container, it will simply be centered.

As you can see in the diagrams, there is also a caption div aligned to the bottom left of the image, and a close icon aligned to the top right.

Here is the code I have now:

/* The page */
.container {
  width: 100%;
  height: 100%;
}

/* 16:9 container */
.imageWrapper {
  padding-bottom: 56.25%;
  position: relative;
  width: 100%;
  border: 1px solid red;
}

.imageInnerWrapper {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
  display: flex;
  align-items: center;
  justify-content: center;
}

/* The image, footer and close button all need to fit inside the container */
.imageAndFooterWrapper {
  display: inline-flex;
  flex-direction: column;
  position: relative;
  border: 1px solid lightblue;
}

.image {
  max-height: 100%;
  object-fit: contain;
}

.footer {
  text-align: left;
}

.closeButton {
  position: absolute;
  top: -30px;
  right: -30px;
}
<div class="container">
  <div class="imageWrapper">
    <div class="imageInnerWrapper">
      <div class="imageAndFooterWrapper">
        <img class="image" src="https://images.theconversation.com/files/204986/original/file-20180206-14104-1hyhea9.jpg?ixlib=rb-1.1.0&rect=0%2C1212%2C5550%2C2775&q=45&auto=format&w=1356&h=668&fit=crop">
        <div class="footer">
          Caption
        </div>
        <div class="closeButton">X</div>
      </div>
    </div>
  </div>
</div>

The following CodePen contains the above code, with some examples of different sized images.

https://codepen.io/anon/pen/NMKxxm

Jared Chu
  • 2,757
  • 4
  • 27
  • 38
Zephyr
  • 225
  • 1
  • 3
  • 14
  • This is possibly a duplicate of this: https://stackoverflow.com/questions/12991351/css-force-image-resize-and-keep-aspect-ratio -- if not an exact duplicate, there still may be info there to help you. – kshetline Apr 17 '18 at 07:31
  • @Zephyr why don't have remove **** tag and use background-image property with background size ? – user6016913 Apr 17 '18 at 07:34
  • @kshetline Thanks but my issue is quite different and specific, I'm not only trying to deal with a single image but with specifically sized containers and the absolute positioned close icon and footer. I did actually incorporate that answer already, my image has `max-height: 100%; object-fit: contain;` to keep the ratio and size within `imageAndFooterWrapper`. – Zephyr Apr 17 '18 at 08:00
  • @user6016913 I can't use `background-image` because it doesn't know the width of the image, and the footer and close button both need to know the dimensions of the image for absolute positioning. (Sorry I posted an incorrect comment earlier, saying the problem was it cropped the image) – Zephyr Apr 17 '18 at 10:00
  • @Zephyr Do you get your answer or you are still stuck in design issue, if yes then I shall try to give you an answer. Actually I keep eye on question but due to lack of time I did not able to give you an answer but now I am able , so first ask you – Dipak Apr 17 '18 at 17:13
  • @Dipakchavda I do get my answer, actually the code I have on my production project roughly works for the cases where the image is too wide horizontally, and where it fits in the container. It is the same code as I posted minus a lot of parent containers (in the CodePen it doesn't work for fitting horizontally which might be due to the missing parent divs). My problem is that my current solution doesn't work for images that are too tall. – Zephyr Apr 18 '18 at 00:54
  • @zephyr means still issue persist with a tall image right? – Dipak Apr 18 '18 at 03:14

1 Answers1

1

This may not be a direct answer to your question but usually when I have to do something like this, I use a div with a background image instead of an img tag. Using a div with a bg image allows you to use styles like background-image, background-position and background-size which allow you to create the effect as described by you.

Sample:

var imgDiv = $('.image')[0];
var closeButton = $('.fixed-el')[0];
var img = document.createElement('img');
img.src = getComputedStyle(imgDiv).backgroundImage.split('"')[1];

var calculate_positions = {
  img_width: img.naturalWidth,
  img_height: img.naturalHeight,
  img_ratio: function() {
    return calculate_positions.img_width / calculate_positions.img_height;
  },
  elm_ratio: function(elm) {
    return $(elm).width() / $(elm).height();
  },
  img_offset: function(elm) {
    var offset = []; //[x,y]

    if (calculate_positions.elm_ratio(elm) > calculate_positions.img_ratio()) {
      //centered x height 100%
      var scale_percent = $(elm).height() / calculate_positions.img_height;
      var scaled_width = calculate_positions.img_width * scale_percent;
      var x_offset = ($(elm).width() - scaled_width) / 2;
      offset = [x_offset, 0];
    } else {
      //centered y width 100%
      var scale_percent = $(elm).width() / calculate_positions.img_width;
      var scaled_height = calculate_positions.img_height * scale_percent;
      var y_offset = ($(elm).height() - scaled_height) / 2;
      offset = [0, y_offset];
    }
    return offset;
  }
}

function updatePosition() {
  var offset = calculate_positions.img_offset($('div.image'));
  closeButton.style.top = offset[1] + 'px';
  closeButton.style.left = offset[0] + 'px';
}
$(window).resize(updatePosition)
$(img).load(function() {
  updatePosition();
});
div.image {
  width: 100%;
  background-image: url('http://via.placeholder.com/100x100');
  background-position: center;
  background-size: contain;
  background-repeat: no-repeat;
  flex: 1;
}

html,
body,
div.container {
  width: 100%;
  height: 100%
}

div.container {
  display: flex;
  position: relative;
  flex-direction: column;
}

div.fixed-el {
  position: absolute;
  width: 20px;
  height: 20px;
  background: red;
}

div.caption {
  text-align: center;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="container">
  <div class="image"></div>
  <div class="caption">Some caption here</div>
  <div class="fixed-el"></div>
</div>

EDIT: You can change the image size in the styles and resize the window to see the scaling in action.

I also noticed the comment which mentioned that you do not want to use background image as it will clip the image. This will not happen if you use background-size:contain

EDIT 2: It turns out you can actually figure out what the coordinates of the image are and position other elements around it. Have created a dirty hack to demonstrate this (mixed jQuery and vanilla JS, no proper scoping etc).. But you should be able to get the main idea and implement a neater solution. The main idea is derived from this question on SO

Chirag Ravindra
  • 4,760
  • 1
  • 24
  • 35
  • Thanks for your reply. I just tried this again and remembered why it didn't work for me (I said in an above comment that it cropped the image but that was the wrong reason). I need the footer and close button to be absolute positioned right next to the image, and _with `background-image` it doesn't know the width of the image_. The result is that the footer sits in the very bottom left corner of the page and the close button sits in the very top right corner, while the image could be a small image in the center of the page. – Zephyr Apr 17 '18 at 09:55
  • @Zephyr ah. That makes sense. Sorry this didn't help. I found [this question on SO](https://stackoverflow.com/questions/14587584/get-xy-coords-height-width-of-div-background-image-when-using-background-size?utm_medium=organic&utm_source=google_rich_qa&utm_campaign=google_rich_qa) which explains how you could calculate the coordinates of the image but that requires us to know what the size of the image is before hand which may not be possible in this case. – Chirag Ravindra Apr 17 '18 at 10:05
  • @Zephyr have updated my answer with a fixed div which is absolutely positioned to match the image's top left corner. – Chirag Ravindra Apr 17 '18 at 10:34