Mosaic Thumbnail

Creates a mosaic from a list of images. Tries to keep the ratio as much as it can. Between one and 4 images, the mosaic will used cropped versions if it needs to. After 4 images it will pick and choose given their original ratio.

by Virginie LE GUEN BERTHEAUME

HTML

<div class="thethumbnailwillgohere"></div>
<div class="alltheimages">
  <img src="https://loremflickr.com/320/240?random=1&lock=1" />
  <img src="https://loremflickr.com/320/240?random=2&lock=2" />
  <img src="https://loremflickr.com/320/240?random=3&lock=3" />
  <img src="https://loremflickr.com/320/240?random=4&lock=4" />
  <img src="https://loremflickr.com/320/240?random=5&lock=5" />
  <img src="https://loremflickr.com/320/240?random=6&lock=6" />
  <img src="https://loremflickr.com/100/400?random=1&lock=7" />
  <img src="https://loremflickr.com/200/400?random=1&lock=8" />
  <img src="https://loremflickr.com/200/400?random=2&lock=9" />
  <img src="https://loremflickr.com/500/500?random=1&lock=10" />
  <img src="https://loremflickr.com/500/500?random=2&lock=11" />
  <img src="https://loremflickr.com/500/500?random=3&lock=12" />
</div>

CSS

body {
  margin: 0;
  padding: 0;
}

.thethumbnailwillgohere {
  margin: 10px;
  border: 5px solid purple;
}

.alltheimages>img {
  display: block;
  width: 100%;
}

.atscript-thumbnail {
  position: relative;
  width: 100%;
  padding-bottom: 100%;
  height: 0;
}

.atscript-thumbnail[class*="comb"]>div {
  display: grid;
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
}

.atscript-thumbnail.comb1>div {
  grid-template: 'div1 div2' 3fr 'div3 div4' 3fr / 3fr 3fr;
}

.atscript-thumbnail.comb2>div {
  grid-template: 'div1 div2' 3fr 'div1 div3' 3fr / 3fr 3fr;
}

.atscript-thumbnail.comb2.sym>div {
  grid-template: 'div2 div1' 3fr 'div3 div1' 3fr / 3fr 3fr;
}

.atscript-thumbnail.comb3>div {
  grid-template: 'div1 div1' 3fr 'div2 div3' 3fr / 6fr 6fr;
}

.atscript-thumbnail.comb3.sym>div {
  grid-template: 'div2 div3' 3fr 'div1 div1' 3fr / 6fr 6fr;
}

.atscript-thumbnail.comb4>div {
  grid-template: 'div1 div1 div1' 3fr 'div2 div3 div4' 3fr / 2fr 2fr 2fr;
}

.atscript-thumbnail.comb4.sym>div {
  grid-template: 'div2 div3 div4' 3fr 'div1 div1 div1' 3fr / 2fr 2fr 2fr;
}

.atscript-thumbnail.comb5>div {
  grid-template: 'div1 div2' 2fr 'div1 div3' 2fr 'div1 div4' 2fr / 3fr 3fr;
}

.atscript-thumbnail.comb5.sym>div {
  grid-template: 'div2 div1' 2fr 'div3 div1' 2fr 'div3 div1' 2fr / 3fr 3fr;
}

.atscript-thumbnail.comb6>div {
  grid-template: 'div1 div2' 3fr 'div1 div3' 1fr 'div4 div3' 2fr / 4fr 2fr;
}

.atscript-thumbnail.comb6.sym>div {
  grid-template: 'div2 div1' 3fr 'div3 div1' 1fr 'div3 div4' 2fr / 2fr 4fr;
}

.atscript-thumbnail.comb7>div {
  grid-template: 'div1 div2' 6fr / 3fr 3fr;
}

.atscript-thumbnail.comb8>div {
  grid-template: 'div1' 3fr 'div2' 3fr / 6fr;
}

.atscript-thumbnail.comb9>div {
  grid-template: 'div1 div2' 3fr 'div1 div3' 3fr / 4fr 2fr;
}

.atscript-thumbnail.comb9.sym>div {
  grid-template: 'div2 div1' 3fr 'div3 div1' 3fr / 2fr 4fr;
}

.atscript-thumbnail.comb10>div {
  grid-template: 'div1 div1' 4fr 'div2 div3' 2fr /...

JavaScript

function createATThumbnail($imgs, toDataUrl) {
  function mapNb(n, start1, stop1, start2, stop2) {
    return ((n - start1) / (stop1 - start1)) * (stop2 - start2) + start2;
  }
  var $newDiv = $('<div class="atscript-thumbnail"><div></div></div>');
  if ($imgs.length > 0) {
    var portrait = {
      1: [],
      2: [],
      3: [],
      4: [],
      5: [],
      6: []
    };
    var landscape = {
      1: [],
      2: [],
      3: [],
      4: [],
      5: [],
      6: []
    };
    for (var i = 0; i < $imgs.length; i++) {
      var $hiddenParents = $($imgs[i]).parents('.hidden');
      $hiddenParents.removeClass('hidden');
      var ar = Math.trunc(($imgs[i].clientWidth / $imgs[i].clientHeight) * 6);
      if (ar > 6) {
        ar = Math.trunc(($imgs[i].clientHeight / $imgs[i].clientWidth) * 6);
        if (!(ar in landscape))
          landscape[ar] = [];
        landscape[ar].push(i);
      } else {
        if (!(ar in portrait))
          portrait[ar] = [];
        portrait[ar].push(i);
      }
      $hiddenParents.addClass('hidden');
    }
    var comb = [];
    if (portrait[6].length >= 4) {
      comb.push(1, 1, 1, 1);
    }
    if (portrait[3].length >= 1 && portrait[6].length >= 2 && $imgs.length == 3) {
      comb.push(2, 2, 2);
    }
    if (landscape[3].length >= 1 && portrait[6].length >= 2 && $imgs.length == 3) {
      comb.push(3, 3, 3);
    }
    if (landscape[3].length >= 1 && portrait[4].length >= 3) {
      comb.push(4, 4, 4, 4);
    }
    if (portrait[3].length >= 1 && landscape[4].length >= 3) {
      comb.push(5, 5, 5, 5);
    }
    if (portrait[6].length >= 1 && portrait[4].length >= 2 && landscape[3].length >= 1) {
      comb.push(6, 6, 6, 6);
    }
    if (portrait[3].length >= 2 && $imgs.length == 2) {
      comb.push(7, 7);
    }
    if (landscape[3].length >= 2 && $imgs.length == 2) {
      comb.push(8, 8);
    }
    if (portrait[4].length >= 3 && $imgs.length == 3) {
      comb.push(9, 9, 9);
    }
    if (landscape[4].length >= 3...