Эластичное слайдшоу с миниатюрами

В данном уроке мы создадим эластичное слайдшоу с миниатюрами. Слайдшоу будет автоматически подстраиваться под содержащий его контейнер, навигация по слайдам осуществляется с помощью предварительного просмотра миниатюр, имеется опция автоматического проигрывания.

Для реализации функции адаптации будет использоваться смесь техник JavaScript и CSS.

Разметка HTML
Мы создадим два неупорядоченных списка. Один будет использоваться для основного слайдера, а другой - для миниатюр навигации, расположенных под большим изображением. Элементы "большого слайдера" содержат изображения и заголовки h2 и h3:

<div id="ei-slider" class="ei-slider">
    <ul class="ei-slider-large">
        <li>
            <img src="images/large/1.jpg" alt="image01" />
            <div class="ei-title">
                <h2>Креативно</h2>
                <h3>Geek</h3>
            </div>
        </li>
        <li>...</li>
    </ul>
    <ul class="ei-slider-thumbs">
        <li class="ei-slider-element">Current</li>
        <li>
            <a href="#">Слайд 1</a>
            <img src="images/thumbs/1.jpg" alt="thumb01" />
        </li>
        <li>...</li>
    </ul>
</div>

Элемент списка миниатюр содержит ссылку и изображение. При этом первый пункт списка - абсолютный элемент с классом ei-slider-element.

CSS
Сначала мы определим стили для основного контейнера. У нас есть слайдер в контейнере, который будет иметь ширину равную 100%, чтобы растягиваться на все окно. Сам слайдер тоже будет иметь ширину 100%, чтобы использовать все пространство. Но нам надо также определить максимальную ширину, чтобы изображения не растягивались сильно на больших экранах.

.ei-slider{
    position: relative;
    width: 100%;
    max-width: 1920px;
    height: 400px;
    margin: 0 auto;
}

При загрузке изображений мы будем выводить индикатор, который имеет следующие стили:

.ei-slider-loading{
    width: 100%;
    height: 100%;
    position: absolute;
    top: 0px;
    left: 0px;
    z-index:999;
    background: rgba(0,0,0,0.9);
    color: #fff;
    text-align: center;
    line-height: 400px;
}

Неупорядоченный список будет занимать все доступное пространство и скрывать все выступающие элементы:

.ei-slider-large{
    height: 100%;
    width: 100%;
    position:relative;
    overflow: hidden;
}

Элементы списка содержат изображения в абсолютных позициях. В зависимости от навигации, они будут выскальзывать либо слева, либо справа:

.ei-slider-large li{
    position: absolute;
    top: 0px;
    left: 0px;
    overflow: hidden;
    height: 100%;
    width: 100%;
}

Ширина изображений будет устанавливаться кодом JavaScript, но ее надо определить на случай отключения JavaScript:

.ei-slider-large li img{
    width: 100%;
}

Контейнер заголовков позиционируется в середине элемента списка с полем справа (чтобы не перекрывать лицо на фотографии):

.ei-title{
    position: absolute;
    right: 50%;
    margin-right: 13%;
    top: 30%;
}

Для заголовков используются следующие стили:

.ei-title h2, .ei-title h3{
    text-align: right;
}
.ei-title h2{
    font-size: 40px;
    line-height: 50px;
    font-family: 'Playfair Display', serif;
    font-style: italic;
    color: #b5b5b5;
}
.ei-title h3{
    font-size: 70px;
    line-height: 70px;
    font-family: 'Open Sans Condensed', sans-serif;
    text-transform: uppercase;
    color: #000;
}

Список навигации будет иметь маленькую высоту - 13 px. При инициализации нашего плагина мы установим ширину по умолчанию для миниатюр.

.ei-slider-thumbs{
    height: 13px;
    margin: 0 auto;
    position: relative;
}

Элементы списка навигации будут иметь относительные позцици:

.ei-slider-thumbs li{
    position: relative;
    float: left;
    height: 100%;
}

Специальные элемент, который указывает текущее изображение будет позиционироваться абсолютно поверх текущего элемента миниатюры:

.ei-slider-thumbs li.ei-slider-element{
    top: 0px;
    left: 0px;
    position: absolute;
    height: 100%;
    z-index: 10;
    text-indent: -9000px;
    background: rgba(0,0,0,0.9);
}

Элемент ссылки будет иметь белую тень с небольшой добавочной темной тенью под ней. Также будет использоваться переход для плавного изменения цвета фона при наведении курсора мыши:

.ei-slider-thumbs li a{
    display: block;
    text-indent: -9000px;
    background: #666;
    width: 100%;
    height: 100%;
    cursor: pointer;
    box-shadow:
        0px 1px 1px 0px rgba(0,0,0,0.3),
        0px 1px 0px 1px rgba(255,255,255,0.5);
        transition: background 0.2s ease;
}
.ei-slider-thumbs li a:hover{
    background-color: #f0f0f0;
}

Изображение будет позиционироваться абсолютно, для него задается переход свойств и отражение. Добавление свойства max-width гарантирует, что миниатюра будет выравнивать размер элемента списка, когда окно будет становиться меньше, чем ширина неупорядоченного списка:

.ei-slider-thumbs li img{
    position: absolute;
    bottom: 50px;
    opacity: 0;
    z-index: 999;
    max-width: 100%;
        transition: all 0.4s ease;
    -webkit-box-reflect:
        below 0px -webkit-gradient(
            linear,
            left top,
            left bottom,
            from(transparent),
            color-stop(50%, transparent),
            to(rgba(255,255,255,0.3))
            );
}

При наведении курсора мыши мы будем анимировать значения свойств opacity и bottom, так что элемент будет выскальзывать сверху:

.ei-slider-thumbs li:hover img{
    opacity: 1;
    bottom: 13px;
}

Нужно, чтобы начиная с определенного значения ширины заголовок не закрывал лучшие части изображения. Поэтому его надо будет выводить снизу изображения на белом полупрозрачном фоне:

@media screen and (max-width: 830px) {
    .ei-title{
        position: absolute;
        right: 0px;
        margin-right: 0px;
        width: 100%;
        text-align: center;
        top: auto;
        bottom: 10px;
        background: rgba(255,255,255,0.9);
        padding: 5px 0;
    }
    .ei-title h2, .ei-title h3{
        text-align: center;
    }
    .ei-title h2{
        font-size: 20px;
        line-height: 24px;
    }
    .ei-title h3{
        font-size: 30px;
        line-height: 40px;
    }
}

Для случая отключения JavaScript нужно обеспечить вывод наших слайдов и скрытие навигации миниатюр:

.ei-slider{
    height: auto;
}
.ei-slider-thumbs{
    display: none;
}
.ei-slider-large li{
    position: relative;
}


JavaScript
Так как мы создаем плагин, то рассмотрим сначала определение опций:

  $.Slideshow.defaults      = {
    // Типы анимации:
    // "sides" : новый слайд выскальзывает слева или справа
    // "center": новый слайд появляется в центре
    animation           : 'sides', // sides || center
    // Если значение true, то включается автопроигрывание слайдшоу, которое отключается при нажатии на миниатюру
    autoplay            : false,
    // Интервал для слайшоу
    slideshow_interval  : 3000,
    // Скорость анимации смены слайдов
    speed           : 800,
    // Функция перехода для анимации смены слайдов
    easing          : '',
    // Процентное соотношение анимации заголовков. Скорость будет определяться по формуле speed * titlesFactor
    titlesFactor        : 0.60,
    // Скорость анимации заголвоков
    titlespeed          : 800,
    // Функция перехода для анимации заголовоков
    titleeasing         : '',
    // Максимальная ширина для миниатюр в px
    thumbMaxWidth       : 150
};

В функции _init сначала устанавливается полная прозрачность элементов заголовков и изображений. Также предварительно загружаются изображения, после чего мы устанавливаем для них размер и положение в соответствии с шириной и высотой слайдера. Затем конфигурируем навигацию миниатюр, устанавливая ширину неупорядоченного списка и его пунктов.

Затем выводим первый слайд и, если включена опция автопроигрвания, запускаем слайдшоу. После чего инициализируются обработчики событий для изменений размеров окна и нажатий на миниатюры:

_init               : function( options ) {
     
    this.options        = $.extend( true, {}, $.Slideshow.defaults, options );
     
    // Устанавливаем непрозрачность для элементов заголовков и изображений
    this.$imgItems.css( 'opacity', 0 );
    this.$imgItems.find('div.ei-title > *').css( 'opacity', 0 );
     
    // Индекс текущего видимого слайда
    this.current        = 0;
     
    var _self           = this;
     
    // Предварительная загрузка изображений
    // Добавляем статус загрузки
    this.$loading       = $('<div class="ei-slider-loading">Loading</div>').prependTo( _self.$el );
     
    $.when( this._preloadImages() ).done( function() {
         
        // /скрываем статус загрузки
        _self.$loading.hide();
         
        // Вычисляем размер и положение для каждого изображения
        _self._setImagesSize();
         
        // Конфигурируем контейнер для миниатюр
        _self._initThumbs();
         
        // Выводим первый пункт
        _self.$imgItems.eq( _self.current ).css({
            'opacity'   : 1,
            'z-index'   : 10
        }).show().find('div.ei-title > *').css( 'opacity', 1 );
         
        // Если включено автопроигрывание
        if( _self.options.autoplay ) {
         
            _self._startSlideshow();
         
        }
         
        // Инициализируем события
        _self._initEvents();
     
    });
     
},



Вот код используемых функций:

_preloadImages      : function() {
     
    // Предварительная загрузка всех больших изображений
     
    var _self   = this,
        loaded  = 0;
     
    return $.Deferred(
     
        function(dfd) {
     
            _self.$images.each( function( i ) {
                 
                $('<img/>').load( function() {
                 
                    if( ++loaded === _self.itemsCount ) {
                     
                        dfd.resolve();
                         
                    }
                 
                }).attr( 'src', $(this).attr('src') );
             
            });
             
        }
         
    ).promise();
     
},
_setImagesSize      : function() {
     
    // Сохраняе ширину слайда
    this.elWidth    = this.$el.width();
     
    var _self   = this;
     
    this.$images.each( function( i ) {
         
        var $img    = $(this);
            imgDim  = _self._getImageDim( $img.attr('src') );
             
        $img.css({
            width       : imgDim.width,
            height      : imgDim.height,
            marginLeft  : imgDim.left,
            marginTop   : imgDim.top
        });
         
    });
 
},
_getImageDim        : function( src ) {
     
    var $img    = new Image();
                     
    $img.src    = src;
             
    var c_w     = this.elWidth,
        c_h     = this.$el.height(),
        r_w     = c_h / c_w,
         
        i_w     = $img.width,
        i_h     = $img.height,
        r_i     = i_h / i_w,
        new_w, new_h, new_left, new_top;
             
    if( r_w > r_i ) {
         
        new_h   = c_h;
        new_w   = c_h / r_i;
     
    }
    else {
     
        new_h   = c_w * r_i;
        new_w   = c_w;
     
    }
             
    return {
        width   : new_w,
        height  : new_h,
        left    : ( c_w - new_w ) / 2,
        top     : ( c_h - new_h ) / 2
    };
 
},
_initThumbs         : function() {
 
    // Устанавливаем свойство max-width элемента слайдера
    // Также устанавливаем ширину каждого слайда по формуле 100% / на общее количество элементов
    this.$sliderElems.css({
        'max-width' : this.options.thumbMaxWidth + 'px',
        'width'     : 100 / this.itemsCount + '%'
    });
     
    // Устанавливаем свойство max-width слайда и выводим его
    this.$sliderthumbs.css( 'max-width', this.options.thumbMaxWidth * this.itemsCount + 'px' ).show();
     
},
_startSlideshow     : function() {
 
    var _self   = this;
     
    this.slideshow  = setTimeout( function() {
         
        var pos;
         
        ( _self.current === _self.itemsCount - 1 ) ? pos = 0 : pos = _self.current + 1;
         
        _self._slideTo( pos );
         
        if( _self.options.autoplay ) {
         
            _self._startSlideshow();
         
        }
     
    }, this.options.slideshow_interval);
 
},

Функция _slideTo осуществляет переход между слайдами. В зависимости от установленных опций, слайд либо будет выскальзывать слева/справа, либо будет проявляться в центре.

// Выводим слайд для нажатой миниатюры
_slideTo            : function( pos ) {
     
    // выходим, если нажат тот же элемент или он анаимируется в данный момент
    if( pos === this.current || this.isAnimating )
        return false;
     
    this.isAnimating    = true;
     
    var $currentSlide   = this.$imgItems.eq( this.current ),
        $nextSlide      = this.$imgItems.eq( pos ),
        _self           = this,
         
        preCSS          = {zIndex   : 10},
        animCSS         = {opacity  : 1};
     
    // Новый слайд будет выскальзывать слева или справа
    if( this.options.animation === 'sides' ) {
         
        preCSS.left     = ( pos > this.current ) ? -1 * this.elWidth : this.elWidth;
        animCSS.left    = 0;
     
    }  
     
    // Анимация заголовков
    $nextSlide.find('div.ei-title > h2')
              .css( 'margin-right', 50 + 'px' )
              .stop()
              .delay( this.options.speed * this.options.titlesFactor )
              .animate({ marginRight : 0 + 'px', opacity : 1 }, this.options.titlespeed, this.options.titleeasing )
              .end()
              .find('div.ei-title > h3')
              .css( 'margin-right', -50 + 'px' )
              .stop()
              .delay( this.options.speed * this.options.titlesFactor )
              .animate({ marginRight : 0 + 'px', opacity : 1 }, this.options.titlespeed, this.options.titleeasing )
     
    $.when(
         
        // Убираем текущии заголвоки
        $currentSlide.css( 'z-index' , 1 ).find('div.ei-title > *').stop().fadeOut( this.options.speed / 2, function() {
            // сбрасываем стили
            $(this).show().css( 'opacity', 0 );
        }),
         
        // Анимация вывода следующего слайдаы
        $nextSlide.css( preCSS ).stop().animate( animCSS, this.options.speed, this.options.easing ),
         
        // Перемещаем индикатор в нвое положение
        this.$sliderElem.stop().animate({
            left    : this.$thumbs.eq( pos ).position().left
        }, this.options.speed )
         
    ).done( function() {
         
        // Сбрасываем значения
        $currentSlide.css( 'opacity' , 0 ).find('div.ei-title > *').css( 'opacity', 0 );
            _self.current   = pos;
            _self.isAnimating       = false;
         
        });
         
},

Функция _initEvents устанавливает функции для пересчета размеров изображений при изменении размеров окна и вывода нового слайда при нажатии на миниатюру:

_initEvents         : function() {
     
    var _self   = this;
     
    // Изменение размеров окна
    $(window).on( 'smartresize.eislideshow', function( event ) {
         
        // Изменяем размеры изображений
        _self._setImagesSize();
     
        // Сбрасываем положение миниатюр
        _self.$sliderElem.css( 'left', _self.$thumbs.eq( _self.current ).position().left );
     
    });
     
    // Нажатие на миниатюре
    this.$thumbs.on( 'click.eislideshow', function( event ) {
         
        if( _self.options.autoplay ) {
         
            clearTimeout( _self.slideshow );
            _self.options.autoplay  = false;
         
        }
         
        var $thumb  = $(this),
            idx     = $thumb.index() - 1;
             
        _self._slideTo( idx );
         
        return false;
     
    });
     
}
Теги:
слайдшоу
Добавлено: 18 Апреля 2018 09:04:15 Добавил: Андрей Ковальчук Нравится 0
Добавить
Комментарии:
Нету комментариев для вывода...