В данном уроке мы создадим эластичное слайдшоу с миниатюрами. Слайдшоу будет автоматически подстраиваться под содержащий его контейнер, навигация по слайдам осуществляется с помощью предварительного просмотра миниатюр, имеется опция автоматического проигрывания.
Для реализации функции адаптации будет использоваться смесь техник 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;
});
}