Tuesday 17 January 2017

Responsive Image Grid in HTML5 with Packing Algorithm

As far as Im concerned there is always a need to create an image grid in a web page somewhere, and now with HTML5 and phone browsers it needs to be responsive. But what if you don't want or can't have scrolling? You need the images to resize to fit their container guaranteeing that the screen looks good whatever the browser - like an iPhone etc.

But there are problems.
If you think about what you want to do its very complex. You want the browser to draw up a grid of images that remain inside a box that can be collapsable, so in response the images need to alter their size to exactly fit the right number inside the space available.
Lets say you have a box thats 80% screen width and 50% view height. When you look at that in a desktop with Safari, great - you've got loads of screen real estate. Look at it on an iPhone and its another ball game. The images would need to shrink right down. Ive assumed a beginning image size that works in a browser window at a decent size.
Then you have to calculate within our 80%x50% div, lets say we have a grid of 30 images, what size do you make those images? so that you get the maximum image ( which has to remain in proportion ) width and all thirty images to fit inside the DIV? how to do that.

In fact the concept of a dynamic container is fraught with issues for designing something on screen. The ideal of text flow round images, flowing text that responds in any browser is great in theory. As soon as you want to apply design criteria it all starts to become much more difficult. JQuery is not a magic bullet as that will return often incorrect results depending on the situation, like text flow around images giving incorrect container heights and widths. So in short - there is no simple way to make a collapsable image grid.

Of course there are out of the box solutions that work. Gridifier is once such solution, but personally I run loads of JS scripts already and adding more just complicates things, especially when what you want is fairly simple. Packery is another example, thats a JQuery plugin. But do you need all of those features? I did find some online code solutions, but in the end converting the maths was the best road ahead.

Javascript offers the simplest solution. But it also requires a packing algorithm - relatively complex maths. I like to do a dry version of any problem, or thinking it through. If your grid is 80%x50% at 1024x768 with an image of 180x90. Without responsiveness this is a piece of cake, you make the div  at the correct percentage size and bang the images in the HTML, with a display:inline-block. Of course when you look at the same grid on an iPhone its a disaster. The screen size reduces, the resulting grid div is greatly reduced, the proportion changes as 40% of the height is very different, and the images are way too big to remotely fit.

So the answer is the packing script that calculates the correct size given the new dimensions. Do I need to explain this? basically it works out if the horizontal or vertical axis is the best fit and gives the greatest number of fits. I use JQuery to pass an object for calculating values like width. Its also worth noting this does not use any @media query to adapt the page.

var x = $(".examples").height(); // this passes the DIV height
var y = $(".examples").width(); // this passes the DIV width
     
var n = 30; // number of boxes
var aia  = x*y/n;
     
var px = Math.ceil(x/Math.sqrt(aia/0.782)); // 0.782 just happens to be the proportion of the image I used for the calculating of the width. Original image is 202px x 158px, which is a ratio of 0.782

if (Math.floor(px*y/x)*px<n){
      sx = y/Math.ceil(px*y/x);
} else {
      sx = x/px;
      }
var py = Math.ceil(y/Math.sqrt(aia));
if (Math.floor(py*x/y)*py<n){
     sy = x/Math.ceil(x*py/y);
} else {
sy = y/py;
}
     
var s = Math.max(sx,sy);
     
s = s - 5; // this is just to reduce the width to allow some padding
     
$('.examples img').attr('width', s);

The last line manipulates the img of the container DIV to make the images resize as needed. The container DIV is made to be proportional and the resulting width is then handed to the top vars x,y. This script could be made to work on a resize but in the context of a web page landing on a given size device I guess that doesnt matter too much although you could have a screen resize from an pad or phone changing aspect, but most people are going to simply refresh the page anyway.

1 comment: