Lecaw

Учимся создавать Web-сайты

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

June 9, 2012 1199hits

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

ДЕМО

Исходные файлы 

 

 

 

Мы будем использовать следующие jQuery плагины:

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

ДЕМО

Исходные файлы 

Дополнительная информация

RATTING:
(2 голосов)

Медиа

Комментарии

#1 alex 11.10.2012 20:20
Всем привет.... сделал на сайте пару страничек с таким эффектом... но, после выхода firefox 16... эффект пропал((.... может кто подскажет, как поправить?
Цитировать