Live Album Previews with CSS3 and jQuery

Demo Download

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!

live-previews.jpg

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. The preview effect can come in handy when presenting a large list of products (like the google product search tutorial where it would be neat to have additional product photos presented in this manner), albums, videos, user profiles and more.

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.

Bootstrap Studio

The revolutionary web design tool for creating responsive websites and apps.

Learn more

Related Articles

Alexander

Nice tutorial! CSS3 is a marvel for designers!

Hariharakumar

I like the way images are displayed when mouse is hovered. Thanks for the nice tutorial.

Nice one. Will implement this in my demo showcase site.

How can I set it up where I click on each thumb image and it takes me to a page?

Martin Angelov

Put the URL of the pages as href attributes of the links.

Elduwani

Nice.This will come in handy.

Great one! This one deferentially will be useful!

SDGSteve

Simple and elegant approach to thumbnail previews, thanks for posting!

This is a really good tutorial, but I ask myself how to automate the generation of albums in php.

$albums = array(
'Name1' => array(
'assets/img/thumbs/4.jpg',
'assets/img/thumbs/11.jpg',
'assets/img/thumbs/10.jpg'),
'Name2' => array(
'assets/img/thumbs/4.jpg',
'assets/img/thumbs/11.jpg',
'assets/img/thumbs/10.jpg'),

                           [...]
                          'Name X' =&gt; array ( ....),

        );

I thought to use nested FOREACH,but I can not assign them to the variable $albums.

I am completely blocked, so thank you for any help.
Can you explain the procedure.

Probleme solved ;)

Angelica

That's cool animation. I will try it soon. ;D

thanks so much sir for the tutorial i am trying to enhance an album that i have made but the image cant review i have all my image in the uploads folder and the thumb image inside the upload folder i dont no how to go about it sir please help

Gaz The Designer

Finally some decent examples with CSS3. It's going be a joy to use CSS3 in websites although we still have to consider the fact that IE8 is still around.

Is there a way for the album preview to snap back to it's original photo (kinda like Facebook's album previews)? Thanks!

Here you go:

.on('mouseleave', function(){
   album.trigger('stopAnimation');
   $.each(images,function(){
      $(this).animate({'opacity' : 0});
   });
   album.children().first().animate({'opacity' : 1});

});