0

I'm loading in several images. They are various lengths and widths, but I would like for them all to feel as though they are the same "size". So, if one image is 200x100 and another image is 200x400, I would like for them both to scale in such a way that they take up the same amount of space on the screen. If I fix the width to be 200, then the second element is 4 times the size of the first.

For example:

.my_img {
    width: 200px;
}

produces this behavior

How can I use css to fix the area of an image or other element? Where by area, I mean literally length times width. I would like to load in an image of arbitrary dimensions, and scale it (preserving aspect ratio) so that its area is fixed to be a given value.

isherwood
  • 58,414
  • 16
  • 114
  • 157
Him
  • 5,257
  • 3
  • 26
  • 83
  • 1
    @closevotes, can you clarify what needs more focus? I'm not sure precisely what two questions this is asking. My question is "how to use css to fix the area of an image?". If there are other questions hidden in here, I am happy to attempt to extricate them. – Him Apr 26 '22 at 16:27
  • 1
    Your question criteria are unclear. "*So, if one image is 200x100 and another image is 200x400, I would like for them both to scale in such a way that they take up the same amount of space on the screen.*" What does this mean? How will they scale? Will smaller ones scale up *and* bigger ones scale down to some average? Can you provide a [mcve] of your current markup with a diagram or explanation of how that differs from your desired outcome? – TylerH Apr 26 '22 at 17:55

2 Answers2

2

I don't believe you can do this with CSS. While you can calculate square root with CSS in various ways, getting natural dimensions may be problematic. You'd need that in order to find the smallest image.

For a JavaScript solution, you'd have to first establish the smallest image area, then resize each down according to initial proportion, maintaining aspect ratio.

const images = document.querySelectorAll('img');
let smallestArea = 999999999;

const getSmallestImageByArea = () => {
  images.forEach(image => {
    const width = image.naturalWidth;
    const height = image.naturalHeight;

    if (width * height < smallestArea) {
      smallestArea = width * height;
    }
  });
};

const sizeImagesToSmallestArea = () => {
  images.forEach(image => {
    let width = image.naturalWidth;
    let height = image.naturalHeight;
    const area = width * height;

    if (area > smallestArea) {
      const areaRoot = Math.sqrt(area);
      const proportion = areaRoot / Math.sqrt(smallestArea);
      const aspectRoot = Math.sqrt(width / height);

      width = areaRoot / proportion * aspectRoot;
      height = areaRoot / proportion / aspectRoot;

      image.style.width = width + 'px';
      image.style.height = height + 'px';
    }

    // show hidden images
    image.style.display = 'inline';
    
    console.log('Initial area:', area, '| Final area:', width * height);
  });
};

// wait for images: https://stackoverflow.com/a/60949881/1264804
Promise.all(Array.from(document.images)
  .filter(img => !img.complete)
  .map(img => new Promise(resolve => {
    img.onload = img.onerror = resolve;
  }))).then(() => {
  getSmallestImageByArea();
  sizeImagesToSmallestArea();
});
/* hide images to prevent jumping effect */
img {
  display: none;
}
<img src="https://via.placeholder.com/165x250" />
<img src="https://via.placeholder.com/100x50" />
<img src="https://via.placeholder.com/200x200" />
<img src="https://via.placeholder.com/1900x300" />
isherwood
  • 58,414
  • 16
  • 114
  • 157
  • 1
    You can solve for the width and height given the area, and also given a "proportional" scale. Constants (i.e. given area A and starting image of dim WxH) are given as capital letters: `wh = A`, `w = cW`, `h = cH`, this is 3 equations with 3 unknowns (`w,h,c`). We can find `c` easily plugging the latter two into the first: `c^2WH = A` => `c = sqrt(A/(WH))`. Then, we can find `w` and `h` by plugging `c` into the latter two formulae: `w = sqrt(AW/H)` and `h = sqrt(AH/W)`. This obviates the need for the loop, and results in an exact solution up to floating point error. – Him Apr 25 '22 at 17:26
0

Maybe you can try something like this. if you want them to fit on a specific container you have to wrap them and set their height and width and let them fit on it using obejct-fit:cover. This sample have 2 different size images but they fit exactly the way I would do it no matter how big they are. Let me know if this is what you are looking at??

.my_img {
    justify-content:space-around;
    display:flex;
    
}

.my_img > img {
    object-fit: fill;
    height: 300px;
    width: 300px;
    border: 1px solid gray;
    margin: 10px;
    overflow:visible;
    }
<div class="my_img">

<img src="https://www.kindpng.com/picc/m/227-2275045_daisy-yellow-bloom-frame-flower-border-flowers-transparent.png">
<img src="https://external-content.duckduckgo.com/iu/?u=https%3A%2F%2Fwww.clir.org%2Fwp-content%2Fuploads%2Fsites%2F6%2F2016%2F09%2FWelcome-banner.png&f=1&nofb=1">

</div>
Crystal
  • 1,845
  • 2
  • 4
  • 16
  • This doesn't seem to scale the images, it crops them. For example, replacing one of the images with [this image](https://external-content.duckduckgo.com/iu/?u=https%3A%2F%2Fwww.clir.org%2Fwp-content%2Fuploads%2Fsites%2F6%2F2016%2F09%2FWelcome-banner.png&f=1&nofb=1) cuts off most of the image. – Him Apr 25 '22 at 15:26
  • you can use the object-fit:fill. but the image is not as contain as it can be. Let me know what are you trying to attain I might be able to help in some other ways. – Crystal Apr 25 '22 at 16:27