Elastic Image Slideshow with Thumbnail Preview

No comments
The Markup

We will create two unordered lists, one for the main slider and one for the thumbnail navigation beneath the large image. The "large slider" list elements will contain the image and a title with an h2 and h3 element:


01<div id="ei-slider" class="ei-slider">
02    <ul class="ei-slider-large">
03        <li>
04            <img src="images/large/1.jpg" alt="image01" />
05            <div class="ei-title">
06                <h2>Creative</h2>
07                <h3>Geek</h3>
08            </div>
09        </li>
10        <li>...</li>
11    </ul>
12    <ul class="ei-slider-thumbs">
13        <li class="ei-slider-element">Current</li>
14        <li>
15            <a href="#">Slide 1</a>
16            <img src="images/thumbs/1.jpg" alt="thumb01" />
17        </li>
18        <li>...</li>
19    </ul>
20</div>


The list for the thumbnail preview navigation will contain an absolute element (the first list element with the class ei-slider-element and the thumbnail list elements which consist of an anchor and an image (the thumbnail).

Now, let’s add the style.
The CSS

First, we will define the style for the main wrapper. We will have the slider inside of a wrapper which will be 100% in width in order to stretch over the whole window. Now, the slider itself will also have a width of 100%, making it use all the space there is in width. But we will also define a maximum width, so that the images in our slider don’t get stretched too much when dealing with a big screen:
1 .ei-slider{
2 position: relative;
3 width: 100%;
4 max-width: 1920px;
5 height: 400px;
6 margin: 0 auto;
7 }

While the images are loading, we will add a loading element which will have the following style:
01 .ei-slider-loading{
02 width: 100%;
03 height: 100%;
04 position: absolute;
05 top: 0px;
06 left: 0px;
07 z-index:999;
08 background: rgba(0,0,0,0.9);
09 color: #fff;
10 text-align: center;
11 line-height: 400px;
12 }

The unordered list will occupy all the space we defined and will not show any overflow:
1 .ei-slider-large{
2 height: 100%;
3 width: 100%;
4 position:relative;
5 overflow: hidden;
6 }

The list elements that will hold the images will be of absolute position. Depending from where we navigate we will slide them from the left or from the right:
1 .ei-slider-large li{
2 position: absolute;
3 top: 0px;
4 left: 0px;
5 overflow: hidden;
6 height: 100%;
7 width: 100%;
8 }

The width of the image will be set in our JavaScript but we also want to define it if we don’t have JS enabled:
1 .ei-slider-large li img{
2 width: 100%;
3 }

The title holder will be positioned in the middle of the list element with a right margin to fit the picture in our example (and not to overlap the face in the photography):
1 .ei-title{
2 position: absolute;
3 right: 50%;
4 margin-right: 13%;
5 top: 30%;
6 }

The style for the headings is the following:
01 .ei-title h2, .ei-title h3{
02 text-align: right;
03 }
04 .ei-title h2{
05 font-size: 40px;
06 line-height: 50px;
07 font-family: 'Playfair Display', serif;
08 font-style: italic;
09 color: #b5b5b5;
10 }
11 .ei-title h3{
12 font-size: 70px;
13 line-height: 70px;
14 font-family: 'Open Sans Condensed', sans-serif;
15 text-transform: uppercase;
16 color: #000;
17 }

The navigation list will have a small height of 13 pixels. We will set a default width for the thumbnails in the initialisation of our plugin. From that width we will set a max-width to the unordered list. This will make it elastic when we resize the window, but not occupy all the width there is.
1 .ei-slider-thumbs{
2 height: 13px;
3 margin: 0 auto;
4 position: relative;
5 }

The list elements of the navigation list will be of relative position:
1 .ei-slider-thumbs li{
2 position: relative;
3 float: left;
4 height: 100%;
5 }

The special slider element that indicates the current image will be positioned absolutely on top of the current thumbnail element:
1 .ei-slider-thumbs li.ei-slider-element{
2 top: 0px;
3 left: 0px;
4 position: absolute;
5 height: 100%;
6 z-index: 10;
7 text-indent: -9000px;
8 background: rgba(0,0,0,0.9);
9 }

The link elements will have a white box shadow to show a tiny separation and some darker shadow to appear under them. We’ll also add a transition to the element so that we can change the background-color smoothly on hover:
01 .ei-slider-thumbs li a{
02 display: block;
03 text-indent: -9000px;
04 background: #666;
05 width: 100%;
06 height: 100%;
07 cursor: pointer;
08 box-shadow:
09 0px 1px 1px 0px rgba(0,0,0,0.3),
10 0px 1px 0px 1px rgba(255,255,255,0.5);
11 transition: background 0.2s ease;
12 }
13 .ei-slider-thumbs li a:hover{
14 background-color: #f0f0f0;
15 }

The image will be positioned absolutely and we will add a transition and a box reflection to it. Adding a max-width to it will make sure that the thumb will adjust to the size of the list element when the window gets smaller than the width of the unordered list itself:
01 .ei-slider-thumbs li img{
02 position: absolute;
03 bottom: 50px;
04 opacity: 0;
05 z-index: 999;
06 max-width: 100%;
07 transition: all 0.4s ease;
08 -webkit-box-reflect:
09 below 0px -webkit-gradient(
10 linear,
11 left top,
12 left bottom,
13 from(transparent),
14 color-stop(50%, transparent),
15 to(rgba(255,255,255,0.3))
16 );
17 }

On hover, we will animate the opacity and the bottom value so that it appears to be sliding in from the top:
1 .ei-slider-thumbs li:hover img{
2 opacity: 1;
3 bottom: 13px;
4 }

Last but not least, we want to make sure that from a certain width on, the slider title will not cover the best parts of our image. So, we will make it appear at the bottom of the image with a semi-transparent white background:
01 @media screen and (max-width: 830px) {
02 .ei-title{
03 position: absolute;
04 right: 0px;
05 margin-right: 0px;
06 width: 100%;
07 text-align: center;
08 top: auto;
09 bottom: 10px;
10 background: rgba(255,255,255,0.9);
11 padding: 5px 0;
12 }
13 .ei-title h2, .ei-title h3{
14 text-align: center;
15 }
16 .ei-title h2{
17 font-size: 20px;
18 line-height: 24px;
19 }
20 .ei-title h3{
21 font-size: 30px;
22 line-height: 40px;
23 }
24 }

For the case that we don’t have JavaScript enabled we will add this piece of CSS to ensure that our slides are shown. We will hide the thumbnail navigation:
1 .ei-slider{
2 height: auto;
3 }
4 .ei-slider-thumbs{
5 display: none;
6 }
7 .ei-slider-large li{
8 position: relative;
9 }

And that’s all the style! Let’s take a look at the JavaScript.
The JavaScript

Since we are creating a plugin, let’s first look at the definition of the options:
01 $.Slideshow.defaults = {
02 // animation types:
03 // "sides" : new slides will slide in from left / right
04 // "center": new slides will appear in the center
05 animation : 'sides', // sides || center
06 // if true the slider will automatically
07 // slide, and it will only stop if the user
08 // clicks on a thumb
09 autoplay : false,
10 // interval for the slideshow
11 slideshow_interval : 3000,
12 // speed for the sliding animation
13 speed : 800,
14 // easing for the sliding animation
15 easing : '',
16 // percentage of speed for the titles animation.
17 // Speed will be speed * titlesFactor
18 titlesFactor : 0.60,
19 // titles animation speed
20 titlespeed : 800,
21 // titles animation easing
22 titleeasing : '',
23 // maximum width for the thumbs in pixels
24 thumbMaxWidth : 150
25 };

In the _init funtion we will start by setting the opacity of the title elements and the images to 0. We will also preload the images and once they are loaded we will set their size and position according to the slider width and height. Then we configure the thumbnails navigation by setting the width of the unordered list and the list items.

We will then show the first slide and if we set autoplay in our options to true, then we’ll start the slideshow. The we initialize the events which are the events for resizing the window and for clicking the thumbnails:
01 _init : function( options ) {
02
03 this.options = $.extend( true, {}, $.Slideshow.defaults, options );
04
05 // set the opacity of the title elements and the image items
06 this.$imgItems.css( 'opacity', 0 );
07 this.$imgItems.find('div.ei-title > *').css( 'opacity', 0 );
08
09 // index of current visible slider
10 this.current = 0;
11
12 var _self = this;
13
14 // preload images
15 // add loading status
16 this.$loading = $('
17
Loading
18
19 ').prependTo( _self.$el );
20
21 $.when( this._preloadImages() ).done( function() {
22
23 // hide loading status
24 _self.$loading.hide();
25
26 // calculate size and position for each image
27 _self._setImagesSize();
28
29 // configure thumbs container
30 _self._initThumbs();
31
32 // show first
33 _self.$imgItems.eq( _self.current ).css({
34 'opacity' : 1,
35 'z-index' : 10
36 }).show().find('div.ei-title > *').css( 'opacity', 1 );
37
38 // if autoplay is true
39 if( _self.options.autoplay ) {
40
41 _self._startSlideshow();
42
43 }
44
45 // initialize the events
46 _self._initEvents();
47
48 });
49
50 },

And here are the single functions we just talked about:
001 _preloadImages : function() {
002
003 // preloads all the large images
004
005 var _self = this,
006 loaded = 0;
007
008 return $.Deferred(
009
010 function(dfd) {
011
012 _self.$images.each( function( i ) {
013
014 $('').load( function() {
015
016 if( ++loaded === _self.itemsCount ) {
017
018 dfd.resolve();
019
020 }
021
022 }).attr( 'src', $(this).attr('src') );
023
024 });
025
026 }
027
028 ).promise();
029
030 },
031 _setImagesSize : function() {
032
033 // save ei-slider's width
034 this.elWidth = this.$el.width();
035
036 var _self = this;
037
038 this.$images.each( function( i ) {
039
040 var $img = $(this);
041 imgDim = _self._getImageDim( $img.attr('src') );
042
043 $img.css({
044 width : imgDim.width,
045 height : imgDim.height,
046 marginLeft : imgDim.left,
047 marginTop : imgDim.top
048 });
049
050 });
051
052 },
053 _getImageDim : function( src ) {
054
055 var $img = new Image();
056
057 $img.src = src;
058
059 var c_w = this.elWidth,
060 c_h = this.$el.height(),
061 r_w = c_h / c_w,
062
063 i_w = $img.width,
064 i_h = $img.height,
065 r_i = i_h / i_w,
066 new_w, new_h, new_left, new_top;
067
068 if( r_w > r_i ) {
069
070 new_h = c_h;
071 new_w = c_h / r_i;
072
073 }
074 else {
075
076 new_h = c_w * r_i;
077 new_w = c_w;
078
079 }
080
081 return {
082 width : new_w,
083 height : new_h,
084 left : ( c_w - new_w ) / 2,
085 top : ( c_h - new_h ) / 2
086 };
087
088 },
089 _initThumbs : function() {
090
091 // set the max-width of the slider elements to the one set in the plugin's options
092 // also, the width of each slider element will be 100% / total number of elements
093 this.$sliderElems.css({
094 'max-width' : this.options.thumbMaxWidth + 'px',
095 'width' : 100 / this.itemsCount + '%'
096 });
097
098 // set the max-width of the slider and show it
099 this.$sliderthumbs.css( 'max-width', this.options.thumbMaxWidth * this.itemsCount + 'px' ).show();
100
101 },
102 _startSlideshow : function() {
103
104 var _self = this;
105
106 this.slideshow = setTimeout( function() {
107
108 var pos;
109
110 ( _self.current === _self.itemsCount - 1 ) ? pos = 0 : pos = _self.current + 1;
111
112 _self._slideTo( pos );
113
114 if( _self.options.autoplay ) {
115
116 _self._startSlideshow();
117
118 }
119
120 }, this.options.slideshow_interval);
121
122 },

The _slideTo function will take care of the transition between the slides. Depending on what we’ve set in our options, we’ll either make the new slide appear from the side or simply make it fade in without sliding it. We’ll also take care of the title and its heading elements which we will slightly slide from the sides by setting their right margins. The thumbnail slider element will have to move to the new corresponding thumbnail position.
01 _slideTo : function( pos ) {
02
03 // return if clicking the same element or if currently animating
04 if( pos === this.current || this.isAnimating )
05 return false;
06
07 this.isAnimating = true;
08
09 var $currentSlide = this.$imgItems.eq( this.current ),
10 $nextSlide = this.$imgItems.eq( pos ),
11 _self = this,
12
13 preCSS = {zIndex : 10},
14 animCSS = {opacity : 1};
15
16 // new slide will slide in from left or right side
17 if( this.options.animation === 'sides' ) {
18
19 preCSS.left = ( pos > this.current ) ? -1 * this.elWidth : this.elWidth;
20 animCSS.left = 0;
21
22 }
23
24 // titles animation
25 $nextSlide.find('div.ei-title > h2')
26 .css( 'margin-right', 50 + 'px' )
27 .stop()
28 .delay( this.options.speed * this.options.titlesFactor )
29 .animate({ marginRight : 0 + 'px', opacity : 1 }, this.options.titlespeed, this.options.titleeasing )
30 .end()
31 .find('div.ei-title > h3')
32 .css( 'margin-right', -50 + 'px' )
33 .stop()
34 .delay( this.options.speed * this.options.titlesFactor )
35 .animate({ marginRight : 0 + 'px', opacity : 1 }, this.options.titlespeed, this.options.titleeasing )
36
37 $.when(
38
39 // fade out current titles
40 $currentSlide.css( 'z-index' , 1 ).find('div.ei-title > *').stop().fadeOut( this.options.speed / 2, function() {
41 // reset style
42 $(this).show().css( 'opacity', 0 );
43 }),
44
45 // animate next slide in
46 $nextSlide.css( preCSS ).stop().animate( animCSS, this.options.speed, this.options.easing ),
47
48 // "sliding div" moves to new position
49 this.$sliderElem.stop().animate({
50 left : this.$thumbs.eq( pos ).position().left
51 }, this.options.speed )
52
53 ).done( function() {
54
55 // reset values
56 $currentSlide.css( 'opacity' , 0 ).find('div.ei-title > *').css( 'opacity', 0 );
57 $nextSlide.css( 'z-index', 1 );
58 _self.current = pos;
59 _self.isAnimating = false;
60
61 });
62
63 },

The _initEvents function will recalculate the sizes of the images when we resize the window and reposition the thumbnail slider element. When clicking on a thumbnail we will show the regarding slide:
01 _initEvents : function() {
02
03 var _self = this;
04
05 // window resize
06 $(window).on( 'smartresize.eislideshow', function( event ) {
07
08 // resize the images
09 _self._setImagesSize();
10
11 // reset position of thumbs sliding div
12 _self.$sliderElem.css( 'left', _self.$thumbs.eq( _self.current ).position().left );
13
14 });
15
16 // click the thumbs
17 this.$thumbs.on( 'click.eislideshow', function( event ) {
18
19 if( _self.options.autoplay ) {
20
21 clearTimeout( _self.slideshow );
22 _self.options.autoplay = false;
23
24 }
25
26 var $thumb = $(this),
27 idx = $thumb.index() - 1; // exclude sliding div
28
29 _self._slideTo( idx );
30
31 return false;
32
33 });
34
35 }

And that’s it! I hope you enjoyed this tutorial and find it useful!

No comments :

Post a Comment