Эта статья не о дизайне эффективных каруселей, а об эффективном создании стиля. Другими словами, речь пойдет не о UI-дизайне, а о конструкциях CSS – смене элементов карусели, их позиционировании и размерах.
Зависимость от JavaScript с точки зрения взаимодействия, а не стиля
«Эффективная карусель» — это карусель, которая не зависит от JavaScript с точки зрения:
• отзывчивости
• содержания любого количества элементов
• отображения любого количества элементов
Существует множество способов отображения элементов в карусели типа «side-by-side» (в ряд), однако некоторые из этих способов лучше других.
Используем float
Карусель на сайте disneystore.com демонстрирует два основных ограничения такого стиля:
• контейнер должен иметь ширину большую или равную сумме ширины дочерних элементов (их наружной рамки), чтобы отображать все элементы в ряд (чтобы избежать их сползания).
• ширину элементов невозможно указать в процентном соотношении, так как размер блока (рамки, которая используется в качестве основы для расчета этой ширины) привязан к количеству его дочерних элементов, а не к видимой области карусели.
Чтобы лучше понять проблему, посмотрите на следующий пример:
Вышеуказанный список включает пять элементов – все оформленные с помощью элемента float. Сам по себе список создан с помощью overflow:hidden (чтобы создать контекст форматирования блока), поэтому список выравнивает поплавки вместо того, чтобы схлопнуться.
Здесь проблема заключается в том, что элементы сползают, если их контейнер имеет недостаточную ширину, чтобы вмещать все эти элементы в ряд, как показано ниже:
Поэтому это решение требует установки точной ширины контейнера, которая не даст этому контейнеру быть отзывчивым (без необходимости JavaScript).
Используем position:absolute
В каруселях как на сайте on.aol.com все элементы удалены с потока, поэтому каждый из них зависит от отдельного значения смещения, отображаясь сразу после предыдущего своего собрата.
Посмотрите на еще один пример:
Так как все рамки имеют абсолютное позиционирование, они удаляются с потока и размещаются на одних и тех же координатах x/y; последняя рамка показывает верх стопки.
В результате, авторам нужно сделать 3 вещи:
• оформить контейнер с помощью элемента height, чтобы предотвратить смещение вверх последующих элементов (за рамки).
• оформить каждую рамку со смещением влево (left ), чтобы отобразить элементы в ряд.
• оформить контейнер с помощью элемента position:relative, чтобы превратить его в блок, содержащий дочерние элементы.
Одинаковые рамки создаются следующим образом:
Именно так выполнена карусель на сайте aol.com, и все смещения там указаны в пикселях. Но интересно то, что ширина (width) контейнера задана таким же образом, как и в нашем примере с float. Контейнер оформлен таким образом, чтобы иметь ту же ширину, что и суммарная ширина всех его дочерних элементов – даже несмотря на использование position:absolute он позволяет авторам использовать более простой подход, который улучшает характеристики блоков контейнера.
В отличие от конструкций float, ширина (width) контейнера не играет роли в позиционировании вложенных рамок. Это означает, что можно использовать процентное соотношение для отображения элементов в полную ширину (100%) или в качестве части их контейнера (блока, в котором они содержатся); например, оформление каждой рамки с соотношением 50% покажет 2 элемента в ряд, как показано ниже:
Ширина (width) вышеуказанного контейнера установлена на значении 30%, все остальные значения (смещения влево (left) и ширина (width) рамок) также указаны в виде процентных соотношений.
В данном случае ширина контейнера составляет 50%, а ширина каждой рамки — 25%, что позволяет отображать четыре рамки в ряд внутри контейнера.
Это решение определенно лучше, чем использование float, но недостатком тут является то, что удаление все элементов с потока требует оформления контейнера с указанием высоты (height), чтобы предотвратить отображение последующих исходных элементов за всеми рамками.
Грубо говоря, это решение требует убедиться, что блок, содержащий элементы, соответствует видимой области карусели, и что ничего не выпадает из потока – тут высоту карусели задает контент.
Используем inline-block
Начнем с основ:
С таким простым оформлением и макетом, как и в конструкции с float, вложенные рамки сползают, если им недостаточно места:
Волшебная пилюля
Мы можем избежать сползания рамок с помощью white-space:nowrap:
Теперь у нас есть решение, которое не требует установки точного значения высоты (height) контейнера и оформления вложенных рамок со смещением или настройки их ширины (width) с точными значениями. И в качестве бонуса – это решение подходит для RTL:
Смещение за счет margin
Использования левого поля на первом элементе достаточно, чтобы сдвинуть все поля за раз (влево или вправо):
Оформление контейнера с overflow:hidden скроет элементы, которые находятся за пределами контейнера:
Единственно, что стоит не забывать – это сбрасывать описание nowrap, если этот стиль унаследован.
Смещение за счет translate, position, и пр.
Перемещение контейнера, а не его первого дочернего элемента, требует использования дополнительной обертки (обратите внимание, что все стили переносятся со списка на эту надстройку):
Карусель с выглядывающим элементом
Это решение мы можем легко выполнить следующим образом:
Обратите внимание, что именно изображения задают высоту карусели, и что все поля отзывчивы и правильно позиционированы – без использования JavaScript.
Чистая CSS-карусель с эффектом плавного появления
Логика, в которой не требуются знания математики!
Вы можете отредактировать значение width ниже, чтобы проверить «отзывчивость» этого решения.
Как отобразить два элемента, разделенных разрывом, передвигая один элемент карусели за раз.
Зависимость от 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\0 {
.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%);
}
Полезные решения Paysto для читателей Хабра:
→ Получите оплату банковской картой прямо сейчас. Без сайта, ИП и ООО.
→ Принимайте оплату от компаний через Интернет. Без сайта, ИП и ООО.
→ Приём платежей от компаний для Вашего сайта. С документооборотом и обменом оригиналами.
→ Автоматизация продаж и обслуживание сделок с юр.лицами. Без посредника в расчетах.
→ Принимайте оплату от компаний через Интернет. Без сайта, ИП и ООО.
→ Приём платежей от компаний для Вашего сайта. С документооборотом и обменом оригиналами.
→ Автоматизация продаж и обслуживание сделок с юр.лицами. Без посредника в расчетах.