CSS Бесконечные 3D-слайдеры PlatoBlockchain Data Intelligence. Вертикальный поиск. Ай.

Бесконечные 3D-слайдеры CSS

В этой серии, мы делаем слайдеры изображений, используя только HTML и CSS. Идея состоит в том, что мы можем использовать одну и ту же разметку, но разные CSS для получения совершенно разных результатов, независимо от того, сколько изображений мы добавляем. Мы начали с круглого ползунка, который вращается бесконечно, что-то вроде прядильщика, который удерживает изображения. Затем мы сделали один, который пролистывает стопку фотографий.

На этот раз мы погружаемся в третье измерение. Поначалу это может показаться сложным, но большая часть рассматриваемого нами кода — это именно то, что мы использовали в первых двух статьях этой серии, с некоторыми изменениями. Итак, если вы только сейчас знакомитесь с этой серией, я бы посоветовал проверить другие, чтобы узнать контекст концепций, которые мы здесь используем.

Серия слайдеров CSS

Вот к чему мы стремимся:

На первый взгляд кажется, что у нас есть вращающийся куб с четырьмя изображениями. Но на самом деле мы имеем дело всего с шестью изображениями. Вот слайдер с другого ракурса:

Теперь, когда у нас есть хорошее наглядное представление о том, как расположены изображения, давайте разберем код, чтобы увидеть, как мы туда доберемся.

Базовая настройка

Тот же HTML, что и остальные слайдеры, которые мы использовали для других слайдеров:

И снова мы используем CSS Grid для размещения изображений в стопку, одно поверх другого:

.gallery {
  display: grid;
}
.gallery > img {
  grid-area: 1 / 1;
  width: 160px;
  aspect-ratio: 1;
  object-fit: cover;
}

Анимация

Логика этого ползунка очень похожа на круговой слайдер из первой статьи. На самом деле, если вы снова посмотрите видео выше, вы увидите, что изображения расположены таким образом, что создается многоугольник. После полного поворота он возвращается к первому изображению.

Мы полагались на CSS transform-origin и animation-delay свойства для первого ползунка. Одна и та же анимация применяется ко всем элементам изображения, которые вращаются вокруг одной и той же точки. Затем, используя различные задержки, мы правильно размещаем все изображения по большому кругу.

Реализация будет немного отличаться для нашего 3D слайдера. С использованием transform-origin здесь не сработает, потому что мы работаем в 3D, поэтому будем использовать transform вместо того, чтобы правильно разместить все изображения, затем поверните контейнер.

Мы снова обращаемся к Sass, чтобы мы могли просмотреть количество изображений и применить наши преобразования:

@for $i from 1 to ($n + 1) {
  .gallery > img:nth-child(#{$i}) {
     transform: 
       rotate(#{360*($i - 1) / $n}deg) /* 1 */
       translateY(50% / math.tan(180deg / $n)) /* 2 */ 
       rotateX(90deg); /* 3 */
  }
}

Вам может быть интересно, почему мы сразу переходим к Sass. Мы начали с фиксированного количества изображений, используя ванильный CSS в других статьях, прежде чем обобщить код с помощью Sass для учета любого числа (N) изображений. Что ж, я думаю, теперь вы уловили идею, и мы можем отказаться от всей этой исследовательской работы, чтобы перейти к реальной реализации.

Ассоциация transform свойство принимает три значения, которые я проиллюстрировал здесь:

Бесконечные 3D-слайдеры CSS

Сначала мы поворачиваем все изображения друг над другом. Угол поворота зависит от количества изображений. За N изображений, мы имеем приращение, равное 360deg/N, Тогда мы translate все изображения на одинаковую величину таким образом, чтобы их центральные точки совпадали по бокам.

Показана стопка изображений, расположенных плоско по кругу с красной линией, проходящей через центральную точку изображений.
Бесконечные 3D-слайдеры CSS

Есть какая-то скучная геометрия, которая помогает объяснить, как все это работает, но расстояние равно 50%/tan(180deg/N). Мы имели дело с аналогичным уравнением при создании кругового ползунка ( transform-origin: 50% 50%/sin(180deg/N) ).

Наконец, мы вращаем изображения вокруг оси X на 90deg чтобы получить расположение, которое мы хотим. Вот видео, которое иллюстрирует, что делает последнее вращение:

Теперь все, что нам нужно сделать, это повернуть весь контейнер, чтобы создать наш бесконечный слайдер.

.gallery {
  transform-style: preserve-3d;
  --_t: perspective(280px) rotateX(-90deg);
  animation: r 12s cubic-bezier(.5, -0.2, .5, 1.2) infinite;
}
@keyframes r {
  0%, 3% {transform: var(--_t) rotate(0deg); }
  @for $i from 1 to $n {
    #{($i/$n)*100 - 2}%, 
    #{($i/$n)*100 + 3}% {
      transform: var(--_t) rotate(#{($i / $n) * -360}deg);
    }  
  }
  98%, 100% { transform: var(--_t) rotate(-360deg); }
}

Этот код может быть трудным для понимания, поэтому давайте сделаем шаг назад и вернемся к анимации, которую мы сделали для круглого ползунка. Вот что мы написали в той первой статье:

.gallery {
  animation: m 12s cubic-bezier(.5, -0.2, .5, 1.2) infinite;
}
@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); }
}

Ключевые кадры почти идентичны. У нас те же процентные значения, тот же цикл и то же вращение.

Почему оба одинаковы? Потому что логика у них одинаковая. В обоих случаях изображения расположены вокруг круглой формы, и нам нужно повернуть все это, чтобы показать каждое изображение. Вот как я смог скопировать ключевые кадры из круглого ползунка и использовать тот же код для нашего 3D-ползунка. Единственная разница в том, что нам нужно повернуть контейнер на -90deg вдоль оси X, чтобы увидеть изображения, так как мы уже повернули их на 90deg на той же оси. Затем мы добавляем немного perspective чтобы получить эффект 3D.

Вот и все! Наш слайдер готов. Вот полная демонстрация снова. Все, что вам нужно сделать, это добавить столько изображений, сколько вы хотите, и обновить одну переменную, чтобы все заработало.

Вертикальный 3D слайдер

Поскольку мы играем в 3D-пространстве, почему бы не сделать вертикальную версию предыдущего слайдера? Последний вращается по оси Z, но мы также можем двигаться по оси X, если захотим.

Если вы сравните код для обеих версий этого ползунка, вы не сразу заметите разницу, потому что это всего один символ! я заменил rotate() rotateX() внутри ключевых кадров и изображения transform, Это оно!

Следует отметить, что rotate() эквивалентна rotateZ(), поэтому, изменив ось с Z в X мы трансформируем ползунок из горизонтальной версии в вертикальную.

Ползунок куба

Мы не можем говорить о 3D в CSS без речь о кубиках. И да, это значит, что мы собираемся сделать другую версию слайдера.

Идея этой версии ползунка состоит в том, чтобы создать реальную форму куба с изображениями и повернуть все это вокруг другой оси. Поскольку это куб, мы имеем дело с шестью гранями. Мы будем использовать шесть изображений, по одному для каждой грани куба. Итак, никакого Sass, но вернемся к ванильному CSS.

Эта анимация немного ошеломляет, верно? С чего вообще начать?

У нас есть шесть граней, поэтому нам нужно выполнить как минимум шесть поворотов, чтобы каждое изображение получило поворот. Ну, на самом деле нам нужно пять поворотов — последний возвращает нас к первому лицу изображения. Если вы возьмете кубик Рубика или какой-нибудь другой предмет в форме куба, например игральные кости, и повернете его рукой, у вас будет хорошее представление о том, что мы делаем.

.gallery {
  --s: 250px; /* the size */

  transform-style: preserve-3d;
  --_p: perspective(calc(2.5*var(--s)));
  animation: r 9s infinite cubic-bezier(.5, -0.5, .5, 1.5);
}

@keyframes r {
  0%, 3%   { transform: var(--_p); }
  14%, 19% { transform: var(--_p) rotateX(90deg); }
  31%, 36% { transform: var(--_p) rotateX(90deg) rotateZ(90deg); }
  47%, 52% { transform: var(--_p) rotateX(90deg) rotateZ(90deg) rotateY(-90deg); }
  64%, 69% { transform: var(--_p) rotateX(90deg) rotateZ(90deg) rotateY(-90deg) rotateX(90deg); }
  81%, 86% { transform: var(--_p) rotateX(90deg) rotateZ(90deg) rotateY(-90deg) rotateX(90deg) rotateZ(90deg); }
  97%, 100%{ transform: var(--_p) rotateX(90deg) rotateZ(90deg) rotateY(-90deg) rotateX(90deg) rotateZ(90deg) rotateY(-90deg); }
}

Ассоциация transform свойство начинается с нулевого поворота, и в каждом состоянии мы добавляем новый поворот на определенной оси, пока не достигнем шести поворотов. Затем мы возвращаемся к первому изображению.

Давайте не будем забывать о размещении наших изображений. Каждый из них применяется к грани куба с помощью transform:

.gallery img {
  grid-area: 1 / 1;
  width: var(--s);
  aspect-ratio: 1;
  object-fit: cover;
  transform: var(--_t,) translateZ(calc(var(--s) / 2));
}
.gallery img:nth-child(2) { --_t: rotateX(-90deg); }
.gallery img:nth-child(3) { --_t: rotateY( 90deg) rotate(-90deg); }
.gallery img:nth-child(4) { --_t: rotateX(180deg) rotate( 90deg); }
.gallery img:nth-child(5) { --_t: rotateX( 90deg) rotate( 90deg); }
.gallery img:nth-child(6) { --_t: rotateY(-90deg); }

Вы, вероятно, думаете, что за значениями, которые я использую, стоит странная сложная логика, верно? Ну нет. Все, что я сделал, это открыл DevTools и поиграл с разными значениями поворота для каждого изображения, пока не получил правильное решение. Это может звучать глупо, но, эй, это работает — тем более, что у нас есть фиксированное количество изображений, и мы не ищем что-то, что поддерживает N изображениями.

На самом деле, забудьте значения, которые я использую, и попробуйте выполнить размещение самостоятельно в качестве упражнения. Начните со всех изображений, сложенных друг над другом, откройте DevTools и вперед! Вероятно, вы получите другой код, и это совершенно нормально. Можно по-разному расположить изображения.

в чем прикол с запятой внутри var()? Это опечатка?

Это не опечатка, не удаляйте! Если вы удалите его, вы заметите, что он влияет на размещение первого изображения. Вы можете видеть, что в моем коде я определил --_t для всех изображений, кроме первого, потому что мне нужен только перевод для него. Эта запятая заставляет переменную возвращаться к нулевому значению. Без запятой у нас не будет запасного варианта, и все значение будет недействительным.

от спецификация:

Примечание: то есть, var(--a,) является допустимой функцией, указывающей, что если --a пользовательское свойство недействительно или отсутствует, var()` следует заменить ничем.

Ползунок случайного куба

Немного случайности может быть хорошим улучшением для такого рода анимации. Таким образом, вместо того, чтобы вращать куб в последовательном порядке, мы можем, так сказать, бросать кости и позволять кубу катиться, как он будет.

Круто, да? Не знаю, как вам, а мне эта версия больше нравится! Это более интересно, и переходы приятно смотреть. И угадайте, что? Вы можете поиграть со значениями, чтобы создать свой собственный слайдер случайного куба!

Логика на самом деле совсем не случайная — она просто так кажется. Вы определяете transform на каждом ключевом кадре, что позволяет показать одно лицо и… ну вот и все! Вы можете выбрать любой заказ.

@keyframes r {
  0%, 3%   { transform: var(--_p) rotate3d( 0, 0, 0,  0deg); }
  14%,19%  { transform: var(--_p) rotate3d(-1, 1, 0,180deg); }
  31%,36%  { transform: var(--_p) rotate3d( 0,-1, 0, 90deg); }
  47%,52%  { transform: var(--_p) rotate3d( 1, 0, 0, 90deg); }
  64%,69%  { transform: var(--_p) rotate3d( 1, 0, 0,-90deg); }
  81%,86%  { transform: var(--_p) rotate3d( 0, 1, 0, 90deg); }
  97%,100% { transform: var(--_p) rotate3d( 0, 0, 0,  0deg); }
}

я использую rotate3d() на этот раз, но я все еще полагаюсь на DevTools, чтобы найти значения, которые кажутся мне «правильными». Не пытайтесь найти взаимосвязь между ключевыми кадрами, потому что ее просто нет. Я определяю отдельные преобразования, а затем наблюдаю за «случайным» результатом. Убедитесь, что первое изображение является первым и последним кадрами соответственно, и покажите другое изображение на каждом из других кадров.

Вы не обязаны использовать rotate3d() трансформируйся, как я. Вы также можете объединить различные вращения, как мы сделали в предыдущем примере. Поиграйте и посмотрите, что вы можете придумать! Буду ждать, когда вы поделитесь со мной своей версией в комментариях!

Подведение итогов

Надеюсь, вам понравилась эта небольшая серия. Мы создали несколько забавных (и забавных) ползунков, попутно изучая все виды концепций CSS — от размещения сетки и порядка наложения до задержек и преобразований анимации. Нам даже пришлось немного поиграть с Sass, чтобы перебрать массив элементов.

И мы сделали все это с одним и тем же HTML для каждого созданного слайдера. Как это круто? CSS чертовски мощен и способен на многое без помощи JavaScript.

Отметка времени:

Больше от CSS хитрости