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 a nice and fresh image gallery. The idea is to show the albums as a slider, and when an album is chosen, we show the images of that album as a beautiful photo stack. In the photo stack view, we can browse through the images by putting the top most image behind all the stack with a slick animation.
We will use jQuery and CSS3 properties for the rotated image effect. We will also use the webkit-box-reflect property in order to mirror the boxes in the album view – check out the demo in Google Chrome or Apple Safari to see this wonderful effect.
We will also use a bit of PHP for getting the images from each album.
So, let’s start!
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
In our HTML structure we will have several elements. The main ones are for the album columns with thumbnail and description, and the preview with the photo stack.
The structure for the album view is going to look as follows:
<div id="ps_slider" class="ps_slider"> <a class="prev disabled"></a> <a class="next disabled"></a> <div id="ps_albums"> <div class="ps_album" style="opacity:0;"> <img src="albums/album1/thumb/thumb.jpg" alt=""/> <div class="ps_desc"> <h2>Album title</h2> <span>Description text</span> </div> </div> <div class="ps_album" style="opacity:0;"> ... </div> ... </div> </div>
The opacity of the album div is going to be 0 in the beginning, we will then use JavaScript to fade in the columns.
The information that we need to provide in the HTML is the location of each album and its thumbnail images. With our little PHP script we will then get all the images from the respective album.
The structure for the dark overlay and the preview with the photo stack is going to look like this:
<div id="ps_overlay" class="ps_overlay" style="display:none;"></div> <a id="ps_close" class="ps_close" style="display:none;"></a> <div id="ps_container" class="ps_container" style="display:none;"> <a id="ps_next_photo" class="ps_next_photo" style="display:none;"></a> </div>
We will dynamically insert the pictures of each album into the ps_container div.
Now, let’s take a look at the PHP.
The PHP
Our PHP file will be called from our JavaScript and we will get the information with AJAX call. The PHP looks as follows:
$location = 'albums'; $album_name = $_GET['album_name']; $files = glob($location . '/' . $album_name . '/*.{jpg,gif,png}', GLOB_BRACE); $encoded = json_encode($files); echo $encoded; unset($encoded);
The CSS
The style for the gallery is going to contain some CSS3 properties and also a Webkit specific property for a mirror effect.
Let’s start by resetting some paddings and margins and defining the general style for the body:
body, ul, li, h1, h2, h3{ margin:0; padding:0; } body{ background:#121212; font-family:Arial, Helvetica, sans-serif; font-size:11px; color:#fff; overflow-x:hidden; }
We will continue with the style of the preview. The overlay is going to have the following style:
.ps_overlay{ z-index:90; background:#111; width:100%; height:100%; position:fixed; top:0px; left:0px; opacity:0.5; }
The container for the photo stack will have a defined height and width. We will position it absolutely and center it with the “50%/negative margins” method:
.ps_container{ width:480px; height:350px; position:absolute; top:50%; margin-top:-175px; left:50%; margin-left:-240px; z-index:100; }
The image will have a thick white border and a nice box shadow. We will center the image relatively to its container but since we will only know the width and height of the images once we dynamically add them, we will set the margins in our JavaScript function:
.ps_container img{ border:10px solid #fff; position:absolute; top:50%; left:50%; -moz-box-shadow:1px 1px 10px #000; -webkit-box-shadow:1px 1px 10px #000; box-shadow:1px 1px 10px #000; }
The close button for the preview is going to be fixed at the top right of the window:
a.ps_close{ background:#000 url(../images/close.png) no-repeat center center; cursor:pointer; width:56px; height:56px; position:fixed; right:10px; top:10px; z-index:1000; -moz-border-radius:10px; -webkit-border-radius:10px; border-radius:10px; opacity:0.6; }
We will show a “next photo” element on top of the current image when the user hovers over it:
a.ps_next_photo{ position:absolute; top:50%; left:50%; width:56px; height:56px; margin:-28px 0 0 -28px; z-index:200; cursor:pointer; background:#000 url(../images/next_photo.png) no-repeat 50% 50%; opacity:0.6; -moz-border-radius:10px; -webkit-border-radius:10px; border-radius:10px; }
The hover classes for the last two elements:
a.ps_next_photo:hover, a.ps_close:hover{ opacity:0.8; }
Now we are going to define the style for the album view and its columns. The slider container will be positioned relatively. With the auto values for the left and right margins we center the container horizontally on the page:
.ps_slider{ width:845px; height:300px; position:relative; margin:110px auto 0px auto; }
The navigation elements are going to have the following style:
.ps_slider a.next, .ps_slider a.prev{ position:absolute; background-color:#000; background-position:center center; background-repeat:no-repeat; border:1px solid #232323; width:20px; height:20px; top:50%; margin-top:-10px; opacity:0.6; -moz-border-radius:5px; -webkit-border-radius:5px; border-radius:5px; cursor:pointer; outline:none; } .ps_slider a.prev{ left:-30px; background-image:url(../images/prev.png); } .ps_slider a.next{ right:-30px; background-image:url(../images/next.png); } .ps_slider a.prev:hover, .ps_slider a.next:hover{ border:1px solid #333; opacity:0.9; }
We also define a class for the navigation elements when they are disabled:
.ps_slider a.disabled, .ps_slider a.disabled:hover{ opacity:0.4; border:1px solid #111; cursor:default; }
Each album column is going to have the following style:
.ps_slider .ps_album{ width:140px; height:310px; padding:10px; background-color:#333; border:1px solid #444; position:absolute; top:0px; text-align:center; cursor:pointer; -moz-box-shadow:1px 1px 4px #000; -webkit-box-shadow:1px 1px 4px #000; box-shadow:1px 1px 4px #000; -webkit-box-reflect: below 5px -webkit-gradient( linear, left top, left bottom, from(transparent), color-stop(0.6, transparent), to(rgb(18, 18, 18)) ); }
The last property is the Webkit box reflex that mirrors the columns. We say that it should mirror the element in a gradient like way.
We add a hover class for the whole column:
.ps_slider .ps_album:hover{ background-color:#383838; }
The thumbnail image and the content of the column are going to have the following styles:
.ps_slider .ps_album img{ height:90px; border:1px solid #444; -moz-box-shadow:1px 1px 4px #000; -webkit-box-shadow:1px 1px 4px #000; box-shadow:1px 1px 4px #000; } .ps_slider .ps_album .ps_desc{ display:block; color:#666; background:#111 url(../images/overlay.png) no-repeat bottom right; height:200px; margin-top:10px; text-align:left; line-height:20px; overflow:hidden; text-overflow:ellipsis; border:1px solid #393939; -moz-box-shadow:0px 0px 2px #000 inset; -webkit-box-shadow:0px 0px 2px #000 inset; box-shadow:0px 0px 2px #000 inset; } .ps_slider .ps_album:hover .ps_desc{ background-image:none; } .ps_slider .ps_album .ps_desc span{ display:block; margin:0px 10px 10px 10px; border-top:1px solid #333; padding-top:5px; } .ps_slider .ps_album .ps_desc h2{ margin:10px 10px 0px 10px; text-align:left; padding-bottom:5px; font-weight:normal; color:#ddd; text-shadow:0px 0px 1px #fff; border-bottom:1px solid #000; }
The loading element will show when we click on an album and all the images get loaded in the preview:
.ps_slider .loading{ background:#121212 url(../images/loading.gif) no-repeat 50% 50%; position:absolute; top:0px; left:0px; width:100%; height:100%; opacity:0.7; }
For the opacity to work in IE you need to add this filter (with the right value):
filter:progid:DXImageTransform.Microsoft.Alpha(opacity=60);
I did not want to uglify the code.
Let’s add the magic!
The JavaScript
First, we need to define some variables that help us control the album navigation and keep some elements. The variable positions saves the left value for each album column. This depends on the width of the column.
/** * navR,navL are flags for controlling the albums navigation * first gives us the position of the album on the left * positions are the left positions for each of the 5 albums displayed at a time */ var navR,navL = false; var first = 1; var positions = { '0' : 0, '1' : 170, '2' : 340, '3' : 510, '4' : 680 } var $ps_albums = $('#ps_albums'); /** * number of albums available */ var elems = $ps_albums.children().length; var $ps_slider = $('#ps_slider');
We are going to give an initial positioning to the album columns:
/** * position all the albums on the right side of the window */ var hiddenRight = $(window).width() - $ps_albums.offset().left; $ps_albums.children('div').css('left',hiddenRight + 'px'); /** * move the first 5 albums to the viewport */ $ps_albums.children('div:lt(5)').each( function(i){ var $elem = $(this); $elem.animate({'left': positions[i] + 'px','opacity':1},800,function(){ if(elems > 5) enableNavRight(); }); } );
For sliding through the albums, we will define what happens when we click on the next or previous button and create two functions for moving left or right:
/** * next album */ $ps_slider.find('.next').bind('click',function(){ if(!$ps_albums.children('div:nth-child('+parseInt(first+5)+')').length || !navR) return; disableNavRight(); disableNavLeft(); moveRight(); }); /** * we move the first album (the one on the left) to the left side of the window * the next 4 albums slide one position, and finally the next one in the list * slides in, to fill the space of the first one */ function moveRight () { var hiddenLeft = $ps_albums.offset().left + 163; var cnt = 0; $ps_albums.children('div:nth-child('+first+')').animate({'left': - hiddenLeft + 'px','opacity':0},500,function(){ var $this = $(this); $ps_albums.children('div').slice(first,parseInt(first+4)).each( function(i){ var $elem = $(this); $elem.animate({'left': positions[i] + 'px'},800,function(){ ++cnt; if(cnt == 4){ $ps_albums.children('div:nth-child('+parseInt(first+5)+')').animate({'left': positions[cnt] + 'px','opacity':1},500,function(){ //$this.hide(); ++first; if(parseInt(first + 4) < elems) enableNavRight(); enableNavLeft(); }); } }); } ); }); } /** * previous album */ $ps_slider.find('.prev').bind('click',function(){ if(first==1 || !navL) return; disableNavRight(); disableNavLeft(); moveLeft(); }); /** * we move the last album (the one on the right) to the right side of the window * the previous 4 albums slide one position, and finally the previous one in the list * slides in, to fill the space of the last one */ function moveLeft () { var hiddenRight = $(window).width() - $ps_albums.offset().left; var cnt = 0; var last= first+4; $ps_albums.children('div:nth-child('+last+')').animate({'left': hiddenRight + 'px','opacity':0},500,function(){ var $this = $(this); $ps_albums.children('div').slice(parseInt(last-5),parseInt(last-1)).each( function(i){ var $elem = $(this); $elem.animate({'left': positions[i+1] + 'px'},800,function(){ ++cnt; if(cnt == 4){ $ps_albums.children('div:nth-child('+parseInt(last-5)+')').animate({'left': positions[0] + 'px','opacity':1},500,function(){ //$this.hide(); --first; enableNavRight(); if(first > 1) enableNavLeft(); }); } }); } ); }); }
The next helper functions deal with disabling and enabling the the navigation items:
/** * disable or enable albums navigation */ function disableNavRight () { navR = false; $ps_slider.find('.next').addClass('disabled'); } function disableNavLeft () { navL = false; $ps_slider.find('.prev').addClass('disabled'); } function enableNavRight () { navR = true; $ps_slider.find('.next').removeClass('disabled'); } function enableNavLeft () { navL = true; $ps_slider.find('.prev').removeClass('disabled'); }
In the next steps we will define what happens when we open an album. We first make the loading element appear and show the preview after we loaded all the images. The information of the source comes from an AJAX call to our PHP class. For the rotation effect, we use the rotate CSS3 property which we restrict to a certain range of degrees, so that we don’t rotate an image crazily:
var $ps_container = $('#ps_container'); var $ps_overlay = $('#ps_overlay'); var $ps_close = $('#ps_close'); /** * when we click on an album, * we load with AJAX the list of pictures for that album. * we randomly rotate them except the last one, which is * the one the User sees first. We also resize and center each image. */ $ps_albums.children('div').bind('click',function(){ var $elem = $(this); var album_name = 'album' + parseInt($elem.index() + 1); var $loading = $('<div />',{className:'loading'}); $elem.append($loading); $ps_container.find('img').remove(); $.get('photostack.php', {album_name:album_name} , function(data) { var items_count = data.length; for(var i = 0; i < items_count; ++i){ var item_source = data[i]; var cnt = 0; $('<img />').load(function(){ var $image = $(this); ++cnt; resizeCenterImage($image); $ps_container.append($image); var r = Math.floor(Math.random()*41)-20; if(cnt < items_count){ $image.css({ '-moz-transform' :'rotate('+r+'deg)', '-webkit-transform' :'rotate('+r+'deg)', 'transform' :'rotate('+r+'deg)' }); } if(cnt == items_count){ $loading.remove(); $ps_container.show(); $ps_close.show(); $ps_overlay.show(); } }).attr('src',item_source); } },'json'); }); /** * when hovering each one of the images, * we show the button to navigate through them */ $ps_container.live('mouseenter',function(){ $('#ps_next_photo').show(); }).live('mouseleave',function(){ $('#ps_next_photo').hide(); });
The nice animation of putting the current image at the back of our stack is defined in the following:
/** * navigate through the images: * the last one (the visible one) becomes the first one. * we also rotate 0 degrees the new visible picture */ $('#ps_next_photo').bind('click',function(){ var $current = $ps_container.find('img:last'); var r = Math.floor(Math.random()*41)-20; var currentPositions = { marginLeft : $current.css('margin-left'), marginTop : $current.css('margin-top') } var $new_current = $current.prev(); $current.animate({ 'marginLeft':'250px', 'marginTop':'-385px' },250,function(){ $(this).insertBefore($ps_container.find('img:first')) .css({ '-moz-transform' :'rotate('+r+'deg)', '-webkit-transform' :'rotate('+r+'deg)', 'transform' :'rotate('+r+'deg)' }) .animate({ 'marginLeft':currentPositions.marginLeft, 'marginTop' :currentPositions.marginTop },250,function(){ $new_current.css({ '-moz-transform' :'rotate(0deg)', '-webkit-transform' :'rotate(0deg)', 'transform' :'rotate(0deg)' }); }); }); });
We animate the top and left margins with certain values that create the “putting back” effect. Since our structure always shows the last image on the top, we will also need to insert the moving image at the beginning, so that it appear as last one in the stack.
Clicking on the close element brings us back to the album view:
/** * close the images view, and go back to albums */ $('#ps_close').bind('click',function(){ $ps_container.hide(); $ps_close.hide(); $ps_overlay.fadeOut(400); });
Our famous resize function resizes the image according to the given container width and height.
In the end of the function we apply the sizes and we also apply the negative margins – remember, that’s the way we can center an absolute positioned element; we defined top and left in the CSS to be 50%, so now we need to pull it to the right place with these margin values.
/** * resize and center the images */ function resizeCenterImage($image){ var theImage = new Image(); theImage.src = $image.attr("src"); var imgwidth = theImage.width; var imgheight = theImage.height; var containerwidth = 460; var containerheight = 330; if(imgwidth > containerwidth){ var newwidth = containerwidth; var ratio = imgwidth / containerwidth; var newheight = imgheight / ratio; if(newheight > containerheight){ var newnewheight = containerheight; var newratio = newheight/containerheight; var newnewwidth =newwidth/newratio; theImage.width = newnewwidth; theImage.height= newnewheight; } else{ theImage.width = newwidth; theImage.height= newheight; } } else if(imgheight > containerheight){ var newheight = containerheight; var ratio = imgheight / containerheight; var newwidth = imgwidth / ratio; if(newwidth > containerwidth){ var newnewwidth = containerwidth; var newratio = newwidth/containerwidth; var newnewheight =newheight/newratio; theImage.height = newnewheight; theImage.width= newnewwidth; } else{ theImage.width = newwidth; theImage.height= newheight; } } $image.css({ 'width' :theImage.width, 'height' :theImage.height, 'margin-top' :-(theImage.height/2)-10+'px', 'margin-left' :-(theImage.width/2)-10+'px' }); } });
And that’s it! We hope you enjoyed this tutorial and find it useful!
P.S. Because of some Webkit properties, this demo is a really nice experience if you use a Webkit browser like Chrome or Safari. It will also be fully functional in all latest browsers like Firefox and IE (without those beautiful properties, though).
i change the photos, but after I changed my size or photo above or fraud
how to do change photo size and name
Eni kazemi.
Really nice! Love it! 🙂 I’m running some webpages in a none php server environment. Is there a possibility to load the images straight from a html document, say for example from within a tag or a specific ?
Thank you a lot for this great tutorial. I’ll try to translate it in Russian language =). Goog luck to you and your site!
Thank you George! And thanks for including this on your blog! Cheers, ML
Thanks i added a link from my site to your script in spanish and english
http://www.ajaxshake.com
thanks
This is a great plug in. Thank you. I am incorporating it into an existing webpage which has a very large heading area. I would need the photostack to be lower on the page as opposed to the default which is nearer the top. Where would I adjust that?
Also, IE 8.07 does not have the images in the photostack fan out randomly. They stay vertically lined up for me. But I assume this could be related to its lack of CSS3 support.
Any help with either of these would be great. Thanks.
Wow…! what can i say more ? its great ! i love it. thanks .
but firefox,IE browser seems a bit issue on display style . do anyone got solution for this ?
@NOEDIR mind if i ask you help in mysql system which access photos dynamically ?
when i click on photo it keep showing loading image it dont load pictures…
Robin,
I feel had the same problem along with others on this board. I had never worked in the php environment before and it looks like folks just need the next step in implementing this great plugin. The page will load the images only when run from the php server. As the moderator Mary Lou mentioned, download and install XAMPP at http://www.apachefriends.org/en/xampp.html.
It is really easy to install. Once it is installed, put the entire Photostack folder in an XAMPP folder. I have a PC so it is located in c://xampp/htdocs/xampp.
Then, in your browser (on a PC) enter the location, http://localhost/xampp/PhotoStack/index.html
The page should then load with the images. In order to access this page again, you must use that same link otherwise you just get the loader image.
Hope this helps others.
Excellent work on this, it’s a great demonstration of what we web developers can look forward to.
I’ve recently developed a CakePHP Flickr plugin, and added your Beautiful Photostack as a demo. It uses the Flickr photoset thumbnail, photoset name, and photoset description as the gallery details. I’ve set the demo to load only a few images per set as photos won’t show until all are loaded, but with some pre-loading optimizations this Flickr and the Photo Stack could be a wonderful combination.
http://technokracy.net/flickr_demos/demos/photostack/
so good ,i lovein this web! Thanks
Hello
great tutorial, I just have a small question I want to have a single album displayed instead of the 5 (I manage to change the css) but I don not have to scroll happens next album and the previous one, could you indicate the way m do?
thank you beforehand
Gilles
Excelente trabajo, una presentación nítida, profesional y eficiente.
Thank you for this great tutorial.
Really this post is so beautiful. Thanks for sharing.
Nice tutorial… although there is a bug that I have found..
If you click the images so quickly then at some point they all disappear..
I think you should inactive the click event until the previous image completes its animation..
It does not work with IE7. Any hack?
Thanks.
There are a bug with Chrome. It slides on the horizontal axis to position the following albums. With the laptop trackpad can be done.
Anyone know to solve this problem?
I got an issue when you double click on the images, the images disappeared
Hello
Is it possible to resize photos? If yes, how?
Thank you in advance
Hello, this gallery and this site is excellent.
But I have a doubt. How to change the number of photos to show?
I would like to put 10 instead of 5. What should I change?
Thanks and congratulations
Great work! I love it. Can i use this with asp.net?
Hello,
thanks for this beautiful work.
I have a problem, when i upload this on my ftp, the loader turn continuously.
the “jsonwrapper” doesn’t works.
have you got an idea plz ?
(sorry for my bad english)
Best regards, Foligraf’
Problem resolved, i’ve created a .htaccess and add : AddType x-mapp-php5 .php . My php version was upgraded in 5.2.
Problem resolved, i’ve created a .htaccess and add : AddType x-mapp-php5 .php . My php version was upgraded in 5.2.
great job ! very nice and easy to use.
1 question: how to reverse from left to right at start ? …easy update on js or not ?
thanks again
Really great!
But can anyone help me with just having the photo stack work on a few static thumbnails without a slider (previous/next)?
Thanks!
Has anybody got this working in wordpress? I have PHP5.2, and all works but the album does not open….
When I upload the files to the root folder of my website (outside of WordPress) everything works fine.
It must be something about the PHP path?
$location = ‘albums’;
question: how to reverse from left to right at start ? …easy update on js or not ?
thanks again
Great job!!!!!
But it doesn’t work with IE 7 and IE 8…
Any help with either of these would be great.
thanks
Hey, great photo slider, nice job.
I want to change the photo names and sizes but so far no luck in doing so.
when i change the size the new photo’s don’t show
how can i change the photo size and names
thx.
Image rename and resized solved !!
but when more folders then 9 are used then the large photo’s show up in the wrong folder
can it be like that because i use the folders in a backwards order. so the new folder is named album10, album 11 and so on and will show as first item in the gallery.
thx
Hi
Love this.
Could you expand on the Jquery random rotation. I want to apply this to a set of images within a list with. So on load each image within the list rotates a random amount (all different). I’ve tried to filter out your code, but I can’t get it to work.
Your input very appreciated!
Glennyboy
Lovely script !
But is it possible to add a album location function ?
like this :
via the .attr() function get the alt ?
so the final $album_name is basically
“folder”
so it looks in the album/folder/
instead of the album/album1/
??
* is it possible to add album locations ? myself ?
instead of using /album1/
Can I make this work with MySQL ?
can I pull out album text etc ?
How add 6th colum ?
I cant add a 10th album ?
help ?
Great Site! I finally get it to work on my local server.
just a note to those who encountered the same prob as me, that you keep on getting stuck in the loading icon.
This is due not to cross-browser compatibility nor the path issue. You got this problem due to running directly from the folder itself. You should run the index page directly from your server or localhost.
So your address will be localhost/ instead of file://…
Hi Mary,
may i ask, how to make the 6th album go to the next row so that the display will be 2 rows instead of 1 ?
Thanks in advance.
Hi !!
I would like to how can I change image size ? It seems impossible to me… please help !!
HI Cyril,
which image size are u talking about ? thumbnail or the exact one shown ?
If thumbnail: go to style.css and find this line -> .ps_slider .ps_album{ to change the height.
If exact image: I guess you have to manually change the image size on ur own.
I also have problems with the location of the images. I cant edit the path in the .php file.
How can i edit the path like this:
http://www.myurl.de/images/albums/
??
thx for comments
Super gallery ! I want to limit the display galleries in 3 columns instead of 5, can you give me the values to change? I have some difficulties! Thank you.
I have implemented it successfully, but I want to make it continuous. I don’t want it to disable the next button. Is there is anyone who can help please.
Thanks
Hi, how how display last add album on first position?
Thank you very much for this tutorial.
I was looking for something like this for my WordPress plugin Lazyest Gallery.
Your code is clean and well documented. Very easy to read and change.
Thanks, very nice gallery!
I made it work in a folder of my hosting. Now I’m trying to apply it to a website made in wordpress, but it’s not displaying the images, just the preloader. I think is has to dela with the route in the php file. Any ideas?
After downloading this stack photo gallery, photos are Not popping. Any idea, why?
thank your for your nice gallery
to resize images goto
/* * resize and center the images */
and change desired height and width