SHARE
Facebook X Pinterest WhatsApp

Create a Thumbnail Proximity Effect Using CSS3

Written By
thumbnail
Michael Rohde
Michael Rohde
Jan 16, 2012

The new year is well under way and there certainly has not been a let up in the creativity department when it comes to developing new code with CSS3 and jQuery. It didn’t take me long at all to find this demo from Mary Lou, who we have featured here on HTML Goodies in the past.

This time, Mary Lou has provided code which shows how to create a thumbnail proximity effect. What does that mean? Basically, what Mary Lou did was to take a large collection of thumbnail images and when a visitor hovers over a particular thumbnail, not only does that specific thumbnail enlarge, the thumbnails within a close proximity also enlarge, just not as much. It’s a very nice effect that deserves some recognition.

In total, there are three demos available. The second demo has a bit of description to accompany the thumbnail that the visitor is hovering over. In the third demo, the near-by thumbnails are enlarged while the targeted thumbnail has accompanying text, but in this case the proximity thumbnails are given some space from the main thumbnail. This style declutters what some might call a “busy” look. All-in-all, all three demos are worthy of attention and can be found here.

For the purposes of the sample code, the second demo will be covered. For the thumbnails, unordered lists are used and you will need to add a division for the description of each image. Here’s an example of the HTML:

<ul id="pe-thumbs" class="pe-thumbs">
    <li>
        <a href="#">
            <img src="images/thumbs/1.jpg" />
            <div class="pe-description">
                <h3>Time</h3>
                <p>Since time, and his predestinated end</p>
            </div></a>
    </li>
    <li><!-- ... --></li>
</ul>

The CSS starts off by centering the unordered list on the page. Then, you need to add a background image to offset the thumbnails with a low opacity.

.pe-thumbs {
    width: 900px;
    height: 400px;
    margin: 20px auto;
    position: relative;
    background: transparent url(../images/3.jpg) top center;
    border: 5px solid #fff;
    box-shadow: 0 1px 2px rgba(0,0,0,0.2);
}

Here’s how to add a tint to the background image by adding a pseudo element with a rgba background color:

.pe-thumbs:before {
    content: "";
    display: block;
    position: absolute;
    top: 0px;
    left: 0px;
    width: 100%;
    height: 100%;
    background: rgba(255,102,0,0.2);
}

You now need to float the list items to the left and then set the anchor positions and images to relative:

.pe-thumbs li {
    float: left;
    position: relative;
}
.pe-thumbs li a,
.pe-thumbs li a img{
    display: block;
    position: relative;
}

For the purposes of this demo, each thumbnail has an initial width of 100 pixels and an opacity of 0.2:

.pe-thumbs li a img{
    width: 100px;
    opacity: 0.2;
}

Now, hide the description by default and then you can add some JavaScript later to make it appear:

.pe-thumbs li a div.pe-description{
    width: 200px;
    height: 100px;
    background: rgba(0,0,0,0.8);
    position: absolute;
    top: 0px;
    left: -200px;
    color: white;
    display: none;
    z-index: 1001;
    text-align: left;
}

Last, you can style the content of the description division:

.pe-description h3{
    padding: 10px 10px 0px 10px;
    line-height: 20px;
    font-family: 'BebasNeueRegular', 'Arial Narrow', Arial, sans-serif;
    font-size: 22px;
    margin: 0px;
}
.pe-description p{
    padding: 10px 0px;
    margin: 10px;
    font-size: 11px;
    font-style: italic;
    border-top: 1px solid rgba(255,255,255,0.3);
}

That’s all there is to it for the CSS3 and the HTML. Let’s move on to the JavaScript. This code is set up to calculate the containers and positions when a visitor hovers over a thumbnail. Then, you need to bind the proximity events to the images. After the image scales to the maximum value, the z-index needs to be set very high, so that it stays on top. Finally, the correct description needs to appear.

Show Me The Whole Thing!

Here is the sample code in its entirety:

    // list of thumbs
var $list        = $('#pe-thumbs'),
    // list's width and offset left.
    // this will be used to know the position of the description container
    listW        = $list.width(),
    listL        = $list.offset().left,
    // the images
    $elems        = $list.find('img'),
    // the description containers
    $descrp        = $list.find('div.pe-description'),
    // maxScale : maximum scale value the image will have
    // minOpacity / maxOpacity : minimum (set in the CSS) and maximum values for the image's opacity
    settings    = {
        maxScale    : 1.3,
        maxOpacity    : 0.9,
        minOpacity    : Number( $elems.css('opacity') )
    },
    init        = function() {

        // minScale will be set in the CSS
        settings.minScale = _getScaleVal() || 1;
        // preload the images (thumbs)
        _loadImages( function() {

            _calcDescrp();
            _initEvents();

        });

    },
    // Get Value of CSS Scale through JavaScript:
    // http://css-tricks.com/get-value-of-css-rotation-through-javascript/
    _getScaleVal= function() {

        var st = window.getComputedStyle($elems.get(0), null),
            tr = st.getPropertyValue("-webkit-transform") ||
                 st.getPropertyValue("-moz-transform") ||
                 st.getPropertyValue("-ms-transform") ||
                 st.getPropertyValue("-o-transform") ||
                 st.getPropertyValue("transform") ||
                 "fail...";

        if( tr !== 'none' ) {     

            var values = tr.split('(')[1].split(')')[0].split(','),
                a = values[0],
                b = values[1],
                c = values[2],
                d = values[3];

            return Math.sqrt( a * a + b * b );

        }

    },
    // calculates the style values for the description containers,
    // based on the settings variable
    _calcDescrp    = function() {

        $descrp.each( function(i) {

            var $el        = $(this),
                $img    = $el.prev(),
                img_w    = $img.width(),
                img_h    = $img.height(),
                img_n_w    = settings.maxScale * img_w,
                img_n_h    = settings.maxScale * img_h,
                space_t = ( img_n_h - img_h ) / 2,
                space_l = ( img_n_w - img_w ) / 2;

            $el.data( 'space_l', space_l ).css({
                height    : settings.maxScale * $el.height(),
                top        : -space_t,
                left    : img_n_w - space_l
            });

        });

    },
    _initEvents    = function() {

        $elems.on('proximity.Photo', { max: 80, throttle: 10, fireOutOfBounds : true }, function(event, proximity, distance) {

            var $el            = $(this),
                $li            = $el.closest('li'),
                $desc        = $el.next(),
                scaleVal    = proximity * ( settings.maxScale - settings.minScale ) + settings.minScale,
                scaleExp    = 'scale(' + scaleVal + ')';

            // change the z-index of the element once
            // it reaches the maximum scale value
            // also, show the description container
            if( scaleVal === settings.maxScale ) {

                $li.css( 'z-index', 1000 );

                if( $desc.offset().left + $desc.width() > listL + listW ) {

                    $desc.css( 'left', -$desc.width() - $desc.data( 'space_l' ) );

                }

                $desc.fadeIn( 800 );

            }
            else {

                $li.css( 'z-index', 1 );

                $desc.stop(true,true).hide();

            }    

            $el.css({
                '-webkit-transform'    : scaleExp,
                '-moz-transform'    : scaleExp,
                '-o-transform'        : scaleExp,
                '-ms-transform'        : scaleExp,
                'transform'            : scaleExp,
                'opacity'            : ( proximity * ( settings.maxOpacity - settings.minOpacity ) + settings.minOpacity )
            });

        });

    },
    _loadImages    = function( callback ) {

        var loaded     = 0,
            total    = $elems.length;

        $elems.each( function(i) {

            var $el = $(this);

            $('<img>').load( function() {

                ++loaded;
                if( loaded === total )
                    callback.call();

            }).attr( 'src', $el.attr('src') );

        });

    };

return {
    init    : init
};

This is a very nice effect that websites with a lot of images might consider using on their own website.

Recommended for you...

Importing Custom Less Styles at Run-time in Angular
Rob Gravelle
Jun 14, 2022
Setting CSS Values from Less Variables in Angular
Rob Gravelle
Jun 4, 2022
Color Manipulation with JavaScript
Rob Gravelle
May 21, 2022
An Introduction to CSS-in-JS
Rob Gravelle
May 14, 2022
HTML Goodies Logo

The original home of HTML tutorials. HTMLGoodies is a website dedicated to publishing tutorials that cover every aspect of being a web developer. We cover programming and web development tutorials on languages and technologies such as HTML, JavaScript, and CSS. In addition, our articles cover web frameworks like Angular and React.JS, as well as popular Content Management Systems (CMS) that include WordPress, Drupal, and Joomla. Website development platforms like Shopify, Squarespace, and Wix are also featured. Topics related to solid web design and Internet Marketing also find a home on HTMLGoodies, as we discuss UX/UI Design, Search Engine Optimization (SEO), and web dev best practices.

Property of TechnologyAdvice. © 2025 TechnologyAdvice. All Rights Reserved

Advertiser Disclosure: Some of the products that appear on this site are from companies from which TechnologyAdvice receives compensation. This compensation may impact how and where products appear on this site including, for example, the order in which they appear. TechnologyAdvice does not include all companies or all types of products available in the marketplace.