Live Album Previews with CSS3 and jQuery

Today we are going to make a script for previewing the contents of an album with a slideshow-like animation. This can be used in photo galleries, online shops, profile pages and more. The example is inspired by Facebook, where you hover over an album and get a slideshow of some of the photos contained inside it.

Let’s begin with the HTML.

The HTML

The first step is to lay down the HTML foundation of today’s example. This is a standard HTML5 document:

index.php

<!DOCTYPE html> <html>    
 <head>       
  <meta charset="utf-8" />      
   <title>Album Previews with CSS3 and jQuery | Tutorialzine </title>      
    <!-- Our stylesheet -->    
     <link rel="stylesheet" href="assets/css/styles.css" />  
    </head>  
   <body>     
     <div id="main">          
        <a href="#" data-images="assets/img/thumbs/11.jpg|assets/img/thumbs/10.jpg"            
        class="album">       
             <img src="assets/img/thumbs/4.jpg" alt="People" />      
              <span class="preloader"></span>   
              </a>          
        <!-- More albums will go here -->    
      </div>       
   <!-- JavaScript Includes -->      
   <script src="http://code.jquery.com/jquery-1.8.3.min.js"></script>   
      <script src="assets/js/script.js"></script>     
    <script src="assets/js/albumPreviews.js"></script>   
   </body> </html>

The #main div holds the markup of the albums:

<a href="#" data-images="assets/img/thumbs/11.jpg|assets/img/thumbs/10.jpg" class="album"> 
   <img src="assets/img/thumbs/4.jpg" alt="People" />  
  <span class="preloader"></span> 
</a>

 

Each album is a link, which would normally point to the full album page, where the user would see all the photos in the album (it points to # here). The album also includes a data attribute that holds the URLs of images contained in it (it is a good idea to point to thumbnails and not the full images). In the jQuery part of the tutorial, we will get these URLs and append them as real images to the album link, and animate them.

Inside the link, there is the initial image of the album (which would be displayed even if JavaScript is disabled). This image should be different from the ones included in the data attribute. Last is the preloader span, which displays a transparent PNG that is rotated using CSS3. I decided to go this route instead of using a GIF, as the PNG format supports proper transparency and looks better.

However, it would be too much work to write the HTML for all the albums manually. This is the perfect opportunity to throw some PHP to generate it automatically.

index.php

$  albums = array(   
  'People' => array(        
         'assets/img/thumbs/4.jpg',       
          'assets/img/thumbs/11.jpg',    
             'assets/img/thumbs/10.jpg'),    
 // More albums go here ); 
 foreach ($  albums as $  name => $  a) {   
   ?>     
 <a href="#" data-images="<?php echo implode('|', array_slice($  a,1))?>" class="album">     
   <img src="<?php echo $  a[0]?>" alt="<?php echo $  name?>" />    
    <span class="preloader"></span>  
   </a>   
   <?php  }

 

You can see that I am using the array_slice function when building the data attribute, so that the default image is not repeated (otherwise you would see the same image twice in the slideshow).

With this out of the way, let’s write some JavaScript!

The JavaScript

As with the other tutorials on the site, I am using the jQuery library to make writing JavaScript easier.

The main functionality of this example takes the form of a portable jQuery plugin. On the mouseenter event, the plugin looks for the data-images attribute, parses it and appends the images to the album. It then starts a slideshow which is automatically stopped on the mouseleave event:

assets/js/albumPreviews.js

(function($  ) {    
  $  .fn.albumPreviews = function() {    
      return this.each(function(){       
       var album = $  (this),           
      loop = null, images = $  ();       
       if(!album.data('images')){          
       // The data-images attribute is missing. Skip this album.      
           return true;             }          
    var sources = album.data("images").split('|');     
         album.on('mouseenter', function(){   
               if(!images.length){             
        // The images have not been loaded yet       
               $  .each(sources,function(){       
                  images = images.add('<img src="' + this + '" />');      
               });                      // Start the animation after the first photo is loaded      
               images.first().load(function() {              
           album.trigger('startAnimation');           
          });              
        album                 
        .append(images)            
             .addClass('loading');     
            }              
   else{                 
    // Start the animation directly         
            album.trigger('startAnimation');     
            }           
   }).on('mouseleave', function(){        
         album.trigger('stopAnimation');   
          });              
// Custom events:            
  album.on('startAnimation',function(){   
               var iteration = 0;   
               // Start looping through the photos   
              (function animator(){          
            album.removeClass('loading');   
                   // Hide the currently visible photo,   
                  // and show the next one:         
             album.find('img').filter(function(){   
                      return ($  (this).css('opacity') == 1);                    
 }).animate({                   
      'opacity' : 0          
           }).nextFirst('img').animate({    
                     'opacity' : 1           
          });         
             loop = setTimeout(animator, 1000);  // Once per second    
              })();           
   });           
   album.on('stopAnimation',function(){        
          album.removeClass('loading');    
             // stop the animation     
            clearTimeout(loop);       
      });      
    });   
   };   
   // This jQuery method will return the next     
// element of the specified type, or the    
 // first one if it doesn't exist     
 $  .fn.nextFirst = function(e) {    
     var next = this.nextAll(e).first();   
      return (next.length) ? next : this.prevAll(e).last();    
 };  
})(jQuery);

I am using two custom events to organize my code better startAnimation and stopAnimation. They are triggered on mouseenter/mouseleave. The animation is handled by the animator function, which is called once every second with a timeout. You can tweak this to your liking.

And here is how it is used:

assets/js/script.js

$  (function() {  	
// Initialize the plugin 	
$  ('#main .album').albumPreviews(); 
 });

 

This will activate the plugin and set up the mouseenter/mouseleave event handlers on the elements. All that we have to do now is to add some neat CSS to make it look the part.

The CSS

I will only present the more interesting part of the stylesheet here. You can see the rest of the CSS rules in the assets/css/styles.css.

The albums have the .album class. This makes it easy to style them:

.album{ 	
width:140px; 	
height:140px; 
	margin: 15px 5px; 
	position:relative;  
	display:inline-block; 
	border: 4px solid #F0F0F0; 
	background-color: #F0F0F0;  
	border-radius:12px; 	
box-shadow:0 -2px 0 #616161;  	

/* Reflections below the image */ 
	-webkit-box-reflect: below 0 
-webkit-linear-gradient(rgba(255,255,255,0) 0%,rgba(255,255,255,0) 80%, rgba(255,255,255,0.1) 100%); 
}  

/* Showing a subtle shadow on the borders of the image */ 
.album:before{ 	
content: ''; 	
top: -1px; 
	left: -1px; 
	right: -1px; 
	bottom: -1px; 
	z-index:1000; 
	position: absolute; 
	box-shadow: 0 0 2px rgba(0, 0, 0, 0.4) inset; 
	border:1px solid #fff; 
} 

/* The album photos (hidden by default) */ 
.album img{ 	
top:0; 
	left:0; 
	opacity:0; 	
width:140px; 
	height:140px; 
	position:absolute;
 }  

/* The first (the default) thumbnail is visible */
 .album img:first-child{ 	opacity:1; } 
 .album img, .album:before{ border-radius: 10px; }  

/* The preloader PNG. It is rotated with a CSS keyframe animation */ 
.album .preloader{ 	display:none; }  
.album.loading .preloader{ 	content:''; 
	position:absolute; 
	width:18px; 	
	height:18px; 	
	background:url('../img/preloader.png') center center; 
	top: 0; 	
	left: 0; 	
	right: 0; 	
	bottom: 0; 	
	margin:auto; 
	display:block;  

/* Configure a keyframe animation for Firefox */ 
	-moz-animation: rotate 1s linear infinite;  

/* Configure it for Chrome and Safari */ 
	-webkit-animation: rotate 1s linear infinite; }  

/* Webkit keyframe animation */
 @-webkit-keyframes rotate{ 	
0%{		
-webkit-transform:rotate(0deg);} 	
100%{	-webkit-transform:rotate(360deg);} 

}  

/* Firefox Keyframe Animation */ 
@-moz-keyframes rotate{ 	
0%{		-moz-transform:rotate(0deg);} 	
100%{	-moz-transform:rotate(360deg);} }

The .preloader png is shown the first time you hover your mouse on the album. Then the script starts loading the images. If you are on a speedy connection you might not see it, but it is good to have, to give users on slow networks the feeling that something is happening in the background. The preloader is animated with a CSS keyframe animation which is repeated an infinite number of times.

With this our live album preview is complete!

Done!

You can use this example to enhance visitor’s experience when using your site.

All you have to do to use the example in your site, is to generate the HTML markup of the albums and include the plugin.