Фоновая анимация - PullRequest
       55

Фоновая анимация

4 голосов
/ 16 апреля 2019

Я пытаюсь создать загрузчик контента, но у меня проблема с производительностью фоновой анимации. Это гладко, когда на экране всего несколько элементов, но резко падает fps при увеличении количества заглушек до 20-30. Теперь я знаю, что анимация свойства background-position - плохая идея, и для этого лучше использовать преобразования. но как я могу это сделать? Я хотел бы сохранить бесшовную анимацию. Градиент должен быть относительно экрана, а не контейнера.

Вот код:

const cardsRoot = document.getElementById('cards')
const addButton = document.getElementById('add')
const card = document.getElementsByClassName('card')[0]
let cardsCount = 1

addButton.addEventListener('click', () => {
  cardsRoot.innerHTML = ''
  cardsCount++
  for (let i = 0; i < cardsCount; i++) {
    let cardClone = card.cloneNode(true)
    cardsRoot.appendChild(cardClone)
  }
})
body {
  padding: 40px;
}

.card {
  display: flex;
  margin-top: 20px;
}

.stub {
  width: 300px;
  height: 12px;
  margin: 8px;
  border-radius: 8px;
  background: linear-gradient(to right, rgba(0, 0, 0, 0.04), rgba(0, 0, 0, 0.1) 10%, rgba(0, 0, 0, 0.04) 20%) fixed;
  animation: stub 1.3s linear infinite;
  margin-bottom: 8px;
}

.circle {
  width: 40px;
  height: 40px;
  margin-right: 12px;
  border-radius: 20px;
}

@keyframes stub {
  0% { background-position: 0vw; }
  100% { background-position: 100vw; }
}
<button id="add">
  ADD CARD
</button>

<div id="cards">
  <div class="card">
    <div>
      <div class="stub circle"></div>
    </div>
    <div>
      <div class="stub"></div>
      <div class="stub"></div>
      <div class="stub"></div>
    </div> 
  </div>
</div>

И демоверсия: https://jsfiddle.net/3da4uzm2/57/

1 Ответ

4 голосов
/ 16 апреля 2019

Вы можете заменить анимацию, используя псевдоэлемент, где вы применяете перевод.Хитрость заключается в том, чтобы заменить фиксированный элемент на background-attachment:fixed, затем вы увеличиваете элемент в два раза по сравнению с экраном и переводите его слева направо.

const cardsRoot = document.getElementById('cards')
const addButton = document.getElementById('add')
const card = document.getElementsByClassName('card')[0]
let cardsCount = 1

addButton.addEventListener('click', () => {
  cardsRoot.innerHTML = ''
  cardsCount++
  for (let i = 0; i < cardsCount; i++) {
    let cardClone = card.cloneNode(true)
    cardsRoot.appendChild(cardClone)
  }
})
body {
  padding: 40px;
}

.card {
  display: inline-flex;
  margin-top: 20px;
}

.stub {
  width: 150px;
  height: 12px;
  margin: 8px;
  border-radius: 8px;
  margin-bottom: 8px;
  position:relative;
  z-index:0;
  overflow:hidden;
}
.stub:before {
  content:"";
  position:fixed;
  z-index:-1;
  top:0;
  right:0;
  width:200vw;
  bottom:0;
  background: 
    linear-gradient(rgba(0, 0, 0, 0.04),rgba(0, 0, 0, 0.04)) left/50% 100%,
    linear-gradient(to right, rgba(0, 0, 0, 0.04), rgba(0, 0, 0, 0.1) 10%, rgba(0, 0, 0, 0.04) 20%) right/50% 100%;
  background-repeat:no-repeat;
  animation: stub 1.3s linear infinite;
  pointer-events:none;
}

.circle {
  width: 40px;
  height: 40px;
  margin-right: 12px;
  border-radius: 20px;
}

@keyframes stub {
  0% { transform:translate(0); }
  100% { transform:translate(50%); }
}
<button id="add">
  ADD CARD
</button>

<div id="cards">
  <div class="card">
    <div>
      <div class="stub circle"></div>
    </div>
    <div>
      <div class="stub"></div>
      <div class="stub"></div>
      <div class="stub"></div>
    </div> 
  </div>
  <div class="card">
    <div>
      <div class="stub circle"></div>
    </div>
    <div>
      <div class="stub"></div>
      <div class="stub"></div>
      <div class="stub"></div>
    </div> 
  </div>
</div>

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

body:before {
  content:"";
  position:fixed; /*relative to the screen*/
  z-index:-1;
  top:0;
  right:0;
  width:200vw; /*2x100vw*/
  bottom:0;
  background: 
    /*will cover the left area while sliding*/
    linear-gradient(red,red) left/50% 100%, /*the red should be green*/
    /*the main gradient*/
    linear-gradient(to right, green, blue 10%, green 20%) right/50% 100%;
  background-repeat:no-repeat;
  animation: stub 3s linear infinite;
}
@keyframes stub {
  0% { transform:translate(0); }
  100% { transform:translate(50%); } /*50% will be 200vw/2 = 100vw*/
}
...