Простой слайдер для нескольких пунктов
В данном уроке мы сделаем простой слайдер для пунктов списка на основе анимаций CSS и jQuery. Идея для урока инспирирована слайдером продукции Aplle, где несколько товаров выскальзывают с использованием анимации. Мы трансформируем данную концепцию для онлайн магазина, чтобы представлять товары в разных категориях. Категории с небольшим количеством товаров отлично подходят к данному слайдеру. Конечно, для большого количества товаров такое решение далеко от оптимума, но для магазинов с небольшим ассортиментом слайдер станет отличным инструментом.
Разметка
В качестве разметки HTML будем использовать элемент div
, который содержит несколько неупорядоченных списков. В них размещаются пункты и навигация с ссылками категорий. Каждый пункт имеет ссылку с изображением и заголовком h4
.
<div id="mi-slider" class="mi-slider"> <ul> <li><a href="#"><img src="/images/1.jpg" alt="img01"><h4>Ботинки</h4></a></li> <li><a href="#"><img src="/images/2.jpg" alt="img02"><h4>Классика</h4></a></li> <li><a href="#"><img src="/images/3.jpg" alt="img03"><h4>Мокасины</h4></a></li> <li><a href="#"><img src="/images/4.jpg" alt="img04"><h4>Кроссовки</h4></a></li> </ul> <ul> <li><a href="#"><img src="/images/5.jpg" alt="img05"><h4>Ремни</h4></a></li> <li><a href="#"><img src="/images/6.jpg" alt="img06"><h4>Шляпы и кепи</h4></a></li> <li><a href="#"><img src="/images/7.jpg" alt="img07"><h4>Очки</h4></a></li> <li><a href="#"><img src="/images/8.jpg" alt="img08"><h4>Шарфы</h4></a></li> </ul> <ul> <li><a href="#"><img src="/images/9.jpg" alt="img09"><h4>Свобода</h4></a></li> <li><a href="#"><img src="/images/10.jpg" alt="img10"><h4>Роскошь</h4></a></li> <li><a href="#"><img src="/images/11.jpg" alt="img11"><h4>Спорт</h4></a></li> </ul> <ul> <li><a href="#"><img src="/images/12.jpg" alt="img12"><h4>Чемоданы</h4></a></li> <li><a href="#"><img src="/images/13.jpg" alt="img13"><h4>Дорожные сумки</h4></a></li> <li><a href="#"><img src="/images/14.jpg" alt="img14"><h4>Для ноутбуков</h4></a></li> <li><a href="#"><img src="/images/15.jpg" alt="img15"><h4>Портфели</h4></a></li> </ul> <nav> <a href="#">Обувь</a> <a href="#">Аксессуары</a> <a href="#">Часы</a> <a href="#">Сумки</a> </nav> </div>
CSS
Примечание: префиксы производителей браузеров опущены для большей наглядности кода.
Изначально нужно выводить первый список с пунктами, а остальные элементы li
смещаем вправо за пределы области видимости. При нажатии на ссылку навигации пункты будут выскальзывать справа или слева, в зависимости от текущей позиции выбранной категории.
Сначала определим стили для контейнера, который будет иметь класс mi-slider. У него будет предустановлена высота, которая требуется для установки правильного положения элементов ul
:
.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; }
Без JavaScript смещения не требуется:
.no-js .mi-slider ul li { transform: translateX(0); }
Определим стили для содержимого пунктов списка. Обратите внимание на установку для свойства изображений max-width
значения 100%. Таким образом обеспечивается целостность шаблона при масштабировании изображений в соответствии с размерами контейнера, который представляет собой наш элемент li
с процентным значением ширины.
.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,
так как шаблон позиционируется абсолютно. Мы центрируем навигацию устанавливая автоматические боковые поля и устанавливая максимальную ширину 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; }
Класс 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; }
Для нашего примера мы имеем максимум четыре пункта, поэтому и определены только четыре задержки. Если вам требуется большее количество пунктов, нужно определить большее количество задержек.
Для анимаций выскальзывания определяем четыре случая: два для выскальзывания нового пункта и два для убирания текущего пункта, в зависимости от направления. Определим четыре класса для списков, которые добавляются с помощью JavaScript:
/* Движение справа */ .mi-slider ul.mi-moveFromRight li { animation: moveFromRight 350ms ease-in-out both; } /* Движение слева */ .mi-slider ul.mi-moveFromLeft li { animation: moveFromLeft 350ms ease-in-out both; } /* Движение направо */ .mi-slider ul.mi-moveToRight li { animation: moveToRight 350ms ease-in-out both; } /* Движение налево */ .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.
JavaScript
Сделаем простой плагин для нашего слайдера. Большая часть работы выполняется в 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' }; // Название анимации и события this.animEndEventName = animEndEventNames[ Modernizr.prefixed( 'animation' ) ]; // Поддержка анимаций и событий 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(); },
Привязываем событие click к ссылке категории под слайдером. Предполагаем, что индекс каждой ссылки соответствует индексу категории (элемент ul
). При нажатии на ссылку пункты текущей категории убираются с экрана, а на их место выскальзывают новые один за другим (все анимации и задержки определены в CSS ).
_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 ); // Решение для ошибки в Chrome self.forceRedraw( $this.get(0) ); self.isAnimating = false; } ); }, $newcatchild.length * 90 ); } else { $currcat.hide(); $newcat.show(); this.current = catidx; this.isAnimating = false; } }, // На основании http://stackoverflow.com/a/8840703/989439 forceRedraw : function(element) { if (!element) { return; } var n = document.createTextNode(' '), position = element.style.position; element.appendChild(n); element.style.position = 'relative'; setTimeout(function(){ element.style.position = position; n.parentNode.removeChild(n); }, 25); } } $.fn.catslider = function( options ) { var instance = $.data( this, 'catslider' ); if ( typeof options === 'string' ) { var args = Array.prototype.slice.call( arguments, 1 ); this.each(function() { instance[ options ].apply( instance, args ); }); } else { this.each(function() { instance ? instance._init() : instance = $.data( this, 'catslider', new $.CatSlider( options, this ) ); }); } return instance; };
Готово!
Источник: http://feedproxy.google.com/~r/ruseller/CdHX/~3/SIK-FN0pDU4/lessons.php
Дайджест новых статей по интернет-маркетингу на ваш email
Новые статьи и публикации
- 2024-11-26 » Капитан грузового судна, или Как начать использовать Docker в своих проектах
- 2024-11-26 » Обеспечение безопасности ваших веб-приложений с помощью PHP OOP и PDO
- 2024-11-22 » Ошибки в Яндекс Вебмастере: как найти и исправить
- 2024-11-22 » Ошибки в Яндекс Вебмастере: как найти и исправить
- 2024-11-15 » Перенос сайта на WordPress с одного домена на другой
- 2024-11-08 » OSPanel 6: быстрый старт
- 2024-11-08 » Как установить PhpMyAdmin в Open Server Panel
- 2024-09-30 » Как быстро запустить Laravel на Windows
- 2024-09-25 » Next.js
- 2024-09-05 » OpenAI рассказал, как запретить ChatGPT использовать содержимое сайта для обучения
- 2024-08-28 » Чек-лист: как увеличить конверсию интернет-магазина на примере спортпита
- 2024-08-01 » WebSocket
- 2024-07-26 » Интеграция с Яндекс Еда
- 2024-07-26 » Интеграция с Эквайринг
- 2024-07-26 » Интеграция с СДЕК
- 2024-07-26 » Интеграция с Битрикс-24
- 2024-07-26 » Интеграция с Travelline
- 2024-07-26 » Интеграция с Iiko
- 2024-07-26 » Интеграция с Delivery Club
- 2024-07-26 » Интеграция с CRM
- 2024-07-26 » Интеграция с 1C-Бухгалтерия
- 2024-07-24 » Что такое сторителлинг: техники и примеры
- 2024-07-17 » Ошибка 404: что это такое и как ее использовать для бизнеса
- 2024-07-03 » Размещайте прайс-листы на FarPost.ru и продавайте товары быстро и выгодно
- 2024-07-01 » Профилирование кода в PHP
- 2024-06-28 » Изучаем ABC/XYZ-анализ: что это такое и какие решения с помощью него принимают
- 2024-06-17 » Зачем вам знать потребности клиента
- 2024-06-11 » Что нового в работе Яндекс Метрики: полный обзор обновления
- 2024-06-11 » Поведенческие факторы ранжирования в Яндексе
- 2024-06-11 » Скорость загрузки сайта: почему это важно и как влияет на ранжирование
Всегда храни верность своему начальнику - следующий, может быть еще хуже... |
Мы создаем сайты, которые работают! Профессионально обслуживаем и продвигаем их , а также по всей России и ближнему зарубежью с 2006 года!
Как мы работаем
Заявка
Позвоните или оставьте заявку на сайте.
Консультация
Обсуждаем что именно Вам нужно и помогаем определить как это лучше сделать!
Договор
Заключаем договор на оказание услуг, в котором прописаны условия и обязанности обеих сторон.
Выполнение работ
Непосредственно оказание требующихся услуг и работ по вашему заданию.
Поддержка
Сдача выполненых работ, последующие корректировки и поддержка при необходимости.