rocksNwaves
rocksNwaves

Reputation: 6154

fit images with different aspect ratios into multiple rows evenly

Good morning.

First, thanks in advance! I've been a stack overflow spectator for quite a while, and you guys are great.

I am looking to create a photo layout for my webpage www.eden-koru.com, where photos are presented in rows. Due to cropping, and different cameras, each photo may have different aspect ratios and therefor there are many uneven gaps when just placed in a row.

A perfect example of what I want to do is www.flickr.com/childe-roland. Those are my photos, all laid out perfectly despite aspect ratio.

On a different, but similar question I found an 80% solution with this JSFiddle http://jsfiddle.net/martinschaer/aJtdb/:

var container_width = $('#container2').width();
var container_width_temp = 0.0; // must be float!
var container_height = 100.0; // random initial container heigth for calculations

$('#container2 img').each(function(){
    var newwidth = (this.width / this.height) * container_height;
    this.width = newwidth;
    $(this).data('width', newwidth); 
    container_width_temp += newwidth;
});

$('#container2 img').each(function(){
    this.width = $(this).data('width') * (container_width / container_width_temp);
});

Now, that only works for one row. I have no experience with JQuery, but I was able to see the math and create a "row_counter" variable that counted the number of image wrapper divs... That got me to 90%. I just multiplied the final width by that row count, then subtracted a few pixels to make up for margins.

it looks like this:

$('.imageWrapper').each(function(){
    rows +=1;
});

My div layout looks like this:

<div class="mainWrapper">
    <div class="imageWrapper">
        <img width="326" src="images/_DSC4434.jpg"></img>
        <img width="276" src="images/_DSC4537.jpg"></img>
        <img width="254" src="images/_DSC4483.jpg"></img>
    </div>
    <div class="imageWrapper">
        <img width="276" src="images/_DSC0253.jpg"></img>
        <img width="306" src="images/The_Alaska_RangeIR.jpg"></img>
        <img width="275" src="images/DSC_9111.jpg"></img>
    </div>
    <div class="imageWrapper">
        <img width="276" src="images/_DSC4689.jpg"></img>
        <img width="276" src="images/_DSC4718.jpg"></img>
        <img width="276" src="images/_DSC4738.jpg"></img>
    </div>
</div>

and my CSS like this:

.mainWrapper {
  background-color: black;
  margin: 0 auto 50px auto;
  width: 70%;
  height: auto;
  border: 2px solid white;
  border-radius: 10px;
  clear: both;
  padding: 7px 7px 7px 7px;
  text-align: center;
  overflow: hidden;
}
.mainWrapper .imageWrapper {
  overflow: hidden;
  width: 100%x;
  margin: 0px auto 0px auto;
}
.mainWrapper .imageWrapper img {
  display: inline-block;
  border: 1px solid #fff;
}

Now, it looks better than it did, but there is still a lot of unevenness that I can't account for with styling. Additionally I can no longer use width: 100% to make my images shrink as the viewport changes.

I hope that I have given enough detail. Please keep in mind that I know nothing about JQuery and haven't touched JavaScript in 5 years. I was an IT major who joined the navy after graduation and never coded again until last week.

Cheers! Wes

Upvotes: 0

Views: 739

Answers (1)

Lennart
Lennart

Reputation: 1560

This is something quite complex. I managed to make a jQuery plugin that almost achieves what you want, I'm having some issues with making it dynamic when a user resizes their browser window, but ignoring this, it should do what you're asking for.

jQuery Plugin

(function ( $ ) {         
$.fn.gallery = function( options ) {
    var settings = $.extend({
        imgs: [],
        row_height: 300,
        margin: 10
    }, options);

    var container = $(this);

    //create a div for each image
    for(var i=0;i<settings.imgs.length;i++){
        $(this).append("<div class='imgwrapper'></div>");
    }

    //setup the css for the imgwrappers
    $("head").append("<style type='text/css'>.imgwrapper{ float: left; margin-left: "+settings.margin+"px; margin-top: "+settings.margin+"px; height: 261px; background-repeat: no-repeat; background-position: center; background-size: cover;}</style>")

    //define some global vars
    var imgs_aspect = [];
    var imgs_rows = [0];
    var tot = 0;
    var loaded = 0;

    function setup(){
        var imgs = settings.imgs;
        var row_width = 0;
        $(".imgwrapper").each(function(index){
            var imgwrapper = $(this);
            var img = new Image();
            img.src = imgs[index];
            img.onload = function(){
                //determine the aspect ratio of the image
                var img_aspect = img.height/img.width;
                imgs_aspect.push(img_aspect);
                //work out a rough width for the image
                var w = settings.row_height*img_aspect;
                row_width += w;
                //check if there is still space on this row for another image
                if(row_width >= container.width()){
                    imgs_rows.push(1);
                    row_width = 0;
                }
                else{
                    imgs_rows[imgs_rows.length-1]++;
                }
                //set some of the css vars
                imgwrapper.css("width",w+"px"); 
                imgwrapper.css("height",settings.row_height+"px"); 
                imgwrapper.css("background-image","url("+imgs[index]+")");
                loaded++;
                checkIfLoaded();
            }
        });
    }

    function checkIfLoaded(){
        //make sure all images are loaded
        if(loaded == $(".imgwrapper").length){
            setHeight();
        }
    }

    function setHeight(){
      for(var r=0;r<imgs_rows.length;r++){
        if(r==0){
          var y = 0;
        }
        else{
          var y = 0;
          for(var j=0;j<r;j++){
            y += imgs_rows[j]
          }
        }
        if(imgs_rows[r] == 0){

        }
        else{
          tot = 0;
          for(var i=y;i<(y+imgs_rows[r]);i++){
            tot += imgs_aspect[i];
          }
          //work out optimum height of image to fit perfectly on the row
          var h = ((container.width()-(settings.margin*(imgs_rows[r]+1)))/tot);
          $(".imgwrapper").each(function(index){
            if(index >= y && index < (y+imgs_rows[r])){
                //work out width using height
                var w = h*imgs_aspect[index];
                $(this).css("width",w+"px");
            }
          });
        }
      }
    }
    setup();
};

}( jQuery ));

How To Use

var images = ["http://lorempixel.com/300/300",
          "http://lorempixel.com/250/250",
          "http://lorempixel.com/200/200",
          "http://lorempixel.com/210/220",
          "http://lorempixel.com/210/230",
          "http://lorempixel.com/260/230",
          "http://lorempixel.com/410/830",
          "http://lorempixel.com/300/200",
          "http://lorempixel.com/250/250",
          "http://lorempixel.com/200/200",
          "http://lorempixel.com/210/220",
          "http://lorempixel.com/210/230",
          "http://lorempixel.com/260/230",
          "http://lorempixel.com/410/830"];
$(".container").gallery({imgs:images, margin: 0, row_height: 300});

images is an array which should contain the images url that you wish to use. The container can have any width desired (define in the css). The margin value allows you to have a similar white border around the images. Since I saw in your code that you had a row height, this is also implemented, by just changing the row_height value.

Demo: http://codepen.io/motorlatitude/pen/iHgCx

This is far from perfect, but it might give you an idea of what you need to do.

Hope it helps!

Upvotes: 1

Related Questions