Как сделать бесконечный круговой CSS-слайдер

от автора

Слайдеры изображений, также известные как карусели картинок, очень распространены. Есть множество вариантов обычного 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 для лучшего понимания того, как двигаются изображения. Как будто четыре изображения размещены на круге, который вращается против часовой стрелки.

Обратите внимание на синий круг с радиусом Р, который проходит через центр всех изображений. Значение P понадобится позже для создания анимации. Р равен 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.


ссылка на оригинал статьи https://habr.com/ru/company/skillfactory/blog/710196/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *