Слайдеры изображений, также известные как карусели картинок, очень распространены. Есть множество вариантов обычного CSS-слайдера, в котором изображения смещаются слева направо (или наоборот). Можно использовать JavaScript-библиотеки для создания красивых слайдеров со сложной анимацией. Но здесь я подойду к созданию карусели иначе.
В серии статей я расскажу вам, как создать необычные и красивые слайдеры исключительно в CSS. Если вы устали от одинаковых слайдеров, вам это понравится! Продолжение — к старту нашего курса по Fullstack-разработке на Python.
Я расскажу, как создать «круговой вращающийся слайдер с изображениями»:
Неплохо выглядит, правда?
HTML-разметка
Если вы прочитали мои статьи о необычном оформлении изображений или о гридах CSS и пользовательских фигурах, то знаете, что я всегда стараюсь работать с как можно меньшим количеством HTML-разметки. Всегда стараюсь решить задачу с помощью CSS прежде, чем заполнить свой код множеством <div>
-ов и других элементов.
Здесь я использую схожий подход: мой код — всего лишь список изображений в контейнере.
К примеру, давайте пропишем четыре изображения:
<div class="gallery">
<img src="" alt="">
<img src="" alt="">
<img src="" alt="">
<img src="" alt="">
</div>
Вот и весь HTML! А теперь займёмся интересным. Но сначала я бы хотел разобраться с логикой работы слайдера.
Как работает слайдер?
Посмотрите это видео, где я убрал свойство overflow: hidden
из CSS для лучшего понимания того, как двигаются изображения. Как будто четыре изображения размещены на круге, который вращается против часовой стрелки.
Обратите внимание на синий круг с радиусом R
, который проходит через центр всех изображений. Значение P
понадобится позже для создания анимации. R
равен 0.707 * S
(не буду рассказывать вам о правилах геометрии, из которых выводится это уравнение).
Пишем CSS!
Я использую CSS грид, чтобы расположить все изображения в одной области одно над другим:
.gallery {
--s: 280px; /* отвечает за размер */
display: grid;
width: var(--s);
aspect-ratio: 1;
padding: calc(var(--s) / 20); /* позже я объясню, зачем это нужно */
border-radius: 50%;
}
.gallery > img {
grid-area: 1 / 1;
width: 100%;
height: 100%;
object-fit: cover;
border-radius: inherit;
}
Пока что ничего сложного. Но загвоздка в анимации.
Речь шла о вращении большого круга, но в действительности необходимо поворачивать каждое изображение: так возникает иллюзия вращения большой окружности. Определим анимацию, m
, и применим её к изображениям:
.gallery > img {
/* всё как раньше */
animation: m 8s infinite linear;
transform-origin: 50% 120.7%;
}
@keyframes m {
100% { transform: rotate(-360deg); }
}
Посмотрите на выделенную строчку со свойством transform-origin
, его значение — center
(или 50% 50%
), что заставляет картинку вращаться вокруг её центра. Но нам это не нужно. Я хочу, чтобы изображение вращалось вокруг центра большого круга, где находятся изображения, а значит, transform-origin
нужно присвоить новое значение.
Поскольку Р
равен 0.707 * S
, можно сказать, что Р
равен 70.7%
размера изображения. Здесь показано, как получается значение 120.7%
:
Запустим анимацию и посмотрим, что произойдёт:
Вставка Pen на Хабр работает избирательно, поэтому здесь и далее иногда вместо них ссылки.
Знаю, знаю, это совсем не то, что нужно. Но на самом деле мы очень близки к этому. Может показаться, что здесь всего одно изображение, но не забывайте, что изображения наложены друг на друга. Они вращаются одновременно, и видно только верхнее. Чтобы убрать наложение, нужно прописать задержку для анимации каждой картинки.
.gallery > img:nth-child(2) { animation-delay: -2s; } /* -1 * 8с / 4 */
.gallery > img:nth-child(3) { animation-delay: -4s; } /* -2 * 8с / 4 */
.gallery > img:nth-child(4) { animation-delay: -6s; } /* -3 * 8с / 4 */
Уже лучше!
Если убрать свойство overflow
контейнера, вы уже увидите готовый слайдер. Но давайте немного изменим анимацию, чтобы каждое изображение, прежде чем смениться другим, появлялось в поле зрения на короткое время.
Для этого обновим ключевые кадры анимации:
@keyframes m {
0%, 3% { transform: rotate(0); }
22%, 27% { transform: rotate(-90deg); }
47%, 52% { transform: rotate(-180deg); }
72%, 77% { transform: rotate(-270deg); }
98%, 100% { transform: rotate(-360deg); }
}
Через каждые 90 градусов (90deg
) (360deg/4
, где 4
— это число изображений) добавим небольшую паузу. Каждая картинка будет в поле видимости на 5%
от общей продолжительности анимации, а затем сменится следующей (27–22%, 52–47%
и т. д.). Для красоты я добавлю в animation-timing-function
функцию cubic-bezier()
.
Слайдер выглядит великолепно! Ну, почти. Последний штрих — яркая круглая граница, которая вращается вокруг картинок. Можно использовать псевдоэлемент на контейнере .gallery
:
.gallery {
padding: calc(var(--s) / 20); /* здесь нужен внутренний отступ */
position: relative;
}
.gallery::after {
content: "";
position: absolute;
inset: 0;
padding: inherit; /* наследует тот же внутренний отступ */
border-radius: 50%;
background: repeating-conic-gradient(#789048 0 30deg, #DFBA69 0 60deg);
mask:
linear-gradient(#fff 0 0) content-box,
linear-gradient(#fff 0 0);
mask-composite: exclude;
}
.gallery::after,
.gallery >img {
animation: m 8s infinite cubic-bezier(.5, -0.2, .5, 1.2);
}
Я создал круг с повторяющимся коническим градиентом на фоне при помощи маскирования, которое показывает только заполненную область.
Вот и классный круговой слайдер!
Добавим больше изображений
Четыре картинки — неплохо, но что, если сделать слайдер с любым количеством изображений? В конце концов, именно возможности работать с N
картинок мы и хотим.
Нам потребуется SASS, чтобы сделать код более универсальным. Для начала определим переменную количества изображений ($n
). Эта переменная будет использоваться вместо жёстко заданного числа картинок (4
).
Начнём с задержек:
.gallery > img:nth-child(2) { animation-delay: -2s; } /* -1 * 8с / 4 */
.gallery > img:nth-child(3) { animation-delay: -4s; } /* -2 * 8с / 4 */
.gallery > img:nth-child(4) { animation-delay: -6s; } /* -3 * 8с / 4 */
Формула для задержек — (1-$i)*продолжительность (duration)/$n
, что даёт такой цикл SASS:
@for $i from 2 to ($n + 1) {
.gallery > img:nth-child(#{$i}) {
animation-delay: calc(#{(1-$i) / $n} * 8s);
}
}
Продолжительность можно прописать как переменную. Но займёмся анимацией:
@keyframes m {
0%, 3% { transform: rotate(0); }
22%, 27% { transform: rotate(-90deg); }
47%, 52% { transform: rotate(-180deg); }
72%, 77% { transform: rotate(-270deg); }
98%, 100% {transform: rotate(-360deg); }
}
Упростим код, чтобы лучше понимать логику:
@keyframes m {
0% { transform: rotate(0); }
25% { transform: rotate(-90deg); }
50% { transform: rotate(-180deg); }
75% { transform: rotate(-270deg); }
100% { transform: rotate(-360deg); }
}
Шаг между каждым состоянием равен 25%
— 100%/4
. Добавим угол минус 90 градусов (-90deg
) — 360 градусов/4 (-360deg/4
). Таким образом, цикл можно переписать:
@keyframes m {
0% { transform: rotate(0); }
@for $i from 1 to $n {
#{($i / $n) * 100}% { transform: rotate(#{($i / $n) * -360}deg); }
}
100% { transform: rotate(-360deg); }
}
Каждое изображение занимает 5%
времени анимации, поэтому заменяем этот код:
#{($i / $n) * 100}%
…на этот:
#{($i / $n) * 100 — 2}%, #{($i / $n) * 100 + 3}%
Стоит отметить, что 5%
в этом примере — произвольное значение. Его также можно заменить на переменную, чтобы управлять временем видимости каждой картинки. Для простоты сейчас я не буду этого делать, но вы можете попытаться осуществить эту замену в качестве домашнего задания. Не забудьте поделиться итогом работы в комментариях!
@keyframes m {
0%,3% { transform: rotate(0); }
@for $i from 1 to $n {
#{($i / $n) * 100 - 2}%, #{($i / $n) * 100 + 3}% { transform: rotate(#{($i / $n) * -360}deg); }
}
98%,100% { transform: rotate(-360deg); }
}
И, наконец, обновим transform-origin
. Здесь потребуется знание геометрии. Формула не зависит от количества изображений. Изображения (маленькие круги) расположены внутри большого круга. Нужно вычислить радиус, R
.
Значение R
можно найти так:
R = S / (2 * sin(180 градусов / N))
В процентах эта формула выглядит так:
R = 100% / (2 * sin(180 градусов / N)) = 50% / sin(180 градусов / N)
…а значит, вот значение transform-origin
:
transform-origin: 50% (50% / math.sin(180deg / $n) + 50%);
Готово! Теперь наш слайдер работает с любым количеством картинок!
Закинем туда 9 картинок:
Можно добавить любое любое количество картинок и обновить переменную $n
, чтобы она стала равна количеству изображений.
Заключение
При помощи трансформаций CSS и геометрических формул мы создали красивый круговой слайдер с минимумом кода. Самое классное в этом слайдере — то, что для эффекта бесконечной анимации изображения копировать не нужно, ведь после полного вращения круга слайдер возвращается к первой картинке!
А мы научим вас аккуратно работать с данными, чтобы вы прокачали карьеру и стали востребованным IT-специалистом. Скидка 45% по промокоду HABR.
Data Science и Machine Learning
- Профессия Data Scientist
- Профессия Data Analyst
- Курс «Математика для Data Science»
- Курс «Математика и Machine Learning для Data Science»
- Курс по Data Engineering
- Курс «Machine Learning и Deep Learning»
- Курс по Machine Learning
Python, веб-разработка
- Профессия Fullstack-разработчик на Python
- Курс «Python для веб-разработки»
- Профессия Frontend-разработчик
- Профессия Веб-разработчик
Мобильная разработка
Java и C#
- Профессия Java-разработчик
- Профессия QA-инженер на JAVA
- Профессия C#-разработчик
- Профессия Разработчик игр на Unity
От основ — в глубину
А также