Полноэкранное слайд-шоу с эффектом разреза на CSS3 и jQuery

В этом учебном руководстве мы создадим полноэкранное слайд-шоу с эффектом разреза: "разрезаем" открытый слайд и отображаем следующий или предыдущий. При помощи различных атрибутов мы определим тип, угол вращения и масштаб слайдов, это даст возможность создать уникальные эффекты переходов для каждого слайда.

Сложность

Мы будем использовать следующие 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-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">

Для создания 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>

Добавим навигацию и оболочку для контента.

В момент, когда мы перемещаемся между слайдами, мы берем текущий слайд и копируем его оболочку, создавая разрез:

<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;
}

Общий стиль для класса sl-content-cut:

.sl-content-cut {
    overflow: hidden;
    position: absolute;
    box-sizing: content-box;
    background: #fff;
}

Здесь мы используем свойство box-sizing: content-box, потому что по умолчанию (в normlize.css) мы используем border-box.

sl-content-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;
}
 
/* Вертикальный разрез */
 
.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% от высоты экрана. Оболочка следующего разреза будет иметь отрицательный верхний (горизонтальный) или левый (вертикальный) запас для того, чтобы "подтянуть" дублированный контент вверх или влево.

Элементы используемые для контента, будут декоративными элементами (кружки с изображением животных), заголовки и цитаты. Для красоты, будем использовать различные шрифты для того, чтобы у нас были красивые “иконки” животных.

Контейнер с классом sl-deco, как и все другие элементы контента, будет иметь абсолютную позицию. Разместим его по центру, снизу сделаем отступ в 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 создаст "фальшивый" эффект вставки рамки.

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

.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';
}

Сами кавычки::

.sl-slide blockquote cite {
    font-size: 10px;
    font-style: normal;
    text-transform: uppercase;
    letter-spacing: 4px;
}

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

Темные или черные слайды будут иметь перевернутую цветовую гамму:

/* Темные слайды */
.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;}
}

JavaScript

Для начала взглянем на параметры плагина:

$.Slitslider.defaults   = {
    speed       : 1000,
    autoplay    : false,
    interval    : 4000,
    optOpacity  : false,
    translateF  : 160,
    maxAngle    : 25,
    maxScale    : 2
};

Мы можем задать скорость переходов, установить слайд-шоу, чтобы запускать автоматически с определенным временным интервалом, а также сделать разрез.

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

Начнем с выполнения функции _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.получение размера окна
    this._getWinSize();
    // построение слоя
    this._layout();
    // загрузка обработчиков
    this._loadEvents();
    // запуск слайдшоу
    if( this.options.autoplay ) {
 
        this._startSlideshow();
 
    }
 
}

Давайте взглянем на функцию _layout:

_layout             : function() {

this.$slideWrapper = $( '<div class="sl-slides-wrapper" />' );
// добавить слайды в "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" />' );

} );

// установка размера слайдера в зависимости от размера окна
this._setSize();
// отображение первого слайда
this.$slides.eq( this.current ).show();
// добавление навигации
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 ) {
 
        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 ) {
 
        // обновление размера окна
        _self._getWinSize();
        _self._setSize();
 
    } );
 
}

Давайте посмотрим на "срез" и переход к следующему слайду:

_navigate           : function( dir ) {

if( this.isAnimating ) {

return false;

}

var _self = this;

// если isAnimating = нельзя воспользоваться навигацией
this.isAnimating = true;

// текущий слайд
var $currentSlide = this.$slides.eq( this.current ), css;

// смена текущего
( dir === 'in' ) ?
( ( this.current < this.slidesCount - 1 ) ? ++this.current : this.current = 0 ) :
( ( this.current > 0 ) ? --this.current : this.current = this.slidesCount - 1 )

// следующий слайд для показа
var $nextSlide = this.$slides.eq( this.current ).show(),
// обрезка и анимация слайда
$movingSlide = ( dir === 'in' ) ? $currentSlide : $nextSlide,
// атрибуты data для каждого слайда
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 };

}
// стиль обрезки по умолчанию
var resetStyle = ( orientation === 'horizontal' ) ? { x : '0%', y : '0%', rotate : 0, scale : 1, opacity : 1 } : { x : '0%', y : '0%', rotate : 0, scale : 1, opacity : 1 },

// стиль обрезки 1
cut1Style = ( orientation === 'horizontal' ) ? { y : '-' + this.options.translateF + '%', rotate : cut1angle, scale : cut1scale } : { x : '-' + this.options.translateF + '%', rotate : cut1angle, scale : cut1scale },

// стиль обрезки 2
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;

}

// добавление классов sl-trans-elems и sl-trans-back-elems
( dir === 'in' ) ? $nextSlide.addClass( 'sl-trans-elems' ) : $currentSlide.addClass( 'sl-trans-back-elems' );

$currentSlide.removeClass( 'sl-trans-elems' );

// добавление обрезки и анимации
// (мы используем плагин jquery.transit:
// http://ricostacruz.com/jquery.transit/ для
// добавления переходов к контенту)

$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.

Анимируем разделение слайда, используя JQuery плагин Transit.

В соответствии с направлением, разрежем текущий слайд и отобразим следующий.

Добавим классы sl-trans-elems и sl-trans-back-elems к соседнему слайду или текущему. Как мы уже видели в части CSS, мы добавили классы анимации к элементам контента.

После окончания перехода, вызовем функцию _onEndNavigate, разворачивает содержимое текущего слайда, таким образом удалив два контейнера с классом sl-content-cut :

_onEndNavigate      : function( $slice, $oldSlide, dir ) {

    var $slide          = $slice.parent(),
        removeClasses   = 'sl-trans-elems sl-trans-back-elems';
 
    $slice.remove();
    $slide.css( 'z-index', 1 )
          .find( 'div.sl-content-wrapper' )
          .unwrap();
 
    $oldSlide.hide().removeClass( removeClasses );
    $slide.removeClass( removeClasses );
    this.isAnimating = false;
 
}

Перевод статьи Fullscreen Slit Slider with jQuery and CSS3

Тэги: transitionslideshowslideranimateplugin

Вход

Уважаемый пользователь! Мы обнаружили, что вы используете AdBlock и вынуждены скрыть часть материалов на нашем сайте. Siteacademy существует и развивается за счет доходов от рекламы. Просим внести наш сайт в список исключений или отключить Блокировщик рекламы на нашем сайте.