От автора: учебник о том, как применив минимум дизайна, создать при помощи анимации CSS и jQuery простой слайдер товаров с категориями. Идея состоит в последовательном пролистывании элементов в зависимости от направления скольжения.
В сегодняшнем учебнике мы покажем, как создать простой слайдер товаров с отдельными элементами при помощи CSS-анимации и небольшого количества jQuery. Мысль была навеяна слайдером продукции Apple, где несколько анимированных элементов прямо-таки «влетают» на страницу. Мы хотели перенести эту концепцию на современную альтернативу минималистичного дизайна онлайн-магазина, где элементы представляют различные категории. В данном случае, категории весьма хорошо подходят для этого типа слайдера товаров, по причине ограниченности его использования. Если нужно показать большее количество элементов, то это решение, несомненно, далеко от оптимального. Однако ограниченность позиций придает этому опыту интересный оттенок.
Разметка слайдера товаров
Для HTML мы применим обертку, которая будет включать в себя несколько неупорядоченных списков, которые будут содержать элементы и навигацию со ссылками на категории. У каждого пункта списка будет ссылка, содержащая изображение и заголовок h4.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
<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
Обратите внимание, что CSS не будет содержать никаких префиксов, но вы найдете их в файлах.
Мы собираемся сделать следующее: изначально нам требуется показать первый список элементов в то время, когда все остальные li будут смещены вправо, вне поля просмотра. При щелчке на навигационную ссылку элементы сдвинутся либо справа, либо слева, в зависимости от текущей позиции и того, какова заново выбранная категория.
Давайте сначала назначим стили обертке, которая является разделом с классом mi-slider. У него будет предопределенная высота, нужная нам для того, чтобы правильно установить расположение ul’ов:
1 2 3 4 5 |
.mi-slider { position: relative; margin-top: 30px; height: 490px; } |
ul будет абсолютно позиционирован, это означает, что все списки будут поверх других. Помните, что нам нужно сдвинуть лишь элементы списка, а не сами списки. Мы устанавливаем события указателя pointer-events на none, так как нам требуется возможность щелкать на ссылки текущего списка:
1 2 3 4 5 6 7 8 9 10 |
.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 текущего списка должны возвращаться в исходное положение, чтобы можно было щелкать на ссылки:
1 2 3 |
.mi-slider ul.mi-current { pointer-events: auto; } |
При деактивированном JavaScript’е нельзя допустить разрушения дизайна (мы используем Modernizr):
1 2 3 4 5 6 7 |
.no-js .mi-slider ul { position: relative; left: auto; bottom: auto; margin: 0; overflow: visible; } |
Для центрирования элементов списка мы назначили ul’у выравнивание по центру, а теперь зададим отображение inline-block шириной 20%. Эта ширина гарантирует, что наши элементы будут подходить к списку.
По умолчанию все элементы списка смещены вправо. Здесь мы применяем 600%, потому что это достаточно большое значение, чтобы убрать их из окна просмотра. Также добавим небольшой переход непрозрачности:
1 2 3 4 5 6 7 8 |
.mi-slider ul li { display: inline-block; padding: 20px; width: 20%; max-width: 300px; transform: translateX(600%); transition: opacity 0.2s linear; } |
Нам не нужно, чтобы они сдвигались при деактивированном JS:
1 2 3 |
.no-js .mi-slider ul li { transform: translateX(0); } |
Давайте назначим стили содержимому элементов списка. Обратите внимание, что мы устанавливаем максимальную ширину max-width изображений на 100%. Это гарантия, что разметка не нарушится, а изображения станут менять размер в соответствии со своей оберткой, которой является li с шириной в процентах:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
.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; } |
При проведении мышью над элементом мы будем анимировать непрозрачность элемента списка:
1 2 3 |
.mi-slider ul li:hover { opacity: 0.7; } |
Для навигации необходимо самое высокое значение, так как списки позиционированы абсолютно. Мы разместим навигацию по центру, назначив боковые margin-ы в auto и установив максимальную ширину в 800px:
1 2 3 4 5 6 7 8 |
.mi-slider nav { position: relative; top: 400px; text-align: center; max-width: 800px; margin: 0 auto; border-top: 5px solid #333; } |
Не нужно показывать навигацию, если JavaScript не активирован:
1 2 3 |
.no-js nav { display: none; } |
У навигационных ссылок будет щедрый отступ, а для состояния проведения мышью мы назначим им переход:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
.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; } |
Класс mi-selected, совсем как класс списков mi-current, мы установим с помощью JavaScript’а.
Теперь добавим вверху маленькую стрелку. Для создания двух треугольников с границами воспользуемся псевдоклассами :before и :after:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
.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; } |
Теперь перейдем к самому интересному – анимации. Первая – это масштабирование элементов первого списка. Анимация scaleUp также будет содержать элементы, смещенные на 0, так как нам нужно, чтобы они находились в области просмотра:
1 2 3 4 5 6 7 8 9 |
.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); } } |
Давайте добавим каждому элементу списка разную задержку delay, чтобы те появлялись поочередно:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
.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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
/* 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; } |
Теперь нужно установить соответствующие задержки анимации, тоже в зависимости от направления. Например, первый элемент будет выскальзывать без задержки, если он появляется справа, а также когда уходит влево. То же самое верно для последнего элемента:
1 2 3 4 5 6 |
.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; } |
Соответственно будут установлены увеличенные задержки:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
.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% с тем, чтобы элементы, находящиеся слева, были вне области просмотра. И так далее:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
@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%); } } |
И последнее по счету, но отнюдь не по важности – давайте применим медиазапросы, чтобы адаптировать содержимое слайдера к маленьким экранам.
Начнем с подгонки навигации, чтобы та не разрушалась при слишком маленьком экране:
1 2 3 4 5 6 7 8 9 10 |
@media screen and (max-width: 910px){ .mi-slider nav { max-width: 90%; } .mi-slider nav a { font-size: 12px; padding: 40px 10px 30px 14px; } } |
Так как мы установили слайдеру фиксированную высоту, нам нужно убедиться, что она способна адаптироваться:
1 2 3 4 5 6 7 8 9 |
@media screen and (max-width: 740px){ .mi-slider { height: 300px; } .mi-slider nav { top: 220px; } } |
Для действительно маленьких экранов нам не только нужно все супермаленькое, но и легкая навигация для устройств с сенсорными дисплеями. Так что просто покажем все категории. Назначим стили таким образом, чтобы ничего не скрывалось, а все списки демонстрировались один под другим:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
@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; } } |
Вот и все стили. Теперь проконтролируем кое-что с помощью jQuery.
JavaScript
Давайте создадим для своего слайдера простой плагин jQuery. Большая часть работы проделана в CSS, где мы определили всю анимацию. Плагин в основном будет нацелен на добавление и удаление классов, и контроль текущей показываемой категории. Для браузеров, не поддерживающих анимацию, мы вернемся к простому подходу show/hide.
Начнем с кэширования некоторых элементов и инициализации нескольких переменных:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
_init : function( options ) { // the categories (ul) this.$categories = this.$el.children( 'ul' ); // the navigation this.$navcategories = this.$el.find( 'nav > a' ); var animEndEventNames = { 'WebkitAnimation' : 'webkitAnimationEnd', 'OAnimation' : 'oAnimationEnd', 'msAnimation' : 'MSAnimationEnd', 'animation' : 'animationend' }; // animation end event name this.animEndEventName = animEndEventNames[ Modernizr.prefixed( 'animation' ) ]; // animations and transforms support this.support = Modernizr.csstransforms && Modernizr.cssanimations; // if currently animating this.isAnimating = false; // current category this.current = 0; var $currcat = this.$categories.eq( 0 ); if( !this.support ) { this.$categories.hide(); $currcat.show(); } else { $currcat.addClass( 'mi-current' ); } // current nav category this.$navcategories.eq( 0 ).addClass( 'mi-selected' ); // initialize the events this._initEvents(); } |
Мы привяжем событие щелчка к ссылкам навигационной категории под слайдером. Допустим, что индекс каждой соотносится с индексом соответствующей категории (ul’а). При щелчке на ссылку категории элементы текущей категории убираются, а новой категории появляются один за другим (помните, мы определили в CSS задержку анимации).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
_initEvents : function() { var self = this; this.$navcategories.on( 'click.catslider', function() { self.showCategory( $( this ).index() ); return false; } ); // reset on window resize.. $( 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; // update selected navigation 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', // current category $currcat = this.$categories.eq( this.current ), // new category $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; } } |
Вот и все, мы закончили с вами создание простого слайдера товаров! Надеюсь, что вам понравился этот учебник, оказался для вас полезным и вдохновляющим!
Автор: Mary Lou
Источник: //tympanus.net/
Редакция: Команда webformyself.
Комментарии (8)