В этом учебном руководстве мы создадим полноэкранное слайд-шоу со скручиванием: мы будем скрывать открытый слайд с эффектом разреза, чтобы показать следующий или предыдущий слайд. При помощи различных атрибутов мы определим тип, угол вращения и масштаб слайдов, давая нам возможность создать уникальные эффекты переходов у каждого слайда.
Мы будем использовать следующие jQuery плагины:
- jQuery cond от Ben Alman.
- jQuery Transit от Rico Sta. Cruz.
Давайте начнем с HTML разметки.
HTML разметка
Наша начальная разметка будет состоять из основного контейнера с классом и id sl-slider, который будет содержать все слайды. Также добавили различные цвета для слайдов:
<section id="sl-slider" class="sl-slider">
<div class="sl-slide">
<div class="sl-deco" data-icon="6"></div>
<h2>A bene placito</h2>
<blockquote>
<p>You have just dined, and however scrupulously
the slaughterhouse is concealed in the graceful
distance of miles, there is complicity.
</p>
<cite>Ralph Waldo Emerson</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 должна быть или «вертикалью» или «горизонталью». Это для того что бы знать где мы будем «разрезать» слайд. Он будет разрезан или горизонтально или вертикально. Значение 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">
Наша структура — “base structure”. Чтобы создавать эффекты JavaScript мы будем использовать эту структуру.
<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">
<!-- the content -->
</div>
</div>
</div>
<!-- ... -->
</div>
<nav>
<span class="sl-prev">Previous</span>
<span class="sl-next">Next</span>
</nav>
</section>
Мы добавим навигацию и wrappers для контента.
В момент, когда мы перемещаемся к следующему или предыдущему слайду, мы возьмем текущий слайд и скопируем его wrappers контента, создавая «разрез»:
<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;
}
Ширина и высота будут установлены динамически при помощи Javascript.
Для стрелок навигации мы будем использовать технологию image-less. Мы будем просто использовать небольшие поля и повернем их на 45 градусов.
.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, мы будем контролировать внешний вид, и разрезать слайды с JavaScript:
.sl-slide {
z-index: 1;
}
Контент «cuts» будет абсолютно позиционирован. Общий стиль для него:
/* The duplicate parts/cuts */
.sl-content-cut {
overflow: hidden;
position: absolute;
box-sizing: content-box;
background: #fff;
}
Здесь мы используем box-sizing: content-box, потому что по умолчанию (в нашем normlize.css) мы используем border-box.
Контент «cuts» будет горизонтальным или вертикальным, а это означает, что высота или ширина будет составлять половину экрана. Для того чтобы не видеть края разреза, когда мы поворачиваем его, мы добавим некоторое дополнение.
/* 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;
}
Мы используем отрицательные значения позиции, чтобы «подтянуть» div на место.
Стиль контента и содержания:
/* Content wrapper */
/* Width and height is set dynamically */
.sl-content-wrapper {
position: absolute;
}
.sl-content {
width: 100%;
height: 100%;
background: #fff;
}
Div с классом sl-content-wrapper получит динамичную высоту и ширину. Если, например, слайд горизонтальный, оболочка будет иметь ширину в 100% от ширины экрана и 50% от высоты экрана. Оболочка второго разреза будет иметь отрицательный верхний (горизонтальный) или левый (вертикальный) запас для того, чтобы «подтянуть» дублированный контент вверх или влево.
Элементы, которые мы будем использовать в контенте, будут декоративными элементами (кружки с изображением животных), заголовки и цитаты. Мы будем использовать различные шрифты, чтобы у нас были красивые “иконки” животных, которые мы разместим в виде псевдо элемента декоративного div.
Div с классом sl-deco, как и все другие элементы контента, будет иметь абсолютную позицию. Мы разместим его по центру и дадим ему основание со значением 50%:
/* Content elements */
.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 атрибут "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 создаст «фальшивый» эффект вставки границ.
Заголовок будет абсолютнопозиционирован, и мы дадим ему то же значение, как и у декоративного элемента. Затем мы добавим отрицательную нижнюю границу для того, чтобы поместить её в другой элемент. Также как мы можем использовать декоративный элемент в качестве точки отсчета и положения относительно других элементов, у которых следует использовать отрицательные поля снизу:
.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% и соответствующее выравнивания текста:
.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';
}
И cite будет иметь другой вид:
.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;
}
Для остальных цветов просто установим различные значения:
/* Color 1 slides */
.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;
}
/* Color 2 slides */
.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;
}
/* Color 3 slides */
.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;
}
А теперь, давайте добавим некоторую анимацию к контенту!
/* Animations for elements */
.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;
}
<hh user=keyframes> roll{
0% {transform: translateX(500px) rotate(360deg); opacity: 0;}
100% {transform: translateX(0px) rotate(0deg); opacity: 1;}
}
<hh user=keyframes> moveUp{
0% {transform: translateY(40px);}
100% {transform: translateY(0px);}
}
<hh user=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;
}
<hh user=keyframes> scaleDown{
0% {transform: scale(1);}
100% {transform: scale(0.5);}
}
<hh user=keyframes> fadeOut{
0% {opacity: 1;}
100% {opacity: 0;}
}
JavaScript
Давайте сначала взглянем на возможности плагина:
$.Slitslider.defaults = {
speed : 1000,
autoplay : false,
interval : 4000,
optOpacity : false,
translateF : 160,
maxAngle : 25,
maxScale : 2
};
Мы можем установить скорость переходов, установить слайд-шоу, чтобы запускать автоматически с определенным интервалом, а также сделать разрез.
Опция translateF это количество переводов и сокращений в процентном отношении. Вы можете изменять это значение по мере необходимости, также можете изменить масштаб слайда и угол при помощи data атрибутов maxAngle и maxScale.
Мы начнем с выполнения функции _init.
_init : function( options ) {
// the options
this.options = $.extend( true, {}, $.Slitslider.defaults, options );
// the slider
this.$slides = this.$slider.children( '.sl-slide' ).hide();
// total number of slides
this.slidesCount= this.$slides.length;
// the current slide
this.current = 0;
// currently animating?
this.isAnimating= false;
// get the window size
this._getWinSize();
// build the layout
this._layout();
// load some events
this._loadEvents();
// start the slideshow
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>'
);
}
}
Wrapper вставляется в div с классом "sl-slides-wrapper". Как мы уже упоминали ранее, контент каждого слайда, также будет включать в себя wrapper с div, первый с классом 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 );
}
Таким образом, весь фокус в том, чтобы дублировать содержание слайда в div с классом sl-content-cut. Это будет выглядеть «нормальным», и мы не увидим разделение.
Мы будем анимировать сокращение слайда, используя JQuery плагин Transit.
В соответствии с направлением, мы разрежем текущий слайд и покажем следующий, или мы будем разрезать предыдущий, и соберем его по кусочкам поверх текущего.
Мы добавим классы sl-trans-elems и sl-trans-back-elems к соседнему слайду или текущему. Как мы уже видели в части CSS, мы добавили классы анимации к элементам контента.
После окончания перехода, мы вызовем функцию _onEndNavigate, в которой мы будем разворачивать содержимое текущего слайда, таким образом удалив два div с классом 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;
}
Автор: Lecaw