Простой слайдер с набором категорий
13 февраля 2013 | Опубликовано в css | 2 Комментариев »
В этом уроке мы будем создавать простой слайдер минималистичного дизайна, используя СSS-анимации и jQuery. Идея будет заключаться в том, чтобы последовательно менять изображения в зависимости от выбранной категории. Данный слайдер отлично подойдет для дизайна онлайн-магазина, в котором будет немного категорий с небольшим количеством товаров.
Разметка
Для 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
Обратите внимание, что СSS не будет содержать вендорные префиксы, но вы сможете найти их в исходных файлах.
Сначала нужно показать первую категорию списка, а остальные li будут смещены вправо и размещаться за пределами области просмотра. Когда мы нажимаем на ссылку навигации, элементы будут сдвигаться либо справа, либо слева, в зависимости от текущей позиции выбранной категории.
Давайте сначала зададим стили для контейнера с классом mi-slider. Он будет иметь определенную высоту, которая нужна для установки позиций с корректными ul
s:
.mi-slider{
position: relative; margin-top: 30px; height: 490px; }
ul будет позиционироваться абсолютно, а это означает, что все списки будут размещаться один над другим. Но нужно помнить, что мы будем двигать элементы списка, а не сам список. Устанавливаем значение pointer-events - none, так как хотим, чтобы елементы были кликабельны.
.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
выравнивание текста по центру, а для пунктов списка свойство display:inline-block с шириной 20%. Эта ширина даст гарантию, что элемент поместится в список и сохранит подвижность.
По умолчанию все элементы списка будут смещены вправо. Используем значение 600%, чтобы убрать их из поля видимости. Также добавляем трансформацию для непрозрачности.
.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); }
Давайте зададим стили для элементов списка. Обратите внимание, что мы установили для свойство max-width изображений 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; }
Навигация должна иметь свойство top, так как список позиционируется абсолютно. Центрируем навигацию и устанавливаем следующие свойства:
.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; }
Класс mi-selected и класс mi-current будут устанавливаться с помощью JavaScript.
Теперь добавим маленькую стрелку вверху. Используем псевдо-классы :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; }
Улучшим визуальное представление с помощью анимации. Первая анимация — расширение пунктов первого списка. Анимация scaleUp также будет включать перемещение пунктов в координату 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; }
В нашем примере максимальное количество элементов будет равно 4, так что мы определяем только четыре задержки. Если у нас будет больше элементов, то количество задержек тоже увеличивается.
Для анимации слайдера мы будем определять 4 случая: два для появления нового списка и два для исчезновения текущего — в зависимости от направления. Определим четыре класса для списков, которые будут добавляться с помощью 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; } }
Приступим к jQuery.
JAVA SCRIPT
Давайте создадим простой JQuery-плагин для нашего слайдера. Большая часть работы делается в CSS, где определяются все анимации. Плагин будет сфокусирован на добавлении и удалении классов, а также на контроле текущей категории.
Давайте начнем с кэширования некоторых элементов и объявления несколько переменных:
_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(); }
Мы будем связывать событие click с выбранной категорией в соответствии с ползунком под слайдером. Предполагаем, что индекс ссылки соответствует индексу каждой категории.
При нажатии на ссылку элементы текущей категории исчезают с области видимости, а на их место один за другим появляются новые (все анимации и задержки определены в CSS ).
_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
Перевод — Дежурка
24 февраля 2013 в 21:30
При обновлении страницы — сбрасывается выбор.
При сваривание или разворота окна браузера — сбрасывается выбор.
2 августа 2013 в 12:42
При уменьшении страницы по бокам видны фото из категорий, от этого можно избавиться?