Как стать автором
Обновить
148.08
Skillfactory
Онлайн-школа IT-профессий

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

Время на прочтение6 мин
Количество просмотров11K
Автор оригинала: Temani Afif

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




Теги:
Хабы:
Всего голосов 10: ↑10 и ↓0+10
Комментарии2

Публикации

Информация

Сайт
www.skillfactory.ru
Дата регистрации
Дата основания
Численность
501–1 000 человек
Местоположение
Россия
Представитель
Skillfactory School