Lecaw

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

Как создать простой слайдер на jQuery

January 25, 2013
CSS
41hits

Сегодня мы покажем, как создать простой слайдер с jQuery и CSS анимацией. Идея была вдохновлена ползунком продукта Aplle, когда несколько небольших элементов движутся с вращающейся анимацией. Мы хотим перевести это понятие в современную альтернативу для интернет-магазина, где элементы представляют различные категории. Вполне приемлемо использовать категории из-за ограниченной функциональности такого типа слайдера. Это не совсем оптимальное решение если показывать большое количество элементов на слайдере.

ДЕМО Исходные файлы

 

 

HTML разметка

Для HTML мы будем использовать контейнер с некоторыми неупорядоченными списками, в которых будут элементы навигации со ссылками категории. У каждого элемента списка будет ссылка, которая содержит изображение и заголовок h4.

<div id="mi-slider" class="mi-slider"> 
    <ul>
        <li><a href="#"><img src="/images/1.jpg" alt="img01"><h4>Boots</h4></a></li>
        <li><a href="#"><img src="/images/2.jpg" alt="img02"><h4>Oxfords</h4></a></li>
        <li><a href="#"><img src="/images/3.jpg" alt="img03"><h4>Loafers</h4></a></li>
        <li><a href="#"><img src="/images/4.jpg" alt="img04"><h4>Sneakers</h4></a></li>
    </ul>
    <ul>
        <li><a href="#"><img src="/images/5.jpg" alt="img05"><h4>Belts</h4></a></li>
        <li><a href="#"><img src="/images/6.jpg" alt="img06"><h4>Hats & Caps</h4></a></li>
        <li><a href="#"><img src="/images/7.jpg" alt="img07"><h4>Sunglasses</h4></a></li>
        <li><a href="#"><img src="/images/8.jpg" alt="img08"><h4>Scarves</h4></a></li>
    </ul>
    <ul>
        <li><a href="#"><img src="/images/9.jpg" alt="img09"><h4>Casual</h4></a></li>
        <li><a href="#"><img src="/images/10.jpg" alt="img10"><h4>Luxury</h4></a></li>
        <li><a href="#"><img src="/images/11.jpg" alt="img11"><h4>Sport</h4></a></li>
    </ul>
    <ul>
        <li><a href="#"><img src="/images/12.jpg" alt="img12"><h4>Carry-Ons</h4></a></li>
        <li><a href="#"><img src="/images/13.jpg" alt="img13"><h4>Duffel Bags</h4></a></li>
        <li><a href="#"><img src="/images/14.jpg" alt="img14"><h4>Laptop Bags</h4></a></li>
        <li><a href="#"><img src="/images/15.jpg" alt="img15"><h4>Briefcases</h4></a></li>
    </ul>
    <nav>
        <a href="#">Shoes</a>
        <a href="#">Accessories</a>
        <a href="#">Watches</a>
        <a href="#">Bags</a>
    </nav>
</div>

CSS

Для начала нам нужно, чтобы просто отображался первый список элементов, в то время как все другие элементы списка будут переведены направо, за пределы области просмотра. Когда мы щелкнем по кнопке навигации, элементы будут двигаться справа или слева, в зависимости от текущей позиции и от выбора категории.

Давайте сначала разработаем оболочку с классом mi-slider:

.mi-slider {
    position: relative;
    margin-top: 30px;
    height: 490px;
}

Для списков зададим position: absolute, это означает, что все списки будут друг на друге. Помните, нам нужно переместить только элементы списка, не сами списки.

.mi-slider ul {
    list-style-type: none;
    position: absolute;
    width: 100%;
    left: 0;
    bottom: 140px;
    overflow: hidden;
    text-align: center;
    pointer-events: none;
}

Также для текущего списка необходимо сбросить pointer-events, так, чтобы содержание ссылок активировалось по щелчку:

.mi-slider ul.mi-current {
    pointer-events: auto;
}

Если JavaScript отключен, мы будем использовать Modernizr:

.no-js .mi-slider ul {
    position: relative;
    left: auto;
    bottom: auto;
    margin: 0;
    overflow: visible;
}

Для того чтобы разместить элементы списка по центру, мы задали для ul свойство text-align, также зададим для элементов списка display: inline-block с шириной 20%.

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

.mi-slider ul li {
    display: inline-block;
    padding: 20px;
    width: 20%;
    max-width: 300px;
    transform: translateX(600%);
    transition: opacity 0.2s linear;
}

Без JS мы не сможем сделать чтобы они перемещались:

.no-js .mi-slider ul li {
    transform: translateX(0);
}

Давайте перейдем к контенту элементов списка. Обратите внимание, что мы устанавливаем макс. ширину изображений к 100%.

.mi-slider ul li a,
.mi-slider ul li img {
    display: block;
    margin: 0 auto;
}
 
.mi-slider ul li a {
    outline: none;
    cursor: pointer;
}
 
.mi-slider ul li img {
    max-width: 100%;
    border: none;
}
 
.mi-slider ul li h4 {
    display: inline-block;
    font-family: Baskerville, "Baskerville Old Face", "Hoefler Text", Garamond, "Times New Roman", serif;
    font-style: italic;
    font-weight: 400;
    font-size: 18px;
    padding: 20px 10px 0;
}

При наведении мы анимируем непрозрачность элементов списка:

.mi-slider ul li:hover {
    opacity: 0.7;
} 

У навигации должно быть самое высокое значение, потому что списки расположены абсолютно. Мы разместим навигацию по центру при помощи свойства margin, а также установим максимальную ширину в 800px:

.mi-slider nav {
    position: relative;
    top: 400px;
    text-align: center;
    max-width: 800px;
    margin: 0 auto;
    border-top: 5px solid #333;
}

Если в браузере отключен JavaScript, мы не будем отображать навигацию:

.no-js nav {
    display: none;
}

У навигации будут большие отступы и добавим переход при наведении курсора:

.mi-slider nav a {
    display: inline-block;
    text-transform: uppercase;
    letter-spacing: 5px;
    padding: 40px 30px 30px 34px;
    position: relative;
    color: #888;
    outline: none;
    transition: color 0.2s linear;
}
 
.mi-slider nav a:hover,
.mi-slider nav a.mi-selected {
    color: #000;
}

Теперь, добавим небольшие стрелки сверху. Мы будем использовать псевдо-классы :before и :after, чтобы создать два треугольника:

.mi-slider nav a.mi-selected:after,
.mi-slider nav a.mi-selected:before {
    content: '';
    position: absolute;
    top: -5px;
    border: solid transparent;
    height: 0;
    width: 0;
    position: absolute;
    pointer-events: none;
}
 
.mi-slider nav a.mi-selected:after {
    border-color: transparent;
    border-top-color: #fff;
    border-width: 20px;
    left: 50%;
    margin-left: -20px;
}
 
.mi-slider nav a.mi-selected:before {
    border-color: transparent;
    border-top-color: #333;
    border-width: 27px;
    left: 50%;
    margin-left: -27px;
}

Первая анимация - увеличение масштаба элементов первого списка. Анимация увеличения масштаба будет также включать элементы, которые переводим в 0, потому что нам нужно, чтобы они были в области просмотра:

.mi-slider ul:first-child li,
.no-js .mi-slider ul li {
    animation: scaleUp 350ms ease-in-out both;
}
 
@keyframes scaleUp {
    0% { transform: translateX(0) scale(0); }
    100% { transform: translateX(0) scale(1); }
}

Давайте добавим небольшую задержку к каждому элементу списка так, чтобы они появлялись последовательно:

.mi-slider ul:first-child li:first-child {
    animation-delay: 90ms;
}
 
.mi-slider ul:first-child li:nth-child(2) {
    animation-delay: 180ms;
}
 
.mi-slider ul:first-child li:nth-child(3) {
    animation-delay: 270ms;
}
 
.mi-slider ul:first-child li:nth-child(4) {
    animation-delay: 360ms;
}

В нашем примере у нас будет четыре элемента, поэтому мы определим только четыре задержки. Если бы у нас было больше элементов, мы добавили бы больше задержек.

Для анимации слайдера у нас будет четыре функции: две для перемещения новых элементов и две для перемещения текущих элементов, в зависимости от направления. Таким образом, мы определим четыре класса для списков, которые мы добавим с JavaScript:

/* moveFromRight */
 
.mi-slider ul.mi-moveFromRight li {
    animation: moveFromRight 350ms ease-in-out both;
}
 
/* moveFromLeft */
 
.mi-slider ul.mi-moveFromLeft li {
    animation: moveFromLeft 350ms ease-in-out both;
}
 
/* moveToRight */
 
.mi-slider ul.mi-moveToRight li {
    animation: moveToRight 350ms ease-in-out both;
}
 
/* moveToLeft */
 
.mi-slider ul.mi-moveToLeft li {
    animation: moveToLeft 350ms ease-in-out both;
}

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

.mi-slider ul.mi-moveToLeft li:first-child,
.mi-slider ul.mi-moveFromRight li:first-child,
.mi-slider ul.mi-moveToRight li:nth-child(4),
.mi-slider ul.mi-moveFromLeft li:nth-child(4) {
    animation-delay: 0ms;
}

Соответственно увеличим значение задержки:

.mi-slider ul.mi-moveToLeft li:nth-child(2),
.mi-slider ul.mi-moveFromRight li:nth-child(2),
.mi-slider ul.mi-moveToRight li:nth-child(3),
.mi-slider ul.mi-moveFromLeft li:nth-child(3) {
    -webkit-animation-delay: 90ms;
    animation-delay: 90ms;
}
 
.mi-slider ul.mi-moveToLeft li:nth-child(3),
.mi-slider ul.mi-moveFromRight li:nth-child(3),
.mi-slider ul.mi-moveToRight li:nth-child(2),
.mi-slider ul.mi-moveFromLeft li:nth-child(2) {
    -webkit-animation-delay: 180ms;
    animation-delay: 180ms;
}
 
.mi-slider ul.mi-moveToLeft li:nth-child(4),
.mi-slider ul.mi-moveFromRight li:nth-child(4),
.mi-slider ul.mi-moveToRight li:first-child,
.mi-slider ul.mi-moveFromLeft li:first-child  {
    -webkit-animation-delay: 270ms;
    animation-delay: 270ms;
}

Теперь давайте определим анимацию самостоятельно. Например, перемещение справа будет означать, что мы установим значение translateX в 600% и переместимся в 0. Перемещаясь слева, мы установим начальную позицию в -600% так, чтобы элементы были на левой стороне за пределами области просмотра. И т.д.:

@keyframes moveFromRight {
    0% { transform: translateX(600%); }
    100% { transform: translateX(0); }
}
 
@keyframes moveFromLeft {
    0% { transform: translateX(-600%); }
    100% { transform: translateX(0); }
}
 
@keyframes moveToRight {
    0% { transform: translateX(0%); }
    100% { transform: translateX(600%); }
}
 
@keyframes moveToLeft {
    0% { transform: translateX(0%); }
    100% { transform: translateX(-600%); }
}

Наконец, давайте добавим медиа запросы, чтобы скорректировать контент слайдера для небольших экранов.

Мы будем запускать корректировку навигации когда экран слишком маленький:

@media screen and (max-width: 910px){
    .mi-slider nav {
        max-width: 90%;
    }
 
    .mi-slider nav a {
        font-size: 12px;
        padding: 40px 10px 30px 14px;
    }
}

Так как мы устанавливаем фиксированную высоту слайдера, нужно удостоверится что он адаптивный:

@media screen and (max-width: 740px){
    .mi-slider {
        height: 300px;
    }
 
    .mi-slider nav {
        top: 220px;
    }
}

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

@media screen and (max-width: 490px){ 
    .mi-slider {
        text-align: center;
        height: auto;
    }
 
    .mi-slider ul {
        position: relative;
        display: inline;
        bottom: auto;
        pointer-events: auto;
    }
 
    .mi-slider ul li {
        animation: none !important;
        transform: translateX(0) !important;
        padding: 10px 3px;
        min-width: 140px;
    }
 
    .mi-slider nav {
        display: none;
    }
}

JavaScript

Давайте создадим простой jQuery плагин для нашего слайдера. Большая часть работы была в CSS, где мы определяли все анимации. Плагин будет, главным образом, фокусироваться на добавлении и удалении классов и управлять текущей отображаемой категорией.

Давайте начнем с кэширования некоторых элементов и инициализации нескольких переменных:

_init : function( options ) {
 
    // категории (ul)
    this.$categories = this.$el.children( 'ul' );
    // навигация
    this.$navcategories = this.$el.find( 'nav > a' );
    var animEndEventNames = {
        'WebkitAnimation' : 'webkitAnimationEnd',
        'OAnimation' : 'oAnimationEnd',
        'msAnimation' : 'MSAnimationEnd',
        'animation' : 'animationend'
    };
    // анимация end event name
    this.animEndEventName = animEndEventNames[ Modernizr.prefixed( 'animation' ) ];
    // анимация и transforms support
    this.support = Modernizr.csstransforms && Modernizr.cssanimations;
    // текущая анимация
    this.isAnimating = false;
    // текущая категория
    this.current = 0;
    var $currcat = this.$categories.eq( 0 );
    if( !this.support ) {
        this.$categories.hide();
        $currcat.show();
    }
    else {
        $currcat.addClass( 'mi-current' );
    }
    this.$navcategories.eq( 0 ).addClass( 'mi-selected' );
    // инициализация событий
    this._initEvents();
 
}

Мы свяжем событие щелчка с ссылками категории навигации под слайдером. Мы предполагаем, что индекс каждого элемента соответствует индексу соответствующей категории (ul). Когда по ссылке категории щелкают, элементы текущей категории передвигаются.

_initEvents : function() {
 
    var self = this;
    this.$navcategories.on( 'click.catslider', function() {
        self.showCategory( $( this ).index() );
        return false;
    } );
 
    // сброс размера окна..
    $( window ).on( 'resize', function() {
        self.$categories.removeClass().eq( 0 ).addClass( 'mi-current' );
        self.$navcategories.eq( self.current ).removeClass( 'mi-selected' ).end().eq( 0 ).addClass( 'mi-selected' );
        self.current = 0;
    } );
 
}
 
showCategory : function( catidx ) {
 
    if( catidx === this.current || this.isAnimating ) {
        return false;
    }
    this.isAnimating = true;
    // обновление выбранной категории
    this.$navcategories.eq( this.current ).removeClass( 'mi-selected' ).end().eq( catidx ).addClass( 'mi-selected' );
 
    var dir = catidx > this.current ? 'right' : 'left',
        toClass = dir === 'right' ? 'mi-moveToLeft' : 'mi-moveToRight',
        fromClass = dir === 'right' ? 'mi-moveFromRight' : 'mi-moveFromLeft',
        // текущая категория
        $currcat = this.$categories.eq( this.current ),
        // новая категория
        $newcat = this.$categories.eq( catidx ),
        $newcatchild = $newcat.children(),
        lastEnter = dir === 'right' ? $newcatchild.length - 1 : 0,
        self = this;
 
    if( this.support ) {
 
        $currcat.removeClass().addClass( toClass );
         
        setTimeout( function() {
 
            $newcat.removeClass().addClass( fromClass );
            $newcatchild.eq( lastEnter ).on( self.animEndEventName, function() {
 
                $( this ).off( self.animEndEventName );
                $newcat.addClass( 'mi-current' );
                self.current = catidx;
                var $this = $( this );
                // solve chrome bug
                self.forceRedraw( $this.get(0) );
                self.isAnimating = false;
 
            } );
 
        }, $newcatchild.length * 90 );
 
    }
    else {
 
        $currcat.hide();
        $newcat.show();
        this.current = catidx;
        this.isAnimating = false;
 
    }
 
}

ДЕМО Исходные файлы

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

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

Медиа