From our sponsor: Meco is a distraction-free space for reading and discovering newsletters, separate from the inbox.
In this tutorial we are going to create an image gallery with a Polaroid look. We will have albums that will expand to sets of slightly rotated thumbnails that pop out on hover. The full image will slide in from the bottom once a thumbnail is clicked. In the full image view the user can navigate through the pictures or simply choose another thumbnail to be displayed.
For this gallery we will be using the 2D Transform plugin to animate rotations.
The beautiful photos used in the demo are by talented Tetsumo. Visit his blog here.
Let’s get started!
Tiny break: 📬 Want to stay up to date with frontend and trends in web design? Subscribe and get our Collective newsletter twice a tweek.
The Markup
We are going to include all our HTML in a div with the class “pp_gallery”. It will consist of the loading div, the navigation that will appear when the full image is viewed and the main thumbnails container.
Inside of the thumbnails container which will have the class “pp_thumbContainer”, we will have several divs for the albums and a div for going back to the album view. Each album will contain the thumbnails with a description wrapped in a div with the class “content”. We will also include a div element for the description of the album itself.
<div id="pp_gallery" class="pp_gallery"> <div id="pp_loading" class="pp_loading"></div> <div id="pp_next" class="pp_next"></div> <div id="pp_prev" class="pp_prev"></div> <div id="pp_thumbContainer"> <div class="album"> <div class="content"> <img src="images/album1/thumbs/1.jpg" alt="images/album1/1.jpg" /> <span>The Sixties by Tetsumo</span> </div> <div class="content"> <img src="images/album1/thumbs/2.jpg" alt="images/album1/2.jpg" /> <span>The Sixties by Tetsumo</span> </div> ... <div class="descr"> The Sixties </div> </div> <div class="album" style="bottom:-90px;"> ... </div> ... <div class="pp_back">Albums</div> </div> </div>
The HTML structure of the dynamically created full image preview will be the following:
<div id="pp_preview" class="pp_preview"> <img src="images/album1/1.jpg" /> <div class="pp_descr"><span>Description</span></div> </div>
Now, let’s take a look at the style.
The CSS
We will start with a reset and some general styles for the body:
*{ margin:0; padding:0; } body{ background:#000 url(../bg.jpg) repeat center center; font-family:"Myriad Pro", "Trebuchet MS", Helvetica, sans-serif; font-size:12px; color: #fff; overflow:hidden; }
A structured background fits very nicely, so we choose to add a wood texture. You can find this and more textures on Webtreats.
Next, we will style the loading div and the navigation for stepping through the pictures when we are in the preview mode:
.pp_loading{ display:none; position:fixed; top:50%; left:50%; margin:-35px 0px 0px -35px; background:#fff url(../icons/loader.gif) no-repeat center center; width:70px; height:70px; z-index:999; opacity:0.7; -moz-border-radius:10px; -webkit-border-radius:10px; border-radius:10px; } .pp_next, .pp_prev{ cursor:pointer; top:50%; margin-top:-16px; width:32px; height:32px; position:fixed; text-align:center; border:1px solid #111; color:#fff; -moz-box-shadow:0px 0px 3px #000; -webkit-box-shadow:0px 0px 3px #000; box-shadow:0px 0px 3px #000; } .pp_next{ right:-40px; background:#222 url(../icons/next.png) no-repeat center center; } .pp_prev{ left:-40px; background:#222 url(../icons/prev.png) no-repeat center center; }
The loading div is set to the center of the page by using the 50% negative margin trick. When the position is fixed, we can set the top and left to 50% and add a top and left margin to the negative value of half of the element’s width, or height, respectively.
The same we do for the navigation items, just that here will only center the elements vertically.
The thumbnails container will be positioned at the bottom of the page:
#pp_thumbContainer{ position:fixed; bottom:0px; left:0px; height:65px; width:100%; }
The albums are hidden initially. When we load the page, they will slide in from the bottom, so we will set the initial bottom value to -90 pixels:
#pp_thumbContainer .album{ position:absolute; width:200px; height:65px; bottom:-90px; }
The left value for the positioning of the albums will be calculated dynamically on page load. We will spread all the albums evenly throughout the width of the page.
The description of the album and the back element will share some styles:
.album .descr, .pp_back{ position:absolute; bottom:0px; left:-16px; background:#222; text-align:center; border:1px solid #111; padding:5px; cursor:pointer; width:169px; color:#fff; cursor:pointer; text-shadow:0px 0px 1px #fff; -moz-box-shadow:1px 1px 4px #000; -webkit-box-shadow:1px 1px 4px #000; box-shadow:1px 1px 4px #000; }
…but not all. We will overwrite and add the values that are specific to the .pp_back class:
.pp_back{ text-transform:uppercase; bottom:120px; left:-100px; width:80px; }
The wrapper for the image and the image title will have the following style:
#pp_thumbContainer .content{ position:absolute; top:0px; height:155px; cursor:pointer; }
These wrappers will also be spread into position dynamically in our JavaScript. We set the top value to 0, so that all the thumbnails align to the top of the thumbnails container. We don’t want vertical thumbnails to stick out.
The thumbnail will have a white border and a box shadow:
#pp_thumbContainer img{ border:5px solid #fff; -moz-box-shadow:1px 1px 7px #000; -webkit-box-shadow:1px 1px 7px #000; box-shadow:1px 1px 7px #000; }
The description for each image is invisible. We will only use it to fill the description element for the full view.
#pp_thumbContainer .content span{ display:none; }
The wrapper for the full image will be positioned outside of the page by setting the top value to 150%. Once the image is loaded, we will slide it in from the bottom. We are setting the left value to 50% since we want to center the picture. Since we don’t know the width and height of the picture yet, we cannot set any negative margins yet. We will do that in our JavaScript function.
.pp_preview{ position:fixed; top:150%; left:50%; }
The full image will have a bigger white border at its bottom where we will insert the description:
.pp_preview img{ position:absolute; top:0px; left:0px; border:10px solid #fff; border-bottom:45px solid #fff; -moz-box-shadow:1px 1px 7px #000; -webkit-box-shadow:1px 1px 7px #000; box-shadow:1px 1px 7px #000; }
The description will be at the bottom of the preview element. In the JavaScript we will set the width and height of the preview div to the one of the image dynamically, so the description will be positioned at the thicker bottom border of the image.
.pp_descr{ height:45px; line-height:45px; font-size:20px; width:100%; bottom:0px; left:0px; position:absolute; text-align:center; color:#00021c; }
We will cufonize the description using a nice hand written font (see at the end).
And now, let’s add some real magic!
The JavaScript
There will be many animations throughout the JavaScript. The albums will slide in from the bottom when the page loads. Once an album is clicked, we will spread the thumbnails evenly by giving them an according left value. When we choose a thumbnail, we need to create the preview div and slide it in from the bottom. The image will get resized so that it fits into the window.
Let’s start by defining some initial variables. First, we want to know if we are dealing with Internet Explorer since we don’t want to use the rotation of the full image here (it’s buggy when used together with the slide in animation):
var ie = false; if ($.browser.msie) { ie = true; }
We will use some index variables for the navigation and save some elements:
//current album/image displayed var current = -1; var album = -1; //windows width var w_width = $(window).width(); //caching var $albums = $('#pp_thumbContainer div.album'); var $loader = $('#pp_loading'); var $next = $('#pp_next'); var $prev = $('#pp_prev'); var $images = $('#pp_thumbContainer div.content img'); var $back = $('#pp_back');
We want to spread the albums evenly throughout the page, so we will calculate the according left value:
var nmb_albums = $albums.length; var spaces = w_width/(nmb_albums+1); var cnt = 0; //preload all the images (thumbs) var nmb_images = $images.length; var loaded = 0; $images.each(function(i){ var $image = $(this); $('<img />').load(function(){ ++loaded; if(loaded == nmb_images){ //let's spread the albums evenly at the bottom of the page $albums.each(function(){ var $this = $(this); ++cnt; var left = spaces*cnt - $this.width()/2; $this.css('left',left+'px'); $this.stop().animate({'bottom':'0px'},500); }).unbind('click').bind('click',spreadPictures); //also rotate each picture of an album with a random number of degrees $images.each(function(){ var $this = $(this); var r = Math.floor(Math.random()*41)-20; $this.transform({'rotate' : r + 'deg'}); }); } }).attr('src', $image.attr('src')); });
The spreadPictures function will do a similar thing: it will move the chosen album to the left and spread all the thumbnails:
function spreadPictures(){ var $album = $(this); //track which album is opened album = $album.index(); //hide all the other albums $albums.not($album).stop().animate({'bottom':'-90px'},300); $album.unbind('click'); //now move the current album to the left //and at the same time spread its images throughout //the window, rotating them randomly, hide the description of the album //store the current left for the reverse operation $album.data('left',$album.css('left')) .stop() .animate({'left':'0px'},500) .find('.descr') .stop() .animate({'bottom':'-30px'},200); var total_pic = $album.find('.content').length; var cnt = 0; //each picture $album.find('.content') .each(function(){ var $content = $(this); ++cnt; //window width var w_width = $(window).width(); var spaces = w_width/(total_pic+1); var left = (spaces*cnt) - (140/2); var r = Math.floor(Math.random()*41)-20; //var r = Math.floor(Math.random()*81)-40; $content.stop().animate({'left':left+'px'},500,function(){ $(this).unbind('click') .bind('click',showImage) .unbind('mouseenter') .bind('mouseenter',upImage) .unbind('mouseleave') .bind('mouseleave',downImage); }).find('img') .stop() .animate({'rotate': r+'deg'},300); $back.stop().animate({'left':'0px'},300); }); }
Now, we will define what happens when we click on the item to go back to the albums view. We will animate the album to its initial left position and slide the other albums up again. If the user was viewing a full image, we will make it slide up, out of the window (hideCurrentPicture):
$back.bind('click',function(){ $back.stop().animate({'left':'-100px'},300); hideNavigation(); //there's a picture being displayed //lets slide the current one up if(current != -1){ hideCurrentPicture(); } var $current_album = $('#pp_thumbContainer div.album:nth-child('+parseInt(album+1)+')'); $current_album.stop() .animate({'left':$current_album.data('left')},500) .find('.descr') .stop() .animate({'bottom':'0px'},500); $current_album.unbind('click') .bind('click',spreadPictures); $current_album.find('.content') .each(function(){ var $content = $(this); $content.unbind('mouseenter mouseleave click'); $content.stop().animate({'left':'0px'},500); }); $albums.not($current_album).stop().animate({'bottom':'0px'},500); });
The next function, called showImage, will display the full image by sliding it in from the bottom. If there was another image being shown, we will slide that one up. For centering the preview, we need to set its negative margins according to the width and height of the image. We will also rotate the preview randomly:
function showImage(nav){ if(nav == 1){ //reached the first one if(current==0){ return; } var $content = $('#pp_thumbContainer div.album:nth-child('+parseInt(album+1)+')') .find('.content:nth-child('+parseInt(current)+')'); //reached the last one if($content.length==0){ current-=2; return; } } else var $content = $(this); //show ajax loading image $loader.show(); //there's a picture being displayed //lets slide the current one up if(current != -1){ hideCurrentPicture(); } current = $content.index(); var $thumb = $content.find('img'); var imgL_source = $thumb.attr('alt'); var imgL_description = $thumb.next().html(); //preload the large image to show $('<img style=""/>').load(function(){ var $imgL = $(this); //resize the image based on the windows size resize($imgL); //create an element to include the large image //and its description var $preview = $('<div />',{ 'id' : 'pp_preview', 'className' : 'pp_preview', 'html' : '<div class="pp_descr"><span>'+imgL_description+'</span></div>', 'style' : 'visibility:hidden;' }); $preview.prepend($imgL); $('#pp_gallery').prepend($preview); var largeW = $imgL.width()+20; var largeH = $imgL.height()+10+45; //change the properties of the wrapping div //to fit the large image sizes $preview.css({ 'width' :largeW+'px', 'height' :largeH+'px', 'marginTop' :-largeH/2-20+'px', 'marginLeft' :-largeW/2+'px', 'visibility' :'visible' }); Cufon.replace('.pp_descr'); //show navigation showNavigation(); //hide the ajax image loading $loader.hide(); //slide up (also rotating) the large image var r = Math.floor(Math.random()*41)-20; if(ie) var param = { 'top':'50%' }; else var param = { 'top':'50%', 'rotate': r+'deg' }; $preview.stop().animate(param,500); }).error(function(){ //error loading image. //Maybe show a message : 'no preview available' }).attr('src',imgL_source); }
The next two functions handle the navigation through the images:
//click next image $next.bind('click',function(){ current+=2; showImage(1); }); //click previous image $prev.bind('click',function(){ showImage(1); });
The function hideCurrentPicture will make the preview slide up. We will not use any rotation animation if the browser is IE:
//slides up the current picture function hideCurrentPicture(){ current = -1; var r = Math.floor(Math.random()*41)-20; if(ie) var param = { 'top':'-100%' }; else var param = { 'top':'-100%', 'rotate': r+'deg' }; $('#pp_preview').stop() .animate(param,500,function(){ $(this).remove(); }); }
The showNavigation and hideNavigation functions will take care of showing and hiding the navigation items:
//shows the navigation buttons function showNavigation(){ $next.stop().animate({'right':'0px'},100); $prev.stop().animate({'left':'0px'},100); } //hides the navigation buttons function hideNavigation(){ $next.stop().animate({'right':'-40px'},300); $prev.stop().animate({'left':'-40px'},300); }
When hovering over a thumbnail, we want it to move up a little and rotate slightly:
function upImage(){ var $content = $(this); $content.stop().animate({ 'marginTop' : '-70px' },400).find('img') .stop() .animate({'rotate': '0deg'},400); }
When the mouse leaves the currently hovered thumbnail, we want it to drop back, rotating randomly again:
function downImage(){ var $content = $(this); var r = Math.floor(Math.random()*41)-20; $content.stop().animate({ 'marginTop' : '0px' },400).find('img').stop().animate({'rotate': r + 'deg'},400); }
And finally, we will use a resize function to fit the full image into the window:
function resize($image){ var widthMargin = 50 var heightMargin = 200; var windowH = $(window).height()-heightMargin; var windowW = $(window).width()-widthMargin; var theImage = new Image(); theImage.src = $image.attr("src"); var imgwidth = theImage.width; var imgheight = theImage.height; if((imgwidth > windowW)||(imgheight > windowH)){ if(imgwidth > imgheight){ var newwidth = windowW; var ratio = imgwidth / windowW; var newheight = imgheight / ratio; theImage.height = newheight; theImage.width= newwidth; if(newheight>windowH){ var newnewheight = windowH; var newratio = newheight/windowH; var newnewwidth =newwidth/newratio; theImage.width = newnewwidth; theImage.height= newnewheight; } } else{ var newheight = windowH; var ratio = imgheight / windowH; var newwidth = imgwidth / ratio; theImage.height = newheight; theImage.width= newwidth; if(newwidth>windowW){ var newnewwidth = windowW; var newratio = newwidth/windowW; var newnewheight =newheight/newratio; theImage.height = newnewheight; theImage.width= newnewwidth; } } } $image.css({'width':theImage.width+'px','height':theImage.height+'px'}); }
Since we cufonize the description, we need to add the following lines to the head of the HTML:
<script src="js/cufon-yui.js" type="text/javascript"></script> <script src="js/Note_this_400.font.js" type="text/javascript"></script>
You can find the beautiful “Note This” font on www.fontsquirrel.com.
And that’s it! We hope you enjoyed this tutorial and find it useful!!
Hey very nice article.Very helpful.Keep good work going on. 🙂
Hi,
I would like to limit the rotation of the big images to max 10 degrees. Couldt you tell me where to limit this in the code?
thanks for your help
I love this gallery! It works so well!
Hi Mary Lou! Your articles are amazing and very helpful.
I would like to use this gallery but, i want the albums, the photos and the captions information being stored in a dynamic xml that i’ve got. It can be possible?
Keep the good work;)
Love the design of this, taken it and added loads of functionality, such as server side importing (load images from a URL), drag and drop upload, also dynamic thumbnails, including animated gifs.
http://01792.org/scatter
so Cool
I like it……
I come from China.
Great Tutorial!! I do have a question as to how to make the photos appear upright all the time rather than randomly. My client wants to abstain from tilted photos. Thanks for all your help.
What I believe is missing to this gallery it’s keyboard support. At minimum the “escape” key for closing the overlayed large size photo. Otherwise… perfect as usual.
Thank you very much!
I’m from Russia. Good luck!
awesome… but i have quite a few albums, which causes them to all cram along the bottom, is there a way around this?? slider, or different columns perhaps?
also, can you have nested folders, folders within folders?
Hi, great design and concept.
got a quick question,
is there anyone here who added a text box at the right of the image?
i’m working on adding a facebook plugin on the right and make each image as “likeable” with comments on it
hope someone can share.
many thanks
Hi, This is a very great gallery. However, I have a question to simplify it. In fact, I just have one album. Also, I would have the randomned images when opening page and not one album with images to click. Do you have an idea to do that? Sorry for my bad english, Thanks a lot!
Love the design of this, taken it and added loads of functionality, such as server side importing (load images from a URL), drag and drop upload, also dynamic thumbnails, including animated gifs.
Is it possible to show only few thumbs? I have 4 albums with 20 pix each and it takes a lot of time to load 80 thumbs. Can you put gallery into div?
This is just awesome
I have a question…how can I do to by default when I click in a album open the first photo??
Thanks
Like Magda i have the problem that the gallery is too slow when i have 8 galleries with 15 pictures each.
Inside a gallery its fast, but the main screen seems to display all 120 picture-thumbnails at the same time which is too much for non-highspeed-PCs.
What could i do to only display about 3 thumbnails of each gallery at the main screen?
Thanks a lot for your help!
Thanks for this tutorial, i love jQuery… i used it on my page with another tutorial from this page… one question: how can i change the font for the gallery… etc. albums and title…
Thanks Wolfgang
This is really impressive.
Great work.
I have found a bug though.
If we click to go back to ‘albums’ whilst there is a pre-load image there, the image contiunes to load and then is always displayed on top obscuring future images.
Does anyone know how to stop the image from loading when the albums link is clicked.
It would also be good to start loading the next image to the one displayed, before ‘next’ is clicked – but that is a minor point.
Cheers!
Hi Marry!
Its great idea n great work. Woking fine with more than 53 images in one album on local system but when I’m putting it online it doesn’t support more than 6 images. Please help me because I already included this one to my project.
Hope to hear from you soon.
Thanks.
Really Awesome…
fantastic gallery using jquiry however i cant get this to work at all in internet explorer8 anyone know why,it displays web page in full but no animations or shows any images?
HI ML,
Thx so much, exactly what I was looking for. I adapted it.
Is there a limit regarding the number of pictures?
I added a lot, and at some point, after the loader-gif appears nothing happens anymore.
Thank you!!
Excuse me.. i doesn’t work on IE9 🙁
If i click on the thumb, it doesn’t load the picture. Any tips?
i love the slideshow!
i would like to adopt it for a project
there i dont need the albums – i would just like to show directly the photos.
im not so much into jquery yet.
could you give me a hint how to do that? i would appreciate it a lot..
thanks
greetings from spain, Karo
Very nice
Hey,
Very nice script, i wann to use it, but in IE9, it has problem, can u suggest how to rectify.
Also if i use latest jquery, it will not work properly, i can see the slag.
Any help would be appreciated.
Harish
lovely, as always, mary lou.
i’m curious as to why most of the tutorials do not include image protect code?
i know most earnest hackers can steal any image, but it would still be great to have the code for no copy image in each tutorial, no?
thank you,
emmanuela
Please…I’ m trying to change the font face in “span reference” but if I change font-family anything change! somebody know why? thanks
Hey! Now the DEMO of your gallery works in IE9 and this is great.
But your .zip doesn’t work on IE9.
Could you tell me why?
Thanks!
Hi Madda, please update the Cufon script, it’s probably that: http://cufon.shoqolate.com/js/cufon-yui.js?v=1.09i
Just replace it with the current file in the js folder. Hope it helps, ML
Mary, great work first of all.
I am just wondering if I can make it not full page? Because what I want to do it, using this idea but showing in a side bar div.
Thanks,
Alex.
Hi,
Thanks for sharing tutorials. Is it possible to integrate this gallery with flickr?
Thanks & Regards,
Sachin Walvekar
India.
I have a question:
photo description not album use chinese can not show?
What if I want it run automatically, I mean like going to next content is “x” second?
Is it possible to auto show up (by default) the first image of the selected album and the nav icons once the album spreads out?
Hi Mary,
this is a fantastic work!
Can It work together with slimbox (lightbox)? When i click on a thumb, than slim box opens?
It is possible?
Many Thanx
Micha
fantastic slideshow! really, but it’s not work correctly with ie 9 .
what can i do for this problem?
thank you very much if you can help me 🙂
ciao
chiara from Italy
Is it possible to auto show up (by default) the first image of the selected album and the nav icons once the album spreads out?
Hi
I tried to update the Cufon script, but it’s still not work in IE9 :(…
Hi, thanks for this amazing tutorial, I am CS student and I’m not familiar yet with javascript. So I need some help, at the following page http://www.monemone.gr/voies/index.php?option=com_php&Itemid=97&lang=el the images dont appear, I know that there is a js conflict but I can’t find a solution. Any help would be appreciated. Thanks in advance.
THANK YOU SO MUCH FOR THIS WONDERFUL PHOTO GALLERY ! YOU SAVE MY DAY :))
I have an issue with displaying the preview image at front (z-index).
I use AllWebMenu for my menu and I have the Z-index set to 1.
The preview window still shows behind menu.
I have no issue using a Model Box.
I am at a loss. Please Help.
Hi,
thanks for this nice piece of coding.
I have three questions:
1. Is there a solution to change the layout of the link back to the overview? I would replace the little “banner” (named “Album”) wit a little graphic, but the closed albums on the mainstage using the same CSS. I found no solution to split the code.
2. I didn’t try it yet, but is there a way to embedding a video (internal or hosted external on vimeo)?
3. Is there a way to ad some more info to a picture? I like the way to show only the name for a pic, but I would love it to show a bit more content to my visitors. A good way can be, to have a little black/transparent window (typical under some OS X apps) who the visitor can open separate or open in the same time with the picture. Any ideas or tipps?
Thanks,
Robin
Hi all,
first let thank you for this great tutorial. Still i have a tiny issue.
I’d like to use this script for a gallery with only one album and so i would like to skip the albums spread and directly spread the pictures of my only album. I am really a newbie but i’m sure it’s easy to set this…
thanks for any help
Bye
J
Holaaa, thank you very much for sharing great job: D
There will be some way to make not rotate so much?… bone that the pictures did not turn ( to turn ) much to change from aa another, please,
please tell me which parameter modified
Best Regards
Like JON75 mentioned, can i skip the albums spread and directly spread the pictures of my only album?
Thanks for help.
Wow!! I love it! Looks amazing!
really amazing. thx. i use it at the following :
http://www.facebook.com/clorismurphycollection?sk=app_251887678163643
hey Mary,
can u plz help me out.. how do i make it cross browser. coz its not working in chrome 15.0.874.106 m.
is any buddy hv any idea how to solve this problm..
I tried to install this amazing plugin but I get an error message: “The plugin does not have a valid header.”
Could you please tell me how to fix that?