Update: I’ve done a updated version, with scrolling, and various input boxes for changing the tags to search for here: http://jsbin.com/upinek/15. Enjoy.
Someone posted recently on Stackoverflow asking how the new Flickr Justified photo pages had been created. i.e. how to create multiple rows of evenly spaced images without cropping the images to fit. It was something I had been wondering about for a while, so I did a bit of investigation.
A similar question had been answered to do with the Google+ photo page, but that uses cropped thumbnails to create the effect, something I thought it would be better to avoid.
I’m not 100% sure that this is indeed how Flickr do it, but it produces such nice results that I thought I would show you what I came up with.
The key to this method is to make the height of the row variable.
The first thing I did was pick an arbitrary height, the maximum you want each row to be. Then get the width of the parent div, in this case a div with class “picrow”. Then keep a copy of all the widths of the photos, scaled to match this height. I also set an margin to be included later at 5 px.
1 2 3 4 5 6 7 8 9 10 |
var border = 5; var h = 320; var w = $("div.picrow").innerWidth(); var ws = []; $.each(photos, function(key, val) { var wt = parseInt(val.width_n, 10); var ht = parseInt(val.height_n, 10); if( ht != h ) { wt = Math.floor(wt * (h / ht)); } ws.push(wt); }); |
Then I counted the number of photos that are going to appear in the row, along with their accumulated width until I reach the width of the parent div.
1 2 3 4 5 6 7 8 9 10 |
// number of images appearing in this row var c = 0; // total width of images in this row - including margins var tw = 0; // calculate width of images and number of images to view in this row. while( tw * 1.1 < w) { tw += ws[c++] + border * 2; } |
Then you can calculate the ratio of the actual width of the div to the accumulated width of the photos to include in the row.
Finally, take the images, one at a time, and resize and add to the div. Again, keep track of the actual accumulated width.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
// Ratio of actual width of row to total width of images to be used. var r = w / tw; // image number being processed var i = 0; // reset total width to be total width of processed images tw = 0; // new height is not original height * ratio var ht = Math.floor(h * r); while( i < c ) { var photo = photos[i]; // Calculate new width based on ratio var wt = Math.floor(ws[i] * r); // add to total width with margins tw += wt + border * 2; // Create image, set src, width, height and margin (function() { var img = $('<img/>', {class: "photo", src: photo.url_n, width: wt, height: ht}).css("margin", border + "px"); var url = "http://www.flickr.com/photos/" + photo.owner + "/" + photo.id; img.click(function() { location.href = url; }); $("div.picrow").append(img); })(); i++; } |
That is the basics. In the full code I have added multiple rows, and support for refreshing on resizing of the window, and making it easy to change the tag searched for.
You can view the full code on JSBin here: http://jsbin.com/upinek/3/edit#javascript,html and you can see it running here: http://jsbin.com/upinek/3
There is certainly room for scope for adding other features, such as dynamically creating new rows where there is more content, and adding overlays to show photo titles etc., but hopefully this gives you a place to start. There is also an issue if one of the images it especially wide which I haven’t tracked down yet, so its not perfect by any means.
Hi! I wrote the code on flickr.com. That is the right algorithm, or at least it is the one we used. A lot of random special cases came up and those were the bulk of the work (you already noticed the wide image issue). At some point I will try to write up what we did.
Thanks. Glad i was on the right lines.
You guy did a really great work :) It’s awesome!! :D
I was just wondering how pagination could be implemented with that.
Pagination is left as ‘homework’ for you to think about :)
Ideally you’d want some form of infinite scroll feature like google image search, but I don’t know how you’d do that.
Don’t forget, as a third party app you’re not meant to display more than 30 photos at a time as well, something I hadn’t considered initially.
Thanks for the reply.
Yeah, i want something similar to Google or Twitter. I have done it before, but never loading pictures with Javascript on the fly.
What do you mean about loading more than 30 pictures?
Thanks again.
Thanks Sam, I am the questioner on Stack Overflow. Your solution works like a charm. :)
Hi Sam,
first of all, big ups for your solution. it’s great!
i tried to list instead my public photos and changed the following lines:
method: "flickr.photos.search",
api_key: "dbb49a0e2dcc3958834f1b92c072be62",
tags: tags,
tag_mode: "all",
sort: sort,
to:
method: "flickr.people.getPublicPhotos",
user_id: "[userid]"
everything works fine, the array i get back has the same structure, and the html is appended correctly but i saw no images.
that made me look into the code where i found out that my photoArray’s photo items don’t have any properties
like “url_n” or “height_n” or “width_n” which you use for calculating the image’s dimension and url.
in my array there are only _l, _m and _z versions of the photo, although i still have
extras: "url_n,url_m,url_z,url_l"
in my json call.do you have any idea why i can’t get any _n versions of my public photos?
thanks!
martin
It is possible that url_n is only returned by flickr.photos.search. You can add user_id to the flickr.photos.search call as well and that should work fine.
Hi Sam!
Thank you for your work!
I tried to repeat this effect and did it without “div” containers for rows
http://jsbin.com/awihew/10
Hey Sam this is great, thanks for posting it!
I’m having an issue though, in your example on jsbin the first two picrow divs contain nothing so there are only every two rows of images. I have checked it in firefox and chrome on a mac and chrome on windows 7. Any idea of what is going wrong?
Thanks. There is a bug in the ‘3’ version which is fixed in the ’15’ version. The first two photos returned by Flickr don’t have a url_n size, so no width is returned and the calculation produces an invalid value, which means the first two rows are being skipped. Hope that helps.
It’s look great, but what do you suggest to fix the problem with empty space on the right side of the screen?
Just got it :)
pictest should be 100% not 90%
div#picstest { height: 0px; width:100%; }
I think there is error in code. I tested a lot and found out that if there are, lets say, 11 results, only 8 photos will be shown (Flickr search “bonto” -> interesting photos). In my example JSON return 27 results, but only 25 photos was shown.
have you fixed this issue?
thanks!
Yes, it only displays a small number of rows, and only full rows at that, no partial rows. Not an error, just the way I wrote it.
Very nice post !!!! I wanted to create a gallery with this layout and your post is helping me lot. I think there is little tricky problem at the code that makes the photos wrap sometimes.
You calculate a ratio (r) and you use this to downsize the images but you have to do the downsizing at the border too or downsize the image+borders using r and then use that result-(2*border) for image width , else the total width may be larger than the container div width.
Thanks a lot for sharing ! Your code rocks !
Hey fantastic job on cracking the flicker algorithm.
Wondering is there a way to set a max photo width?
You could do that, but then you would have to modify the height of the row, or skip the image.
hi,
I want to create a different gallery for some albums I have in my computer. I want to organize the view of the gallery in this way. how can I do that?
If I’m not wrong you take the images directly from Flickr…
and…is it possible to incorporate such code in WordPress? or create a an external link to such created pages of galleries?
thank so much!!
WordPress issue is solved….
I want to fix also the problem with partial row…
thanks
I also need this for a gallery website I am making. I see that the row sometimes breaks and there is a small img tag without a src inside the code. Is this because of flickr? Would it work with my own pictures?
Hi there,
I stumbled upon this page looking for possible layouts for a gallery website, really a neat idea.
Just two questions about you code (sorry, if I got that wrong):
– you resize the pictures by the ratio r calculated to fit your original row
into you container; however the margin around the images is fixed.
doesn’t this produce a slight error, if there’s a different number of
images per line (probably it’s just a few pixels)?
– in the last loop, you stress to “keep track of the actual accumulated
width” (tw). however, that variable is never read, so i don’t really see
why you’re doing this..
cheers, and thanx for your post, Christoph
The ‘border’ variable is added per image, to compensate for the margin added to each image.
The ‘tw’ variable is used in plenty of places, so not quite sure about your comment – see the updated version 15 link at the start of the page.
Hey Sam,
Incredible work.
Was wondering if there was a way to get this working from a regular directory on a web server. I have most of my photos spread across some subdirectories and would like to display all of them in this way. I guess I know how to get it working with PHP, but purely jQuery would be awesome!!
The problem with that is that the code replies on there being different sized images available, as we don’t really want to have to scale images too much. I’m sure it could be done, but it would involve a pretty significant re-write.
I wrote a jquery plugin to do it the same, couple of months back. Here is the link in case anyone is interested – https://github.com/kalyan02/flowr-js
Hi Sam,
Incredible work.
If I’m not wrong you take the images directly from Flickr.com
and…is it possible to incorporate such code for my own photo gallery I have create a image array in javascript & image having different height and width . What pieces of code i have to change. Plz… Help. I have to implement it tomorrow.
Has anyone used this, but with an XML file as a list of photo inputs (src, url, etc.)?