Encodera

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.

Оглавление

Анимация при прокрутке

Обычная анимация идёт по часам: две секунды — и конец. Но её ход можно привязать не ко времени, а к прокрутке: прокрутили страницу наполовину — анимация прошла наполовину, вернулись назад — она отмоталась обратно. Раньше такое писали на JavaScript, теперь хватает одного свойства CSS.

Шкала прокрутки: scroll()

Свойство animation-timeline задаёт шкалу — то, что двигает анимацию. Функция scroll() связывает её с прокруткой ближайшего прокручиваемого блока. Сделаем индикатор чтения — полосу, которая растёт по мере прокрутки текста:

<style>
  @keyframes grow {
    from { scale: 0 1; }
    to { scale: 1 1; }
  }
  .reader { height: 180px; overflow-y: auto; border: 1px solid silver; }
  .progress {
    position: sticky;
    top: 0;
    height: 6px;
    background: tomato;
    transform-origin: left;
    animation: grow linear;
    animation-timeline: scroll();
  }
  .reader p { margin: 12px; }
</style>
 
<div class="reader">
  <div class="progress"></div>
  <p>Прокручивайте этот текст — красная полоса сверху показывает, сколько уже прочитано.</p>
  <p>Эспрессо готовят под давлением: горячая вода за полминуты проходит через плотно спрессованный молотый кофе.</p>
  <p>Капучино — это эспрессо, молоко и густая молочная пена примерно в равных долях.</p>
  <p>В латте молока больше, а пена тонкая, поэтому вкус мягче.</p>
  <p>Флэт уайт делают на двойном эспрессо с тонким слоем бархатистого молока.</p>
  <p>Вы дочитали до конца — полоса заполнилась целиком.</p>
</div>

Длительность не указана: при шкале прокрутки её роль играет путь от начала до конца прокрутки. На своей странице ставьте полосе position: fixed — тогда scroll() найдёт прокрутку всей страницы.

Шкала видимости: view()

Функция view() следит не за всей прокруткой, а за тем, как сам элемент проходит через видимую область. Так делают появление блоков при прокрутке:

<style>
  @keyframes reveal {
    from { opacity: 0; translate: -60px 0; }
    to { opacity: 1; translate: 0 0; }
  }
  .list { height: 200px; overflow-y: auto; padding: 8px; border: 1px solid silver; }
  .item {
    margin-bottom: 12px;
    padding: 24px 12px;
    background: lavender;
    animation: reveal linear both;
    animation-timeline: view();
    animation-range: entry;
  }
</style>
 
<div class="list">
  <div class="item">Капучино</div>
  <div class="item">Латте</div>
  <div class="item">Раф</div>
  <div class="item">Флэт уайт</div>
  <div class="item">Эспрессо</div>
  <div class="item">Американо</div>
</div>

Каждая карточка выезжает слева, пока появляется снизу. Прокрутите назад — анимация отыграет обратно. both в animation оставляет первый кадр до начала и последний после конца.

Отрезок: animation-range

animation-range выбирает, на каком отрезке пути элемента идёт анимация. В примере выше entry — пока карточка въезжает в область видимости:

ОтрезокКогда идёт анимация
coverвсё время, пока элемент хоть немного виден (по умолчанию)
containпока элемент виден целиком
entryпока элемент въезжает в область видимости
exitпока элемент уезжает из неё

К отрезку можно добавить проценты: entry 20% cover 50% — от 20% въезда до середины пути.

Поддержка и запасной вариант

Анимации при прокрутке работают в Chrome и Edge с 2023 года, позже их добавил Safari; актуальную поддержку, особенно в Firefox, смотрите на caniuse.com. Если элементы появляются из прозрачности, в старом браузере они могут остаться невидимыми. Поэтому такие стили оборачивают в @supports:

@supports (animation-timeline: view()) {
  .item {
    animation: reveal linear both;
    animation-timeline: view();
    animation-range: entry;
  }
}

Не забывайте и о prefers-reduced-motion из урока «Анимации»: людям, которые отключили движение, достаточно просто показать содержимое.

Частая ошибка

animation-timeline перед animation. Сокращённое свойство animation сбрасывает все свои составляющие, в том числе шкалу, обратно на время. Анимация длительностью 0 заканчивается мгновенно: полоса сразу заполнена целиком и не двигается:

<style>
  @keyframes grow { from { scale: 0 1; } to { scale: 1 1; } }
  .reader { height: 120px; overflow-y: auto; border: 1px solid silver; }
  .bar { position: sticky; top: 0; height: 6px; background: crimson; transform-origin: left;
    animation-timeline: scroll();
    animation: grow linear; }
  .reader p { margin: 12px; height: 200px; }
</style>
 
<div class="reader">
  <div class="bar"></div>
  <p>Прокручивайте — полоса не меняется.</p>
</div>

Сначала пишите animation, а animation-timeline и animation-range — после него.

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.