Анимированный CSS фоновый узор, скользящий бесконечно

Я пытаюсь создать анимированный повторяющийся узор (диагональные полосы скользят горизонтально) в качестве заполнителя для блока загрузки (li в этом случае).

Как я могу сделать анимацию плавной / непрерывной, создавая иллюзию бесконечного скольжения рисунка?

  • Как рассчитать элемент width так что картина непрерывна? (полосы не должны выглядеть сломанными / прерванными).
  • Как сделать так, чтобы петля выглядела так, будто она не перезапускается, а скорее скользит бесконечно? (кадр 100% должен пройти к кадру 0% без сбоев)

Цель состоит в том, чтобы иметь класс, который я могу добавить в любой блок и который будет выглядеть как загрузка / обработка.

Примечание: нет JS; чистый CSS.

li {
  display: inline-block;
  width: calc(20px * 8); /* how to calculate this, relative to the width (of the pattern or the step), to achieve pattern continuity exactly?
    Of course without doing trying&error to know it should be 24.75px * 8.
  */
  height: 200px;
  background-color: blue;

  background-image: repeating-linear-gradient(-45deg, transparent, transparent 10px, black 10px, black 20px
  );
  animation: loading-slide 1s linear infinite;
  @keyframes loading-slide {
    from { background-position: 0% 0% }
    to { background-position: 100% 0% }
  }
}
<ul>
    <li>test
    <li>test
</ul>

1 ответ

Правильная формула должна быть (20px / cos(45deg)) * N, Тогда вы можете просто сделать background-size быть 200% 100% (вдвое больше, чем элемент), и вы можете легко анимировать слева направо:

li {
  display: inline-block;
  width: calc( (20px / 0.707) * 3); /*cos(45deg) = 0.707*/
  height: 50px;
  margin-bottom:10px;
  background-color: blue;
  background-image: repeating-linear-gradient(-45deg, transparent, transparent 10px, black 10px, black 20px);
  background-size: 200% 100%;
  background-color: blue;
  animation: loading-slide 3s linear infinite;
}

@keyframes loading-slide {
  from {
    background-position: left;
  }
  to {
    background-position: right;
  }
}

.alt li {
  width: calc( (20px / 0.707) * 6);
}
<ul>
  <li>test</li><li>test</li>
</ul>

<ul class="alt">
  <li>test</li><li>test</li>
</ul>

Вы можете рассмотреть любую степень и скорректировать формулу по мере необходимости. (20px / cos(90deg - |Xdeg|)) * N с X между -90deg а также 90deg

Пример с -60deg

li {
  display: inline-block;
  width: calc((20px / 0.866) * var(--n,3)); /*cos(30deg) = 0.866*/
  height: 50px;
  margin-bottom:10px;
  background-color: blue;
  background-image: repeating-linear-gradient(-60deg, transparent, transparent 10px, black 10px, black 20px);
  background-size: 200% 100%;
  background-color: blue;
  animation: loading-slide 3s linear infinite;
}

@keyframes loading-slide {
  from {
    background-position: left;
  }
  to {
    background-position: right;
  }
}

.alt li {
  --n:6;
}
<ul>
  <li>test</li><li>test</li>
</ul>

<ul class="alt">
  <li>test</li><li>test</li>
</ul>

Пример с 30deg

li {
  display: inline-block;
  width: calc((20px / 0.5) * var(--n,8)); /*cos(60deg) = 0.5*/
  height: 50px;
  margin-bottom:10px;
  background-color: blue;
  background-image: repeating-linear-gradient(30deg, transparent, transparent 10px, black 10px, black 20px);
  background-size: 200% 100%;
  background-color: blue;
  animation: loading-slide 3s linear infinite;
}

@keyframes loading-slide {
  from {
    background-position: left;
  }
  to {
    background-position: right;
  }
}

.alt li {
  --n:12;
}
<ul>
  <li>test</li><li>test</li>
</ul>

<ul class="alt">
  <li>test</li><li>test</li>
</ul>

Пример с 80deg

li {
  display: inline-block;
  width: calc((20px / 0.9848) * var(--n,8)); /*cos(10deg) = 0.9848*/
  height: 50px;
  margin-bottom:10px;
  background-color: blue;
  background-image: repeating-linear-gradient(80deg, transparent, transparent 10px, black 10px, black 20px);
  background-size: 200% 100%;
  background-color: blue;
  animation: loading-slide 3s linear infinite;
}

@keyframes loading-slide {
  from {
    background-position: left;
  }
  to {
    background-position: right;
  }
}

.alt li {
  --n:12;
}
<ul>
  <li>test</li><li>test</li>
</ul>

<ul class="alt">
  <li>test</li><li>test</li>
</ul>

Вы можете четко определить тривиальный случай, когда X=+/-90deg (вертикальные полосы), и мы будем иметь cos(0)=1 таким образом, формула будет 20px * N, Также когда X=0 (горизонтальные полосы) у нас будет cos(90deg) = 0 и любая ширина будет работать, так как нет вертикального шаблона (формула больше не определена)

li {
  display: inline-block;
  width: calc(20px * var(--n,8)); 
  height: 50px;
  margin-bottom:10px;
  background-color: blue;
  background-image: repeating-linear-gradient(90deg, transparent, transparent 10px, black 10px, black 20px);
  background-size: 200% 100%;
  background-color: blue;
  animation: loading-slide 3s linear infinite;
}

@keyframes loading-slide {
  from {
    background-position: left;
  }
  to {
    background-position: right;
  }
}

.alt li {
 background-image:repeating-linear-gradient(0deg, transparent, transparent 10px, black 10px, black 20px);
}
<ul>
  <li>test</li><li>test</li>
</ul>

<ul class="alt">
  <li>test</li><li>test</li>
</ul>

Как насчет ценности за пределами [-90deg,90deg] ?

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

Пример: 110deg такой же как -70deg

li {
  display: inline-block;
  width: calc((20px / 0.9396) * var(--n,8)); /*cos(20deg) = 0.9396*/
  height: 50px;
  margin-bottom:10px;
  background-color: blue;
  background-image: repeating-linear-gradient(110deg, transparent, transparent 10px, black 10px, black 20px);
  background-size: 200% 100%;
  background-color: blue;
  animation: loading-slide 3s linear infinite;
}
.alt li {
  --n:12;
  background-image: repeating-linear-gradient(-70deg, transparent, transparent 10px, black 10px, black 20px);
}

@keyframes loading-slide {
  from {
    background-position: left;
  }
  to {
    background-position: right;
  }
}
<ul>
  <li>test</li><li>test</li>
</ul>

<ul class="alt">
  <li>test</li><li>test</li>
</ul>

Пример: -150deg такой же как 30deg

li {
  display: inline-block;
  width: calc((20px / 0.5) * var(--n,4)); /*cos(60deg) = 0.5*/
  height: 50px;
  margin-bottom:10px;
  background-color: blue;
  background-image: repeating-linear-gradient(-150deg, transparent, transparent 10px, black 10px, black 20px);
  background-size: 200% 100%;
  background-color: blue;
  animation: loading-slide 3s linear infinite;
}
.alt li {
  --n:6;
  background-image: repeating-linear-gradient(30deg, transparent, transparent 10px, black 10px, black 20px);
}

@keyframes loading-slide {
  from {
    background-position: left;
  }
  to {
    background-position: right;
  }
}
<ul>
  <li>test</li><li>test</li>
</ul>

<ul class="alt">
  <li>test</li><li>test</li>
</ul>

в основном мы добавляем / удаляем 180deg пока мы не попадем внутрь [-90deg,90deg] чтобы иметь возможность применять формулу.


Проверьте этот ответ для более подробной информации о том, как background-size / background-position работает: /questions/28327775/ispolzovanie-protsentnyih-znachenij-s-background-position-na-linejnom-gradiente/28327778#28327778


Другой подход

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

li {
  display: inline-block;
  width: calc( 20px * 3); /* it's only 20px * N */
  height: 50px;
  margin-bottom:10px;
  background-color: blue;
  position:relative;
  z-index:0;
  overflow:hidden
}
li::before {
  content:"";
  position:absolute;
  top:0;
  bottom:0;
  left:0;
  width:400%;
  /*we keep 0deg in the gradient*/
  background-image: repeating-linear-gradient(90deg, transparent, transparent 10px, black 10px, black 20px);
  transform:skewX(30deg);
  transform-origin:bottom left;
  animation: loading-slide 4s linear infinite;
}

@keyframes loading-slide {
  to {
    transform: translateX(-50%) skewX(30deg);
  }
}

.alt li {
  width: calc( 20px * 6);
}
<ul>
  <li>test</li><li>test</li>
</ul>

<ul class="alt">
  <li>test</li><li>test</li>
</ul>

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

При таком подходе у вас также будет лучшая производительность, так как вы будете анимировать преобразование вместо background-size,

Больше примеров:

li {
  display: inline-block;
  width: calc( 20px * var(--n,3)); /* it's only 20px * N */
  height: 50px;
  margin-bottom:10px;
  background-color: blue;
  position:relative;
  z-index:0;
  overflow:hidden
}
li::before {
  content:"";
  position:absolute;
  top:0;
  bottom:0;
  left:-400%;
  right:-800%;
  /*we keep 0deg in the gradient*/
  background-image: repeating-linear-gradient(90deg, transparent, transparent 10px, black 10px, black 20px);
  transform:skewX(var(--d,30deg));
  animation: loading-slide 12s linear infinite;
}

@keyframes loading-slide {
  to {
    transform: translateX(-50%) skewX(var(--d,30deg));
  }
}
<ul>
  <li>test</li><li>test</li>
</ul>

<ul style="--n:6;--d:45deg">
  <li>test</li><li>test</li>
</ul>
<ul style="--n:8;--d:-70deg">
  <li>test</li><li>test</li>
</ul>
<ul style="--n:8;--d:30deg">
  <li>test</li><li>test</li>
</ul>

Другие вопросы по тегам