Полноэкранный разрезной слайдер
Создадим полноэкранное слайдшоу, основная идея которого состоит в том, чтобы порезать текущий открытый слайд при навигации к следующему или предыдущему. Используя jQuery и CSS анимацию, мы можем получить уникальные переходы между слайдами.
Используя различные атрибуты data, мы определим тип, угол вращения и масштаб частей слайда.
Мы будем использовать следующие jQuery плагины:
- http://benalman.com/projects/jquery-cond-plugin/
- http://ricostacruz.com/jquery.transit/
А иконки животных возьмем из шрифта:
- http://www.dafont.com/animals.font
HTML
Наша начальная разметка будет состоять из основного контейнера с классом и идентификатором sl-slider, который будет содержать все слайды, каждый из которых имеет класс sl-slide. Также будут дополнительно добавлены классы для изменения цвета слайдов:
<section id="sl-slider" class="sl-slider">
<div class="sl-slide">
<div class="sl-deco" data-icon="6"></div>
<h2>Цитаты, высказывания, афоризмы</h2>
<blockquote>
<p>Будь проще к людям. Хочешь быть мудрей — Не делай больно мудростью своей.</p>
<cite>Омар Хайям</cite>
</blockquote>
</div>
<div class="sl-slide sl-slide-dark">
<!-- ... -->
</div>
<!-- ... -->
</section>
Каждый слайд имеет некоторые атрибуты data, которые будут использоваться для управления эффектами:data-orientation
data-cut1-rotation
data-cut2-rotation
data-cut1-scale
data-cut2-scale
Первый - data-orientation - ориентация или "vertical" или "horizontal". Это необходимо, чтобы нам знать, где разделять слайд - горизонтально или вертикально. Атрибуты data-cut1-rotation и data-cut2-rotation определяют угол вращения для каждого отрезанного кусочка, а data-cut1-scale и data-cut2-scale - будут определять значение масштабирования.Так для первого слайда у нас будут такие значения:
<div class="sl-slide" data-orientation="horizontal" data-cut1-rotation="-25" data-cut2-rotation="-25" data-cut1-scale="2" data-cut2-scale="2">
Наша структура - это "базовая структура". Мы построим эту структуру, используя jаvascript, чтобы можно было создать эффекты. Итак, мы преобразуем нашу структуру в это:<section id="sl-slider" class="sl-slider">
<div class="sl-slides-wrapper">
<div class="sl-slide sl-slide-horizontal">
<div class="sl-content-wrapper">
<div class="sl-content">
<!-- контент -->
</div>
</div>
</div>
<!-- ... -->
</div>
<nav>
<span class="sl-prev">Назад</span>
<span class="sl-next">Вперед</span>
</nav>
</section>
Добавим навигацию и несколько оболочек (wrapper) для контента.В момент, когда мы переходим к следующему или предыдущему слайду, мы берем текущий слайд и дублируем его оболочку (wrapper) контента, создавая "разрез".
<div class="sl-slide sl-slide-horizontal" >
<div class="sl-content-cut">
<div class="sl-content-wrapper">
<div class="sl-content">
<!-- ... -->
</div>
</div>
</div>
<div class="sl-content-cut">
<div class="sl-content-wrapper">
<div class="sl-content">
<!-- ... -->
</div>
</div>
</div>
</div>
Теперь перейдем к стилям.CSS
Мы хотим сделать полноэкранное слайдшоу, так что давайте позиционируем наш слайдер абсолютно:
.sl-slider {
position: absolute;
top: 0;
left: 0;
font-family: 'Montserrat', Arial, sans-serif;
}
Ширина и высота будет устанавливаться динамически в jаvascript.Для стрелок навигации мы будем использовать технику image-less (уменьшения изображения). У нас будет простой небольшой box (контейнер), который повернем на 45 градусов. Далее добавим штриховую границу (dashed border) к сторонам box и получим аккуратные стрелочки:
.sl-slider nav span {
position: fixed;
z-index: 2000;
top: 50%;
width: 80px;
height: 80px;
border: 2px dashed #ddd;
border: 2px dashed rgba(150,150,150,0.4);
text-indent: -90000px;
margin-top: -40px;
cursor: pointer;
transform: rotate(45deg);
transition: all 0.3s ease-in-out;
}
.sl-slider nav span.sl-prev {
left: 60px;
border-right: none;
border-top: none;
}
.sl-slider nav span.sl-next {
right: 60px;
border-left: none;
border-bottom: none;
}
Также мы добавили переход, чтобы изменять значение прозрачности RGBA при наведении:.sl-slider nav span:hover {
border-color: rgba(150,150,150,0.9);
}
Слайды и новая оболочка (wrapper) будет абсолютно позиционирована и займет 100% ширину и высоту:.sl-slide, .sl-slides-wrapper {
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
overflow: hidden;
}
У каждого слайда будет z-index равный 1; мы будем управлять появлением и расположением слайдов с помощью jаvascript:.sl-slide {
z-index: 1;
}
Содержимое "кусочков" будет позиционироваться абсолютно и их общий стиль будет таким:/* Дублирование частей/кусочков */
.sl-content-cut {
overflow: hidden;
position: absolute;
box-sizing: content-box;
background: #fff;
}
Мы используем box-sizing: content-box, потому что по умолчанию (в нашем normalize.css) мы используем border-box.Содержимое "кусочков" будет горизонтально или вертикально располагаться, а их высота или ширина будет равна половине размера экрана. Чтобы невидеть края "кусочков" при вращении, мы добавим отступ (padding):
/* Horizontal cut */
.sl-slide-horizontal .sl-content-cut {
width: 100%;
height: 50%;
left: -200px;
}
.sl-slide-horizontal .sl-content-cut:first-child {
top: -200px;
padding: 200px 200px 0px 200px;
}
.sl-slide-horizontal .sl-content-cut:nth-child(2) {
top: 50%;
padding: 0px 200px 200px 200px;
}
/* Vertical cut */
.sl-slide-vertical .sl-content-cut {
width: 50%;
height: 100%;
top: -200px;
}
.sl-slide-vertical .sl-content-cut:first-child {
left: -200px;
padding: 200px 0px 200px 200px;
}
.sl-slide-vertical .sl-content-cut:nth-child(2) {
left: 50%;
padding: 200px 200px 200px 0px;
}
Мы используем отрицательные значения положения, чтобы "вернуть" кусочки на место.Далее стиль оболочки контента и разделения контента:
/* Обоолчка контента */
/* Ширина и Высота устанавливаются динамически */
.sl-content-wrapper {
position: absolute;
}
.sl-content {
width: 100%;
height: 100%;
background: #fff;
}
Разделение с классом sl-content-wrapper получит высоту и ширину динамически. Если, например, слайд горизонтальный, то у оболочки ширина будет равна 100% ширине экрана и 50% высоте экрана. Оболочка второго "кусочка" будет также иметь отрицательный отступ сверху - top (горизонтально) или слева - left (вертикально), чтобы вернуть дублированный контент сверху или слева.Элементы, которые используются в контенте будут декоративными элементами (животные с кружками), заголовок и блок цитирования. Мы будем использовать шрифт с иконками животных, которые будут у нас, как псевдо-элементы.
Разделение с классом sl-deco, точно также, как и все другие элементы контента будут абсолютно позиционированы. Также отцентрируем по горизонтали и присвоим значение bottom 50%.
/* Элементы контента */
.sl-deco{
width: 260px;
height: 260px;
border: 2px dashed rgba(150,150,150,0.4);
border-radius: 50%;
position: absolute;
bottom: 50%;
left: 50%;
margin-left: -130px;
}
Используем атрибут "data-icon" в декоративном элементе и используем псевдо-элемент :after, который будет содержать букву из шрифта с иконками животных:[data-icon]:after {
content: attr(data-icon);
font-family: 'AnimalsNormal';
color: #000;
text-shadow: 0 0 1px #000;
position: absolute;
width: 220px;
height: 220px;
line-height: 220px;
text-align: center;
font-size: 100px;
top: 50%;
left: 50%;
margin: -110px 0 0 -110px;
box-shadow: inset 0 0 0 10px #f7f7f7;
border-radius: 50%;
}
Box-shadow создаст "поддельную" внутреннюю границу.Заголовок, также будет позиционироваться абсолютно и мы дадим ему то же самое минимальное значение, что мы и дали декоративному элементу, которое составляет 50%. Затем добавим отрицательный отступ снизу, чтобы поместить его под другим элементом. Мы можем использовать декоративный элемент в качестве контрольной точки и расположить другие элементы относительно него, используя отрицательное значение отступа снизу:
.sl-slide h2 {
color: #000;
text-shadow: 0 0 1px #000;
padding: 20px;
position: absolute;
font-size: 34px;
font-weight: 300;
letter-spacing: 13px;
text-transform: uppercase;
width: 80%;
left: 10%;
text-align: center;
line-height: 50px;
bottom: 50%;
margin: 0 0 -120px 0;
}
У блока цитирования будет ширина 30% и так как мы хотим центрировать его, то дадим ему значение слева равное 35% (потому что мы имеем про запас 70%, и берем половину), и устанавливаем соответствующее значение text-align:.sl-slide blockquote {
position: absolute;
width: 30%;
text-align: center;
left: 35%;
font-size: 13px;
line-height: 20px;
height: 70px;
color: #8b8b8b;
z-index: 2;
bottom: 50%;
margin: 0 0 -200px 0;
padding: 0;
}
Давайте добавим кавычку к блоку цитирования. Используя псевдо класс :before, мы добавим кавычку увеличенного размера позади блока цитирования:.sl-slide blockquote:before {
color: rgba(244,244,244,0.65);
font-family: "Bookman Old Style", Bookman, Garamond, serif;
position: absolute;
line-height: 60px;
width: 75px;
height: 75px;
font-size: 200px;
z-index: -1;
left: -15px;
top: 35px;
content: '201C';
}
А цитата будет выглядеть так:.sl-slide blockquote cite {
font-size: 10px;
font-style: normal;
text-transform: uppercase;
letter-spacing: 4px;
}
Далее, определим некоторые классы для управления цветами слайдов. Когда мы применяем класс цвета к слайду, мы хотим, чтобы цвет фона и цвета элементов отличались. По умолчанию, наши слайды белые, а элементы контента черные и серые.У темных или черных слайдов будет инвертированная цветовая схема:
/* Dark slides */
.sl-slide-dark .sl-content-cut,
.sl-slide-dark .sl-content {
background: #000;
}
.sl-slide-dark [data-icon]:after,
.sl-slide-dark.sl-slide h2 {
color: #fff;
}
.sl-slide-dark.sl-slide blockquote:before {
color: #222;
}
А вот стили для других цветовых схем:/* Цвет 1 для слайда */
.sl-slide-color-1 .sl-content-cut,
.sl-slide-color-1 .sl-content {
background: #8d0f39;
}
.sl-slide-color-1 [data-icon]:after {
color: #e6a6bb;
text-shadow: 0 0 1px #e6a6bb;
box-shadow: inset 0 0 0 10px #e6a6bb;
}
.sl-slide-color-1.sl-slide h2,
.sl-slide-color-1.sl-slide blockquote{
color: #fff;
}
.sl-slide-color-1.sl-slide blockquote:before {
color: #7b0c31;
}
/* Цвет 2 для слайда */
.sl-slide-color-2 .sl-content-cut,
.sl-slide-color-2 .sl-content {
background: #ade1f4;
}
.sl-slide-color-2 [data-icon]:after {
text-shadow: 0 0 1px #8bc7dd;
color: #8bc7dd;
}
.sl-slide-color-2.sl-slide h2,
.sl-slide-color-2.sl-slide blockquote{
color: #fff;
text-shadow: 1px 1px 1px rgba(0,0,0,0.2);
}
.sl-slide-color-2.sl-slide blockquote:before {
color: #8bc7dd;
}
/* Цвет 3 для слайда */
.sl-slide-color-3 .sl-content-cut,
.sl-slide-color-3 .sl-content {
background: #ffeb41;
}
.sl-slide-color-3.sl-slide h2,
.sl-slide-color-3.sl-slide blockquote{
color: #000;
text-shadow: 1px 1px 1px rgba(0,0,0,0.1);
}
.sl-slide-color-3.sl-slide blockquote:before {
color: #ecd82c;
}
И наконец, добавим движения нашим элементам контента. Когда мы перемещаемся по слайдам, то хотим, чтобы элементы контента делали что-нибудь интересное/* Анимация для элементов */
.sl-trans-elems .sl-deco{
animation: roll 1s ease-out both;
}
.sl-trans-elems h2{
animation: moveUp 1s ease-in-out both;
}
.sl-trans-elems blockquote{
animation: fadeIn 0.5s linear 0.5s both;
}
@keyframes roll{
0% {transform: translateX(500px) rotate(360deg); opacity: 0;}
100% {transform: translateX(0px) rotate(0deg); opacity: 1;}
}
@keyframes moveUp{
0% {transform: translateY(40px);}
100% {transform: translateY(0px);}
}
@keyframes fadeIn{
0% {opacity: 0;}
100% {opacity: 1;}
}
Декоративный элемент будет "выкатываться" с правой стороны, заголовок будет двигаться вверх, а цитата будет растворяться.
Теперь, когда мы будем перемещаться по слайдам назад, то мы бы хотели увидеть, что же случиться при таком реверсе:
.sl-trans-back-elems .sl-deco{
animation: scaleDown 1s ease-in-out both;
}
.sl-trans-back-elems h2{
animation: fadeOut 1s ease-in-out both;
}
.sl-trans-back-elems blockquote{
animation: fadeOut 1s linear both;
}
@keyframes scaleDown{
0% {transform: scale(1);}
100% {transform: scale(0.5);}
}
@keyframes fadeOut{
0% {opacity: 1;}
100% {opacity: 0;}
}
А увидим мы следующее: декоративный элемент будет уменьшаться и растворится.JS
Для начала подключим все используемые скрипты:
<script type="text/jаvascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
<script type="text/jаvascript" src="js/jquery.ba-cond.min.js"></script>
<script type="text/jаvascript" src="js/jquery.transit.min.js"></script>
<script type="text/jаvascript" src="js/jquery.slitslider.js"></script>
<script type="text/jаvascript">
$(function() {
$( '#sl-slider' ).slitslider();
});
</script>
А теперь поговорим подробнее о скрипте jquery.slitslider.js.У нашего плагина доступны следующие опции:
$.Slitslider.defaults = {
speed : 1000, //скорость переходов
autoplay : false, //выключение слайдшоу, true - включение
interval : 4000, // интервал смены слайдов при включенном autoplay в мс
optOpacity : false, // непрозрачность "кусочков" (true, false)
translateF : 160, // Значение в % определяющее скорость, масштабирование переходов при разделении на кусочки.
maxAngle : 25, // максимальное значение угла
maxScale : 2 // максимальная величина масштабирования
};
Начнем с _init функции:_init: function(options) {
// опции
this.options = $.extend(true, {}, $.Slitslider.defaults, options);
// слайдер
this.$slides = this.$slider.children('.sl-slide').hide();
// общее количество слайдов
this.slidesCount = this.$slides.length;
// текущий слайд
this.current = 0;
// анимировать текущий?
this.isAnimating = false;
// получение размеров окна
this._getWinSize();
// создание макета
this._layout();
// загрузка событий
this._loadEvents();
// старт слайдшоу
if (this.options.autoplay) {
this._startSlideshow();
}
}
Взглянем на _layout функцию
_layout: function() {
this.$slideWrapper = $('<div class="sl-slides-wrapper" />');
// wrap the slides into "sl-slides-wrapper"
this.$slides.wrapAll(this.$slideWrapper).each(function(i) {
var $slide = $(this),
// vertical || horizontal
orientation = $slide.data('orientation');
$slide.addClass('sl-slide-' + orientation).children().wrapAll('<div class="sl-content-wrapper" />').wrapAll('<div class="sl-content" />');
});
// set the right size of the slider and slides according to the current window size
this._setSize();
// show first slide
this.$slides.eq(this.current).show();
// add navigation
if (this.slidesCount > 1) {
this.$slider.append('<nav><span class="sl-prev">Previous</span><span class="sl-next">Next</span></nav>');
}
}
Вставляем оболочку слайдов в разделитель с классом sl-slides-wrapper. Как мы уже упоминали ранее, содержимое каждого слайда будет обернуто двумя разделителями, первый с классом sl-content и второй с классом sl-content-wrapper.
Также мы добавим соответствующий класс определяющий ориентацию (sl-slide-vertical или sl-slide-horizontal).
Слайдер и разделитель sl-content-wrapper должны иметь ширину и высоту окна. Это мы будем делать в _setSize функции.
Наконец, мы покажем текущий/первый слайд и добавим кнопки навигации.
В функции _loadEvents мы будем связывать события для двух кнопок навигации и событие изменения размера окна (smartresize):
_loadEvents: function() {
var _self = this;
if (this.slidesCount > 1) {
// navigate "in" or "out"
this.$slider.find('nav > span.sl-prev').on('click.slitslider', function(event) {
if (_self.options.autoplay) {
clearTimeout(_self.slideshow);
_self.options.autoplay = false;
}
_self._navigate('out');
}).end().find('nav > span.sl-next').on('click.slitslider', function(event) {
if (_self.options.autoplay) {
clearTimeout(_self.slideshow);
_self.options.autoplay = false;
}
_self._navigate('in');
});
}
$(window).on('smartresize.slitslider', function(event) {
// update window size
_self._getWinSize();
_self._setSize();
});
}
Теперь посмотрим, как мы будем "разделять" слайды и переходить к следующему:
_navigate: function(dir) {
// return if currently navigating / animating
if (this.isAnimating) {
return false;
}
var _self = this;
// while isAnimating is true we cant navigate..
this.isAnimating = true;
// the current slide
var $currentSlide = this.$slides.eq(this.current),
css;
// set new current
(dir === 'in') ? ((this.current < this.slidesCount - 1) ? ++this.current : this.current = 0) : ((this.current > 0) ? --this.current : this.current = this.slidesCount - 1)
// next slide to be shown
var $nextSlide = this.$slides.eq(this.current).show(),
// the slide we want to cut and animate
$movingSlide = (dir === 'in') ? $currentSlide : $nextSlide,
// the following are the data attrs set for each slide
orientation = $movingSlide.data('orientation') || 'horizontal',
cut1angle = $movingSlide.data('cut1Rotation') || 0,
cut1scale = $movingSlide.data('cut1Scale') || 1,
cut2angle = $movingSlide.data('cut2Rotation') || 0,
cut2scale = $movingSlide.data('cut2Scale') || 1;
this._validateValues(cut1angle, cut2angle, cut1scale, cut2scale, orientation);
if (orientation === 'vertical') {
css = {
marginLeft: -this.windowProp.width / 2
};
} else if (orientation === 'horizontal') {
css = {
marginTop: -this.windowProp.height / 2
};
}
// default slides cuts style
var resetStyle = (orientation === 'horizontal') ? {
x: '0%',
y: '0%',
rotate: 0,
scale: 1,
opacity: 1
} : {
x: '0%',
y: '0%',
rotate: 0,
scale: 1,
opacity: 1
},
// cut1 style
cut1Style = (orientation === 'horizontal') ? {
y: '-' + this.options.translateF + '%',
rotate: cut1angle,
scale: cut1scale
} : {
x: '-' + this.options.translateF + '%',
rotate: cut1angle,
scale: cut1scale
},
// cut2 style
cut2Style = (orientation === 'horizontal') ? {
y: this.options.translateF + '%',
rotate: cut2angle,
scale: cut2scale
} : {
x: this.options.translateF + '%',
rotate: cut2angle,
scale: cut2scale
};
if (this.options.optOpacity) {
cut1Style.opacity = 0;
cut2Style.opacity = 0;
}
// we are adding the classes sl-trans-elems and sl-trans-back-elems
// to the slide that is either coming "in"
// or going "out" according to the direction
// the idea is to make it more interesting by
// giving some animations to the respective slides elements
(dir === 'in') ? $nextSlide.addClass('sl-trans-elems'): $currentSlide.addClass('sl-trans-back-elems');
$currentSlide.removeClass('sl-trans-elems');
// add the 2 cuts and animate them
// (we are using the jquery.transit plugin:
// http://ricostacruz.com/jquery.transit/ to
// add transitions to the elements)
$movingSlide.css('z-index', this.slidesCount).find('div.sl-content-wrapper').wrap('<div class="sl-content-cut" />').parent().cond(dir === 'out', function() {
this.css(cut1Style).transition(resetStyle, _self.options.speed, dir);
}, function() {
this.transition(cut1Style, _self.options.speed, dir)
}).clone().appendTo($movingSlide).cond(dir === 'out', function() {
var cut = this;
cut.css(cut2Style).transition(resetStyle, _self.options.speed, dir, function() {
_self._onEndNavigate(cut, $currentSlide, dir);
})
}, function() {
var cut = this;
cut.transition(cut2Style, _self.options.speed, dir, function() {
_self._onEndNavigate(cut, $currentSlide, dir);
})
}).find('div.sl-content-wrapper').css(css);
}
Таким образом, весь фокус заключается в том, чтобы дублировать контент слайда в разделителях с классом sl-content-cut и установить второй margin-left или margin-top от половины ширины окна или высоты.Затем, согласно значений указанных в data атрибутах мы будем анимировать разделение слайдов с помощью плагина jQuery Transit (http://ricostacruz.com/jquery.transit/).
В соответствии с направлением, мы либо режем текущий слайд и показываем следующий, или будем резать предыдущий (не показано) и помещать его поверх текущего.
Мы добавляем классы sl-trans-elems и sl-trans-back-elems к слайду, который будет следующим (когда мы двигаемся вперед) или к текущему (когда мы двигаемся назад).
Далее функция _onEndNavigate, где мы будем разворачивать содержимое текущего слайда, удалив два разделителя sl-content-cut:
_onEndNavigate: function($slice, $oldSlide, dir) {
// reset previous slide's style after next slide is shown
var $slide = $slice.parent(),
removeClasses = 'sl-trans-elems sl-trans-back-elems';
// remove second slide's cut
$slice.remove();
// unwrap..
$slide.css('z-index', 1).find('div.sl-content-wrapper').unwrap();
// hide previous current slide
$oldSlide.hide().removeClass(removeClasses);
$slide.removeClass(removeClasses);
// now we can navigate again..
this.isAnimating = false;
}