Эта статья не о дизайне эффективных каруселей, а об эффективном создании стиля. Другими словами, речь пойдет не о UI-дизайне, а о конструкциях CSS – смене элементов карусели, их позиционировании и размерах.
Зависимость от JavaScript с точки зрения взаимодействия, а не стиля
«Эффективная карусель» — это карусель, которая не зависит от JavaScript с точки зрения:
• отзывчивости
• содержания любого количества элементов
• отображения любого количества элементов
Вызов
Существует множество способов отображения элементов в карусели типа «side-by-side» (в ряд), однако некоторые из этих способов лучше других.
Используем float
Карусель на сайте disneystore.com демонстрирует два основных ограничения такого стиля:
• контейнер должен иметь ширину большую или равную сумме ширины дочерних элементов (их наружной рамки), чтобы отображать все элементы в ряд (чтобы избежать их сползания).
• ширину элементов невозможно указать в процентном соотношении, так как размер блока (рамки, которая используется в качестве основы для расчета этой ширины) привязан к количеству его дочерних элементов, а не к видимой области карусели.
Чтобы лучше понять проблему, посмотрите на следующий пример:
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
</ul>
ul {
border: 5px solid deeppink;
overflow: hidden; /* должен содержать поплавки*/
}
li {
border: 1px solid #fff;
background: #3CB371;
width: 100px;
height: 100px;
float: left;
}
Вышеуказанный список включает пять элементов – все оформленные с помощью элемента float. Сам по себе список создан с помощью overflow:hidden (чтобы создать контекст форматирования блока), поэтому список выравнивает поплавки вместо того, чтобы схлопнуться.
Здесь проблема заключается в том, что элементы сползают, если их контейнер имеет недостаточную ширину, чтобы вмещать все эти элементы в ряд, как показано ниже:
ul {
overflow: hidden; /* должен содержать поплавки */
width: 450px;
}
li {
float: left;
}
Поэтому это решение требует установки точной ширины контейнера, которая не даст этому контейнеру быть отзывчивым (без необходимости JavaScript).
Используем position:absolute
В каруселях как на сайте on.aol.com все элементы удалены с потока, поэтому каждый из них зависит от отдельного значения смещения, отображаясь сразу после предыдущего своего собрата.
Посмотрите на еще один пример:
ul {
height: 100px; /* предотвращает нарушение контейнера */
}
li {
position: absolute;
}
Так как все рамки имеют абсолютное позиционирование, они удаляются с потока и размещаются на одних и тех же координатах x/y; последняя рамка показывает верх стопки.
В результате, авторам нужно сделать 3 вещи:
• оформить контейнер с помощью элемента height, чтобы предотвратить смещение вверх последующих элементов (за рамки).
• оформить каждую рамку со смещением влево (left ), чтобы отобразить элементы в ряд.
• оформить контейнер с помощью элемента position:relative, чтобы превратить его в блок, содержащий дочерние элементы.
Одинаковые рамки создаются следующим образом:
ul {
height: 100px; /* предотвращает нарушение контейнера */
position: relative; /* делает этот контейнер блоком, содержащим вложенные рамки AP */
}
li {
position: absolute;
}
li:nth-child(2) {
left: 100px; /* ширина + левая/правая граница первой рамки */
}
li:nth-child(3) {
left: 200px; /* ширина + левая/правая граница предыдущих рамок */
}
li:nth-child(4) {
left: 300px; /* ширина + левая/правая граница предыдущих рамок */
}
li:nth-child(5) {
left: 400px; /* ширина + левая/правая граница предыдущих рамок */
}
Именно так выполнена карусель на сайте aol.com, и все смещения там указаны в пикселях. Но интересно то, что ширина (width) контейнера задана таким же образом, как и в нашем примере с float. Контейнер оформлен таким образом, чтобы иметь ту же ширину, что и суммарная ширина всех его дочерних элементов – даже несмотря на использование position:absolute он позволяет авторам использовать более простой подход, который улучшает характеристики блоков контейнера.
В отличие от конструкций float, ширина (width) контейнера не играет роли в позиционировании вложенных рамок. Это означает, что можно использовать процентное соотношение для отображения элементов в полную ширину (100%) или в качестве части их контейнера (блока, в котором они содержатся); например, оформление каждой рамки с соотношением 50% покажет 2 элемента в ряд, как показано ниже:
ul {
height: 100px; /* предотвращает нарушение контейнера */
position: relative; /* делает этот контейнер блоком, содержащим вложенные рамки AP */
width: 30%;
}
li {
position: absolute;
width: 50%;
}
li:nth-child(2) {
left: 50%; /* ширина + левая/правая граница первой рамки */
}
li:nth-child(3) {
left: 100%; /* ширина + левая/правая граница предыдущих рамок */
}
li:nth-child(4) {
left: 150%; /* ширина + левая/правая граница предыдущих рамок */
}
li:nth-child(5) {
left: 200%; /* ширина + левая/правая граница предыдущих рамок */
}
ul {
height: 100px; /* предотвращает нарушение контейнера */
position: relative; /* делает этот контейнер блоком, содержащим вложенные рамки AP */
width: 30%;
}
li {
position: absolute;
width: 50%;
}
li:nth-child(2) {
left: 50%; /* ширина + левая/правая граница первой рамки */
}
li:nth-child(3) {
left: 100%; /* ширина + левая/правая граница предыдущих рамок */
}
li:nth-child(4) {
left: 150%; /* ширина + левая/правая граница предыдущих рамок */
}
li:nth-child(5) {
left: 200%; /* ширина + левая/правая граница предыдущих рамок */
}
Ширина (width) вышеуказанного контейнера установлена на значении 30%, все остальные значения (смещения влево (left) и ширина (width) рамок) также указаны в виде процентных соотношений.
ul {
height: 100px; /* предотвращает нарушение контейнера */
position: relative; /* делает этот контейнер блоком, содержащим вложенные рамки AP */
width: 50%;
}
li {
position: absolute;
width: 25%;
}
li:nth-child(2) {
left: 25%; /* ширина + левая/правая граница первой рамки */
}
li:nth-child(3) {
left: 50%; /* ширина + левая/правая граница предыдущих рамок */
}
li:nth-child(4) {
left: 75%; /* ширина + левая/правая граница предыдущих рамок */
}
li:nth-child(5) {
left: 100%; /* ширина + левая/правая граница предыдущих рамок */
}
В данном случае ширина контейнера составляет 50%, а ширина каждой рамки — 25%, что позволяет отображать четыре рамки в ряд внутри контейнера.
Это решение определенно лучше, чем использование float, но недостатком тут является то, что удаление все элементов с потока требует оформления контейнера с указанием высоты (height), чтобы предотвратить отображение последующих исходных элементов за всеми рамками.
Решение
Грубо говоря, это решение требует убедиться, что блок, содержащий элементы, соответствует видимой области карусели, и что ничего не выпадает из потока – тут высоту карусели задает контент.
Используем inline-block
Начнем с основ:
<ul>
<li>1</li><!--
--><li>2</li><!--
--><li>3</li><!--
--><li>4</li><!--
--><li>5</li>
</ul>
li {
display: inline-block;
}
С таким простым оформлением и макетом, как и в конструкции с float, вложенные рамки сползают, если им недостаточно места:
ul {
width: 450px;
}
li {
display: inline-block;
}
Волшебная пилюля
Мы можем избежать сползания рамок с помощью white-space:nowrap:
ul {
width: 450px;
white-space: nowrap;
}
li {
display: inline-block;
}
Теперь у нас есть решение, которое не требует установки точного значения высоты (height) контейнера и оформления вложенных рамок со смещением или настройки их ширины (width) с точными значениями. И в качестве бонуса – это решение подходит для RTL:
<ul class="example example-10" dir="rtl">
<li>1</li><!--
--><li>2</li><!--
--><li>3</li><!--
--><li>4</li><!--
--><li>5</li>
</ul>
Реализация карусели
Смещение за счет margin
Использования левого поля на первом элементе достаточно, чтобы сдвинуть все поля за раз (влево или вправо):
ul {
width: 100px;
white-space: nowrap;
}
li {
display: inline-block;
width: 100%;
}
li:first-child {
margin-left: -100%; /* для старых IE может потребоваться этот класс*/
}
Оформление контейнера с overflow:hidden скроет элементы, которые находятся за пределами контейнера:
ul {
width: 100px;
white-space: nowrap;
overflow: hidden;
}
li {
display: inline-block;
width: 100%;
}
li:first-child {
margin-left: -100%;
}
Единственно, что стоит не забывать – это сбрасывать описание nowrap, если этот стиль унаследован.
Смещение за счет translate, position, и пр.
Перемещение контейнера, а не его первого дочернего элемента, требует использования дополнительной обертки (обратите внимание, что все стили переносятся со списка на эту надстройку):
<div>
<ul>
<li>1</li><!--
--><li>2</li><!--
--><li>3</li><!--
--><li>4</li><!--
--><li>5</li>
</ul>
</div>
div {
white-space: nowrap;
width: 50%;
overflow: hidden;
border: 5px solid deeppink;
}
ul {
border: none;
*position: relative; /* возврат к oldIE */
*left: -100%; /* возврат к oldIE */
transform: translateX(-100%);
}
/*фолбэк для IE8 */
@media screen {
.example-13 {
position: relative;
left: -100%;
}
}
li {
white-space: normal; /* сброс */
display: inline-block;
width: 50%;
}
Карусель с выглядывающим элементом
Это решение мы можем легко выполнить следующим образом:
div {
padding-right: 12%; /* создает разрыв справа от списка, открывая часть следующего поля */
}
img {
width: 100%; /* та же ширина, что и у контейнера */
vertical-align: bottom;
}
Обратите внимание, что именно изображения задают высоту карусели, и что все поля отзывчивы и правильно позиционированы – без использования JavaScript.
Чистая CSS-карусель с эффектом плавного появления
Логика, в которой не требуются знания математики!
<div class="carousel">
<input role="presentation" name="carousel" type="radio" value="1" checked />
<input role="presentation" name="carousel" type="radio" value="2" />
<input role="presentation" name="carousel" type="radio" value="3" />
<input role="presentation" name="carousel" type="radio" value="4" />
<input role="presentation" name="carousel" type="radio" value="5" />
<ul class="carousel-list">
<li><img src="..." alt="Mask #1"></li><!--
--><li><img src="..." alt="Mask #2"></li><!--
--><li><img src="..." alt="Mask #3"></li><!--
--><li><img src="..." alt="Mask #4"></li><!--
--><li><img src="..." alt="Mask #4"></li>
</ul>
</div>
<div class="carousel">
<input role="presentation" name="carousel" type="radio" value="1" checked />
<input role="presentation" name="carousel" type="radio" value="2" />
<input role="presentation" name="carousel" type="radio" value="3" />
<input role="presentation" name="carousel" type="radio" value="4" />
<input role="presentation" name="carousel" type="radio" value="5" />
<ul class="carousel-list">
<li><img src="..." alt="Mask #1"></li><!--
--><li><img src="..." alt="Mask #2"></li><!--
--><li><img src="..." alt="Mask #3"></li><!--
--><li><img src="..." alt="Mask #4"></li><!--
--><li><img src="..." alt="Mask #4"></li>
</ul>
</div>
.carousel {
width: 200px;
padding: 5px;
overflow: hidden;
border: 1px solid #ccc;
border-radius: 3px;
text-align: center; /* центрирует кнопки-переключатели */
}
.carousel-list {
white-space: nowrap;
padding: 0;
margin: 0;
transition: transform .3s;
}
.carousel-list li {
white-space: normal; /* сброс */
display: inline-block;
width: 100%;
}
.carousel-list img {
width: 100%; /* подгоняется под контейнер */
vertical-align: bottom; /* удаляет пустое пространство под изображением */
}
/**
* список перемещается кнопками-переключателями
*/
input:nth-child(1):checked ~ ul {
transform: translateX(0);
}
input:nth-child(2):checked ~ ul {
transform: translateX(-100%);
}
input:nth-child(3):checked ~ ul {
transform: translateX(-200%);
}
input:nth-child(4):checked ~ ul {
transform: translateX(-300%);
}
input:nth-child(5):checked ~ ul {
transform: translateX(-400%);
}
/**
* эффект плавного появления
*/
.carousel-list li {
opacity: .1;
transition: all .4s;
transform: scale(.1);
}
input:nth-child(1):checked ~ ul li:nth-child(1),
input:nth-child(2):checked ~ ul li:nth-child(2),
input:nth-child(3):checked ~ ul li:nth-child(3),
input:nth-child(4):checked ~ ul li:nth-child(4),
input:nth-child(5):checked ~ ul li:nth-child(5) {
opacity: 1;
transform: scale(1);
}
Вы можете отредактировать значение width ниже, чтобы проверить «отзывчивость» этого решения.
.carousel{width:200px}
Более сложная карусель
Как отобразить два элемента, разделенных разрывом, передвигая один элемент карусели за раз.
.carousel {
display: inline-block;
width: 200px;
padding-right: 190px; /* ширина контейнера минут 10px внутреннего поля элементов списка */
overflow: hidden;
border: 1px solid #ccc;
border-radius: 3px;
text-align: center; /* центрирует кнопки-переключатели */
}
.carousel-list {
white-space: nowrap;
padding: 0;
margin: 0;
border: none;
transition: transform .3s;
}
.carousel-list li {
white-space: normal; /* сброс */
display: inline-block;
width: 100%;
box-sizing: border-box;
padding-right: 10px; /* создает разрыв между изображениями */
}
.carousel-list img {
width: 100%; /* подгоняется под контейнер */
vertical-align: bottom;
}
.carousel input {
margin-left: -3px;
}
.carousel input:nth-child(1):checked ~ ul {
transform: translateX(0);
}
.carousel input:nth-child(2):checked ~ ul {
transform: translateX(-100%);
}
.carousel input:nth-child(3):checked ~ ul {
transform: translateX(-200%);
}
.carousel input:nth-child(4):checked ~ ul {
transform: translateX(-300%);
}
.carousel input:nth-child(5):checked ~ ul {
transform: translateX(-400%);
}
.carousel input:nth-child(6):checked ~ ul {
transform: translateX(-500%);
}
.carousel input:nth-child(7):checked ~ ul {
transform: translateX(-600%);
}
→ Принимайте оплату от компаний через Интернет. Без сайта, ИП и ООО.
→ Приём платежей от компаний для Вашего сайта. С документооборотом и обменом оригиналами.
→ Автоматизация продаж и обслуживание сделок с юр.лицами. Без посредника в расчетах.
Автор: Irina_Ua