Бесконечный слайдер CSS, пролистывающий изображения Polaroid. Анализ данных PlatoBlockchain. Вертикальный поиск. Ай.

Бесконечный слайдер CSS, перелистывающий изображения Polaroid

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

Круто, да? Пока не смотрите на код, потому что еще многое предстоит разгадать. Присоединяйся ко мне, а?

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

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

Большая часть HTML и CSS для этого слайдера аналогична круглому слайдеру, который мы сделали в прошлый раз. На самом деле мы используем точно такую ​​же разметку:

И это базовый CSS, который устанавливает наш родительский .gallery контейнер в виде сетки, в которой все изображения расположены друг над другом:

.gallery  {
  display: grid;
  width: 220px; /* controls the size */
}
.gallery > img {
  grid-area: 1 / 1;
  width: 100%;
  aspect-ratio: 1;
  object-fit: cover;
  border: 10px solid #f2f2f2;
  box-shadow: 0 0 4px #0007;
}

Пока ничего сложного. Даже для изображений в стиле Polaroid все, что я использую, это некоторые border и box-shadow. Возможно, вы сможете сделать это лучше, так что не стесняйтесь экспериментировать с этими декоративными стилями! Мы собираемся сосредоточить основное внимание на анимации, которая является самой сложной частью.

Что за хитрость?

Логика этого ползунка зависит от порядка расположения изображений — так что да, мы собираемся поиграть с z-index. Все изображения начинаются с одного и того же z-index ценность (2), что логически сделает последнее изображение на вершине стека.

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

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

Теперь представьте, что тот же трюк применяется ко всем изображениям. Вот шаблон, если мы используем :nth-child() псевдоселектор для различения изображений:

  • Сдвигаем последнее изображение (N). Следующее изображение видно (N - 1).
  • Сдвигаем следующее изображение (N - 1). Следующее изображение видно (N - 2)
  • Сдвигаем следующее изображение (N - 2). Следующее изображение видно (N - 3)
  • (Мы продолжаем тот же процесс, пока не дойдем до первого изображения)
  • Сдвигаем первое изображение (1). Последняя картинка(N) снова виден.

Это наш бесконечный слайдер!

Анализ анимации

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

Бесконечный слайдер CSS, перелистывающий изображения Polaroid

Наша анимация разделена на три части: «скользить вправо», «скользить влево» и «не двигаться». Мы можем легко определить задержку между каждым изображением. Если учесть, что первое изображение начинается с 0s, а длительность равна 6s, то второй начнется в -2s и третий в -4s.

.gallery > img:nth-child(2) { animation-delay: -2s; } /* -1 * 6s / 3 */
.gallery > img:nth-child(3) { animation-delay: -4s; } /* -2 * 6s / 3 */

Мы также можем видеть, что часть «не двигаться» занимает две трети всей анимации (2*100%/3), в то время как части «сдвиг вправо» и «сдвиг влево» вместе занимают одну треть — так что каждая из них равна 100%/6 общей анимации.

Мы можем написать ключевые кадры анимации следующим образом:

@keyframes slide {
  0%     { transform: translateX(0%); }
  16.67% { transform: translateX(120%); }
  33.34% { transform: translateX(0%); }
  100%   { transform: translateX(0%); } 
}

То, что 120% является произвольным значением. Мне нужно было что-то большее, чем 100%. Изображения должны скользить вправо от остальных изображений. Для этого ему нужно пройти не менее 100% от его размера. Вот почему я пошел 120% — чтобы получить дополнительное пространство.

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

@keyframes slide {
  0%     { transform: translateX(0%);   z-index: 2; }
  16.66% { transform: translateX(120%); z-index: 2; }
  16.67% { transform: translateX(120%); z-index: 1; } /* we update the z-order here */
  33.34% { transform: translateX(0%);   z-index: 1; }
  100%   { transform: translateX(0% );  z-index: 1; }  
}

Вместо определения одного состояния в 16.67% (100%/6) точки на временной шкале, мы определяем два состояния в почти идентичных точках (16.66% и 16.67%) где z-index значение уменьшается, прежде чем мы сдвинем изображение обратно на колоду.

Вот что происходит, когда мы собираем все это вместе:

Хммм, раздвижная часть вроде бы работает нормально, но порядок укладки перепутан! Анимация начинается хорошо, так как верхнее изображение перемещается назад… но последующие изображения не следуют этому примеру. Если вы заметили, второе изображение в последовательности возвращается на вершину стека до того, как следующее изображение мигает поверх него.

Нам необходимо внимательно следить за z-index изменения. Изначально все изображения z-index: 2. Это означает, что порядок укладки должен идти…

Our eyes 👀 --> 3rd (2) | 2nd (2) | 1st (2)

Сдвигаем третье изображение и обновляем его z-index чтобы получить этот заказ:

Our eyes 👀 --> 2nd (2) | 1st (2) | 3rd (1)

Проделываем то же самое со вторым:

Our eyes 👀 --> 1st (2) | 3rd (1) | 2nd (1)

…и первое:

Our eyes 👀 --> 3rd (1) | 2nd (1) | 1st (1)

Мы так делаем и вроде все нормально. Но на самом деле это не так! Когда первое изображение переместится на задний план, третье изображение начнет новую итерацию, то есть вернется в исходное положение. z-index: 2:

Our eyes 👀 --> 3rd (2) | 2nd (1) | 1st (1)

Итак, на самом деле у нас никогда не было всех изображений на z-index: 2 вообще! Когда изображения не движутся (т. е. часть анимации «не двигается»), z-index is 1. Если мы сдвинем третье изображение и обновим его z-index значение от 2 в 1, он останется на вершине! Когда все изображения одинаковы z-index, последнее в исходном порядке — в данном случае наше третье изображение — находится на вершине стека. Скольжение третьего изображения приводит к следующему:

Our eyes 👀 --> 3rd (1) | 2nd (1) | 1st (1)

Третье изображение по-прежнему находится наверху, и сразу после него мы перемещаем второе изображение наверх, когда его анимация перезапускается в z-index: 2:

Our eyes 👀 --> 2nd (2) | 3rd (1) | 1st (1)

После того, как мы сдвинем его, мы получим:

Our eyes 👀 --> 3rd (1) | 2nd (1) | 1st (1)

Тогда первое изображение будет прыгать вверху:

Our eyes 👀 --> 1st(2) | 3rd (1) | 2nd (1)

Хорошо, я потерялся. Тогда вся логика неверна?

Я знаю, это сбивает с толку. Но наша логика не совсем неверна. Нам нужно только немного подправить анимацию, чтобы все работало так, как мы хотим. Хитрость заключается в том, чтобы правильно сбросить z-index.

Возьмем ситуацию, когда третье изображение находится сверху:

Our eyes 👀 -->  3rd (2) | 2nd (1) | 1st (1)

Мы видели, что сдвинув третье изображение и изменив его z-index держит на высоте. Что нам нужно сделать, так это обновить z-index второго изображения. Итак, прежде чем убрать третье изображение из колоды, мы обновим z-index второго изображения на 2.

Другими словами, мы сбрасываем z-index второго изображения до окончания анимации.

Диаграммы частей анимации с индикаторами увеличения или уменьшения z-индекса.
Бесконечный слайдер CSS, перелистывающий изображения Polaroid

Зеленый плюс символизирует увеличение z-index в 2, а красный минус соответствует z-index: 1. Второе изображение начинается с z-index: 2, затем мы обновляем его до 1 когда он соскальзывает с палубы. Но прежде чем первое изображение соскользнет с колоды, мы меняем z-index второго изображения обратно в 2. Это гарантирует, что оба изображения имеют одинаковый z-index, но все же третий останется сверху, потому что он появляется позже в DOM. Но после третьего слайда изображения и его z-index обновляется, он перемещается вниз.

Это две трети анимации, поэтому давайте соответствующим образом обновим наши ключевые кадры:

@keyframes slide {
  0%     { transform: translateX(0%);   z-index: 2; }
  16.66% { transform: translateX(120%); z-index: 2; }
  16.67% { transform: translateX(120%); z-index: 1; } /* we update the z-order here */
  33.34% { transform: translateX(0%);   z-index: 1; }
  66.33% { transform: translateX(0%);   z-index: 1; }
  66.34% { transform: translateX(0%);   z-index: 2; } /* and also here */
  100%   { transform: translateX(0%);   z-index: 2; }  
}

Чуть лучше, но все равно нет вполне там. Есть еще одна проблема…

О нет, это никогда не закончится!

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

Когда первое изображение находится сверху, мы имеем следующую ситуацию:

Our eyes 👀 -->  1st (2) | 3rd (1) | 2nd (1)

Учитывая предыдущую настройку, которую мы сделали, третье изображение будет прыгать сверху до того, как первое изображение скользит. Это происходит только в этой ситуации, потому что следующее изображение, которое перемещается после первого изображения, является последний изображение, которое имеет более высокий порядок в DOM. Остальные изображения в порядке, потому что у нас есть N, то N - 1, то идем от 3 в 2и 2 в 1… но тогда мы идем от 1 в N.

Чтобы избежать этого, мы будем использовать следующие ключевые кадры для последнего изображения:

@keyframes slide-last {
  0%     { transform: translateX(0%);   z-index: 2;}
  16.66% { transform: translateX(120%); z-index: 2; }
  16.67% { transform: translateX(120%); z-index: 1; } /* we update the z-order here */
  33.34% { transform: translateX(0%);   z-index: 1; }
  83.33% { transform: translateX(0%);   z-index: 1; }
  83.34% { transform: translateX(0%);   z-index: 2; } /* and also here */
  100%   { transform: translateX(0%);   z-index: 2; }
}

Мы сбрасываем z-index значение 5/6 через анимацию (вместо двух третей), когда первое изображение выходит из стопки. Так что никаких прыжков мы не видим!

ТАДА! Наш бесконечный слайдер теперь идеален! Вот наш окончательный код во всей красе:

.gallery > img {
  animation: slide 6s infinite;
}
.gallery > img:last-child {
  animation-name: slide-last;
}
.gallery > img:nth-child(2) { animation-delay: -2s; } 
.gallery > img:nth-child(3) { animation-delay: -4s; }

@keyframes slide {
  0% { transform: translateX(0%); z-index: 2; }
  16.66% { transform: translateX(120%); z-index: 2; }
  16.67% { transform: translateX(120%); z-index: 1; } 
  33.34% { transform: translateX(0%); z-index: 1; }
  66.33% { transform: translateX(0%); z-index: 1; }
  66.34% { transform: translateX(0%); z-index: 2; } 
  100% { transform: translateX(0%); z-index: 2; }
}
@keyframes slide-last {
  0% { transform: translateX(0%); z-index: 2; }
  16.66% { transform: translateX(120%); z-index: 2; }
  16.67% { transform: translateX(120%); z-index: 1; }
  33.34% { transform: translateX(0%); z-index: 1; }
  83.33% { transform: translateX(0%); z-index: 1; }
  83.34% { transform: translateX(0%); z-index: 2; } 
  100%  { transform: translateX(0%); z-index: 2; }
}

Поддержка любого количества изображений

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

.gallery > img {
  z-index: 2;
  animation: 
    slide 6s infinite,
    z-order 6s infinite steps(1);
}
.gallery > img:last-child {
  animation-name: slide, z-order-last;
}
.gallery > img:nth-child(2) { animation-delay: -2s; } 
.gallery > img:nth-child(3) { animation-delay: -4s; }

@keyframes slide {
  16.67% { transform: translateX(120%); }
  33.33% { transform: translateX(0%); }
}
@keyframes z-order {
  16.67%,
  33.33% { z-index: 1; }
  66.33% { z-index: 2; }
}
@keyframes z-order-last {
  16.67%,
  33.33% { z-index: 1; }
  83.33% { z-index: 2; }
}

Теперь намного меньше кода! Мы делаем одну анимацию для скользящей части и другую для z-index обновления. Обратите внимание, что мы используем steps(1) на z-index анимация. Это потому, что я хочу резко изменить z-index значение, в отличие от анимации скольжения, где нам нужно плавное движение.

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

@for $i from 2 to ($n + 1) {
  .gallery > img:nth-child(#{$i}) {
    animation-delay: calc(#{(1 - $i)/$n}*6s);
  }
}

Это означает, что мы переходим с ванильного CSS на Sass. Далее нам нужно представить, как масштабируется временная шкала с N картинки. Не будем забывать, что анимация происходит в три этапа:

Отображение трех частей анимации в виде ряда линий со стрелками.
Бесконечный слайдер CSS, перелистывающий изображения Polaroid

После «сдвиньте вправо» и «сдвиньте влево» изображение должно оставаться на месте, пока остальные изображения не пройдут через последовательность. Таким образом, часть «не двигаться» должна занимать столько же времени, сколько (N - 1) как «сдвинуть вправо» и «сдвинуть влево». И за одну итерацию N изображения будут скользить. Таким образом, «сдвиньте вправо» и «сдвиньте влево» оба берут 100%/N общей временной шкалы анимации. Изображение соскальзывает с стопки в (100%/N)/2 и скользит обратно в 100%/N .

Мы можем изменить это:

@keyframes slide {
  16.67% { transform: translateX(120%); }
  33.33% { transform: translateX(0%); }
}

…к этому:

@keyframes slide {
  #{50/$n}%  { transform: translateX(120%); }
  #{100/$n}% { transform: translateX(0%); }
}

Если мы заменим N 3, мы получаем 16.67% и 33.33% когда есть 3 изображения в стеке. Это та же логика с порядком наложения, где у нас будет это:

@keyframes z-order {
  #{50/$n}%,
  #{100/$n}% { z-index: 1; }
  66.33% { z-index: 2; }
}

Нам еще нужно обновить 66.33% точка. Это должно быть место, где изображение сбрасывает свое z-index до конца анимации. В то же время следующее изображение начинает скользить. Так как скользящая часть занимает 100%/N, сброс должен произойти в 100% - 100%/N:

@keyframes z-order {
  #{50/$n}%,
  #{100/$n}% { z-index: 1; }
  #{100 - 100/$n}% { z-index: 2; }
}

Но для нашего z-order-last анимация для работы, это должно произойти немного позже в последовательности. Помните исправление, которое мы сделали для последнего изображения? Сброс z-index значение должно происходить, когда первое изображение выходит из стопки, а не когда оно начинает скользить. Мы можем использовать те же рассуждения здесь в наших ключевых кадрах:

@keyframes z-order-last {
  #{50/$n}%,
  #{100/$n}% { z-index: 1; }
  #{100 - 50/$n}% { z-index: 2; }
}

Мы сделали! Вот что мы получаем при использовании пяти изображений:

Мы можем добавить немного поворота, чтобы сделать вещи немного красивее:

Все, что я сделал, это добавил rotate(var(--r)) до transform имущество. Внутри петли, --r определяется случайным углом:

@for $i from 1 to ($n + 1) {
  .gallery > img:nth-child(#{$i}) {
    --r: #{(-20 + random(40))*1deg}; /* a random angle between -20deg and 20deg */
  }
}

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

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

Все это z-index работа была большим балансированием, верно? Если до этого упражнения вы не знали, как работает порядок укладки, то теперь у вас, вероятно, гораздо лучшее представление! Если вы нашли некоторые объяснения трудными для понимания, я настоятельно рекомендую вам еще раз прочитать статью и наметить вещи карандашом и бумагой. Попробуйте проиллюстрировать каждый шаг анимации, используя разное количество изображений, чтобы лучше понять трюк.

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

В следующий раз будем делать 3D слайдеры. Следите за обновлениями!

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

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