Застосування анімацій CSS

Анімації CSS роблять можливим анімування переходів від однієї конфігурації стилю CSS до іншої. Анімації складаються з двох деталей: стилю, що описує анімацію CSS, та набору ключових кадрів, котрі вказують на початковий і кінцевий стани стилю анімації, а також можливі проміжні точки.

Є три ключові переваги анімацій CSS над традиційними методиками анімацій, що працюють на сценаріях:

  1. Їх легко застосовувати для простих анімацій; їх можна створювати навіть без знання JavaScript.
  2. Такі анімації добре працюють навіть тоді, коли система перебуває під навантаженням середнього рівня. Прості анімації на JavaScript нерідко можуть мати погану швидкодію. Рушій візуалізації може застосовувати пропускання кадрів чи інші методики, аби підтримувати швидкодію настільки плавною, наскільки можливо.
  3. Передача браузерові контролю над послідовністю анімації дозволяє йому оптимізувати швидкодію й ефективність шляхом, наприклад, зниження частоти оновлення анімації у вкладках, котрі наразі приховані.

Налаштування анімації

Щоб створити послідовність анімації CSS, елемент, що анімується, оздоблюють властивістю animation чи її підвластивостями. Це дає змогу налаштовувати хронометраж, тривалість та інші подробиці того, як повинна перебігати анімація. Це не налаштовує фактичний вигляд анімації, він виконується за допомогою директиви @keyframes як це описано в розділі Визначення послідовності анімації за допомогою ключових кадрів нижче.

Підвластивості animation – такі:

animation-composition

Задає складену операцію для використання в тих випадках, коли на одну властивість одночасно впливають кілька анімацій. Ця властивість не є частиною властивості-скорочення animation.

animation-delay

Задає затримку між завантаженням елемента і стартом послідовності анімації, а також те, чи повинна анімація початися зразу зі свого старту, чи якоїсь позиції посередині.

animation-direction

Задає те, чи матиме перша ітерація прямий хід, чи зворотний, і чи будуть наступні ітерації мати почергову зміну ходу або будуть скидатися на початкову точку й повторюватися.

animation-duration

Задає тривалість, протягом якої анімація завершує один цикл.

animation-fill-mode

Задає те, як анімація застосовує стилі до своєї цілі, до і після її виконання.

animation-iteration-count

Задає те, скільки разів повинна повторитися анімація.

animation-name

Задає ім'я директиви @keyframes, котра описує ключові кадри анімації.

animation-play-state

Задає паузу чи відновлення анімації.

animation-timeline

Задає часову шкалу, що використовується для контролю прогресу анімації CSS.

animation-timing-function

Задає те, як анімація переходить між ключовими кадрами, шляхом встановлення кривих прискорення.

Визначення послідовності анімації за допомогою ключових кадрів

Після налаштування хронометражу анімації, треба визначити її вигляд. Це виконується шляхом створення одного чи більше ключових кадрів за допомогою директиви @keyframes. Кожний ключовий кадр описує те, як повинен візуалізуватися анімований елемент у певну мить протягом послідовності анімації.

Оскільки хронометраж анімації визначається стилем CSS, котрий налаштовує анімацію, ключові кадри використовують відсотки для вказівки на час, коли під час анімації вони спрацюють. 0% вказує на першу мить послідовності анімації, а 100% – на фінальний стан анімації. Через те, що ці дві миті такі важливі, вони мають особливі псевдоніми: from і to. Обидва – необов'язкові. Якщо from/0% чи to/100% – не задано, то браузер почне або завершить анімацію, використовуючи обчислені значення всіх атрибутів.

Можна, хоча не обов'язково, включити додаткові ключові кадри, котрі описують проміжні кроки між початком і кінцем анімації.

Застосування скорочення animation

Скорочення animation – корисне для економії простору. Як приклад, частина правил, що використовуються в цій статті:

p {
  animation-duration: 3s;
  animation-name: slidein;
  animation-iteration-count: infinite;
  animation-direction: alternate;
}

...можуть бути замінені шляхом використання скорочення animation.

p {
  animation: 3s infinite alternate slidein;
}

Аби дізнатися більше про послідовність, у котрій за допомогою скорочення animation можуть задаватися різні значення властивостей анімації, зверніться до довідкової сторінки animation.

Задання декількох значень властивостей анімації

Специфічні властивості анімації CSS можуть приймати декілька значень, розділених комами. Ця можливість може використовуватися, коли треба застосувати до одного правила декілька анімацій, встановивши для кожної з них різні тривалості, кількості ітерацій тощо. Погляньмо на кілька невеличких прикладів, котрі пояснюють різні перестановки.

У цьому першому прикладі є три тривалості й три кількості ітерацій. Тож кожна анімація отримує значення тривалості й кількості ітерацій на такій само позиції, що й ім'я анімації. Анімація fadeInOut отримує тривалість 2.5s й кількість ітерацій 2, а анімація bounce отримує тривалість 1s і кількість ітерацій 5.

animation-name: fadeInOut, moveLeft300px, bounce;
animation-duration: 2.5s, 5s, 1s;
animation-iteration-count: 2, 1, 5;

У цьому, другому прикладі, задаються три імені анімацій, але є лише одна тривалість й одна кількість ітерацій. У такому випадку всі три анімації отримують однакові тривалість і кількість анімацій.

animation-name: fadeInOut, moveLeft300px, bounce;
animation-duration: 3s;
animation-iteration-count: 1;

У цьому, третьому прикладі, задаються три анімації, але лише дві тривалості й дві кількості ітерацій. У таких випадках, коли в списку недостатньо значень, аби надати кожній анімації окреме, присвоєння значень йде від першого елемента до останнього в доступному списку, а потім продовжується від першого. Таким чином, fadeInOut отримує тривалість 2.5s, moveLeft300px отримує тривалість 5s, що є останнім значенням в списку значень тривалості. Присвоєння значень тривалості далі перестрибує на перше значення; bounce, таким чином, отримує тривалість 2.5s. Значення кількості ітерацій (та інші задані значення властивостей) присвоюються так само.

animation-name: fadeInOut, moveLeft300px, bounce;
animation-duration: 2.5s, 5s;
animation-iteration-count: 2, 1;

Якщо розбіжність числа анімацій та числа значень властивостей анімації – зворотна, скажімо, є п'ять значень animation-duration для трьох значень animation-name, то надлишкові чи невикористані значення властивості, в цьому випадку – два значення animation-duration, не застосовуються до жодної анімації й ігноруються.

Приклади

Примітка: Частина старих браузерів (до 2017 року) могли потребувати префіксів; живі приклади, котрі для перегляду треба клацнути, включають синтаксис із префіксом -webkit.

Текст, що ковзає вікном браузера

Цей зразок так оздоблює елемент <p>, що текст ковзає до і від правого краю вікна браузера.

Зверніть увагу, що анімації штибу цієї можуть призвести до того, що сторінка стане ширшою за вікно браузера. Аби цього уникнути цієї проблеми, слід помістити анімований елемент у контейнер, і цьому контейнеру задати overflow:hidden.

p {
  animation-duration: 3s;
  animation-name: slidein;
}

@keyframes slidein {
  from {
    margin-left: 100%;
    width: 300%;
  }

  to {
    margin-left: 0%;
    width: 100%;
  }
}

В цьому прикладі стиль елемента <p> задає, що виконання анімації від її початку до завершення повинно зайняти 3 секунди, за допомогою властивості animation-duration, і що ім'я директиви @keyframes, котра визначає ключові фрейми для послідовності анімації, – "slidein".

Якби ми хотіли якогось специфічного оздоблення елемента <p>, що з'явилось би у браузерах, котрі не підтримують анімації CSS, то включили б його і сюди; проте в цьому випадку не хочемо жодного оздоблення, крім самого ефекту анімації.

Ключові фрейми визначаються за допомогою директиви @keyframes. В цьому випадку є лишень два ключові фрейми. Перший трапляється на 0% (використовує псевдонім from). Тут ми задаємо лівий зовнішній відступ елемента 100% (тобто аж до правого краю контейнерного елемента), а ширину елемента 300% (чи втричі більшою за ширину контейнерного елемента). Це призводить до того, що перший фрейм анімації має заголовок на правому краю вікна браузера.

Другий (і фінальний) ключовий фрейм трапляється на 100% (використовує псевдонім to). Лівий зовнішній відступ – 0%, а ширина елемента – 100%. Це призводить до того, що заголовок закінчує свою анімацію на рівні лівого краю області вмісту.

<p>
  Коли лежиш в полі лицем до неба і вслухаєшся в многоголосу тишу полів, то
  помічаєш, що в ній щось є не земне, а небесне.
</p>

Примітка: Аби побачити анімацію – перезавантажте сторінку.

Додавання іще одного ключового фрейму

Додаймо до анімації попереднього прикладу іще один ключовий фрейм. Скажімо, хочеться, аби розмір шрифту збільшувався при русі справа наліво, а тоді зменшувався назад. Для цього достатньо додати такий ключовий фрейм:

75% {
  font-size: 300%;
  margin-left: 25%;
  width: 150%;
}

Повний код тепер має наступний вигляд:

p {
  animation-duration: 3s;
  animation-name: slidein;
}

@keyframes slidein {
  from {
    margin-left: 100%;
    width: 300%;
  }

  75% {
    font-size: 300%;
    margin-left: 25%;
    width: 150%;
  }

  to {
    margin-left: 0%;
    width: 100%;
  }
}
<p>
  Коли лежиш в полі лицем до неба і вслухаєшся в многоголосу тишу полів, то
  помічаєш, що в ній щось є не земне, а небесне.
</p>

Такий код каже браузерові, що на 75% послідовності анімації заголовок повинен мати лівий зовнішній відступ 25%, а ширину – 150%.

Примітка: Аби побачити анімацію – перезавантажте сторінку.

Повторення анімації

Щоб анімація повторювалася, треба додати властивість animation-iteration-count, котра вказує на те, скільки разів треба повторити анімацію. У цьому випадку використаймо infinite, аби анімація повторювалася нескінченно:

p {
  animation-duration: 3s;
  animation-name: slidein;
  animation-iteration-count: infinite;
}

Додавши це до наявного коду:

@keyframes slidein {
  from {
    margin-left: 100%;
    width: 300%;
  }

  to {
    margin-left: 0%;
    width: 100%;
  }
}
<p>
  Коли лежиш в полі лицем до неба і вслухаєшся в многоголосу тишу полів, то
  помічаєш, що в ній щось є не земне, а небесне.
</p>

Анімація з рухом назад і вперед

Приклад вище змусив анімацію повторюватися, але вельми дивно, що вона щоразу скаче на початок, коли починається. Насправді хочеться, аби текст рухався текстом назад і вперед. Це легко реалізувати, задавши властивість animation-direction зі значенням alternate:

p {
  animation-duration: 3s;
  animation-name: slidein;
  animation-iteration-count: infinite;
  animation-direction: alternate;
}

І – решта коду:

@keyframes slidein {
  from {
    margin-left: 100%;
    width: 300%;
  }

  to {
    margin-left: 0%;
    width: 100%;
  }
}
<p>
  Коли лежиш в полі лицем до неба і вслухаєшся в многоголосу тишу полів, то
  помічаєш, що в ній щось є не земне, а небесне.
</p>

Використання подій анімації

Над анімаціями можна отримати додатковий контроль – а також корисну інформацію про них – шляхом використання подій анімації. Такі події, представлені об'єктом AnimationEvent, можуть використовуватися для відстеження того, коли анімації починаються, завершуються і коли починають нову ітерацію. Кожна подія містить час, коли вона відбулася, а також ім'я анімації, що спричинила подію.

Змінімо приклад з ковзанням тексту, аби вивести певну інформацію про кожну подію анімації, що спрацьовує, аби побачити, як ці події працюють.

Додавання CSS

Почнімо з CSS для анімації. Ця анімація триватиме 3 секунди, зватиметься "slidein", повториться 3 рази, щоразу зі зміною напряму. В @keyframes width і margin-left змінюються, аби змусити елемент ковзати екраном.

.slidein {
  animation-duration: 3s;
  animation-name: slidein;
  animation-iteration-count: 3;
  animation-direction: alternate;
}

@keyframes slidein {
  from {
    margin-left: 100%;
    width: 300%;
  }

  to {
    margin-left: 0%;
    width: 100%;
  }
}

Додавання слухачів подій анімації

Застосуймо код JavaScript для слухання всіх трьох можливих подій анімації. Код нижче налаштовує слухачів подій; він викликається, коли документ завантажився, щоб усе приготувати.

const element = document.getElementById("watchme");
element.addEventListener("animationstart", listener, false);
element.addEventListener("animationend", listener, false);
element.addEventListener("animationiteration", listener, false);

element.className = "slidein";

Це вельми стандартний код; подробиці про те, як він працює, можна знайти в документації eventTarget.addEventListener(). Останнє, що робить цей код – присвоєння class елемента, що анімується, значення "slidein"; це робиться, щоб почати анімацію.

Чому? Бо подія animationstart спрацьовує, щойно почалась анімація, а в нашому випадку це відбувається до спрацювання нашого коду. Тож почнімо анімацію самі, встановивши клас елемента зі стилем, що, зрештою, анімується.

Отримання подій

Події передаються функції listener(), показаній нижче.

function listener(event) {
  const l = document.createElement("li");
  switch (event.type) {
    case "animationstart":
      l.textContent = `Почалося: час, що минув – ${event.elapsedTime}`;
      break;
    case "animationend":
      l.textContent = `Скінчилося: час, що минув – ${event.elapsedTime}`;
      break;
    case "animationiteration":
      l.textContent = `Новий цикл почався після ${event.elapsedTime}`;
      break;
  }
  document.getElementById("output").appendChild(l);
}

Цей код також є вельми простим. Він дивиться на event.type, аби визначити, якого роду подія анімації сталася, а потім додає відповідний запис у <ul> (невпорядкований список), що використовується як журнал таких подій.

Вивід після всього цього має якийсь такий вигляд:

  • Почалося: час, що минув – 0
  • Новий цикл почався після 3.01200008392334
  • Новий цикл почався після 6.00600004196167
  • Скінчилося: час, що минув – 9.234000205993652

Зверніть увагу, що миті часу дуже наближені до очікуваних згідно з заданим часом анімації, але не рівні їм. Також зверніть увагу, що після фінальної ітерації анімації подія animationiteration не надсилається; замість неї надсилається подія animationend.

Заради повноти – ось HTML, що виводить вміст сторінки, включно зі списком, до якого сценарій вставляє інформацію про отримані події:

<h1 id="watchme">Дивіться, як я рухаюсь</h1>
<p>
  Цей приклад демонструє, як використовувати анімації CSS, аби елементи
  <code>H1</code> рухались по сторінці.
</p>
<p>
  На додачу – щоразу, коли спрацьовує подія анімації, виводиться певний текст,
  тож ці події помітні в дії.
</p>
<ul id="output"></ul>

І ось – справжній вивід.

Примітка: Аби побачити анімацію – перезавантажте сторінку.

Анімування display та content-visibility

Цей приклад демонструє те, як можна анімувати властивості display та content-visibility. Це корисно для створення анімацій входу-виходу, де, наприклад, треба вилучити контейнер із DOM за допомогою display: none, але зробити його зникнення плавним за допомогою opacity, а не миттєвим.

Браузери, що це підтримують, анімують display та content-visibility за допомогою варіації на дискретному типі анімації. Це, як правило, означає, що властивість перемикається між двома значеннями на 50% шляху анімації між ними.

Проте є виняток, а саме – коли анімується display: none чи content-visibility: hidden до значення видимості. У цьому випадку браузер перемикається між двома значеннями так, щоб анімований вміст був видимим протягом усієї тривалості анімації.

Отже, наприклад:

  • Коли display анімується від none до block (чи іншого видимого значення display), значення перемикається на block на 0% тривалості анімації, щоб воно було видимим протягом усієї анімації.
  • Коли display анімується від block (чи іншого видимого значення display) до none, значення перемикається на none на 100% тривалості анімації, щоб воно було видимим протягом усієї анімації.

HTML

HTML тут містить два елементи <p> з <div> між ними, котрий ми анімуємо від display none до block.

<p>
  Клацніть десь на екрані чи натисніть будь-яку клавішу, щоб перемкнути
  <code>&lt;div&gt;</code> між прихованістю та показом.
</p>

<div>
  Це елемент <code>&lt;div&gt;</code>, що анімується між
  <code>display: none; opacity: 0</code> та
  <code>display: block; opacity: 1</code>. Цікаво, чи не так?
</div>

<p>
  Це інший абзац, потрібний для того, щоб показати, що
  <code>display: none; </code> застосовується й вилучається до вищезазначеного
  <code>&lt;div&gt; </code>. Якби змінювалася лише його <code>opacity</code>,
  він завжди займав би місце в DOM.
</p>

CSS

html {
  height: 100vh;
}

div {
  font-size: 1.6rem;
  padding: 20px;
  border: 3px solid red;
  border-radius: 20px;
  width: 480px;
  opacity: 0;
  display: none;
}

/* Класи анімації */

div.fade-in {
  display: block;
  animation: fade-in 0.7s ease-in forwards;
}

div.fade-out {
  animation: fade-out 0.7s ease-out forwards;
}

/* Ключові кадри анімації */

@keyframes fade-in {
  0% {
    opacity: 0;
    display: none;
  }

  100% {
    opacity: 1;
    display: block;
  }
}

@keyframes fade-out {
  0% {
    opacity: 1;
    display: block;
  }

  100% {
    opacity: 0;
    display: none;
  }
}

Зверніть увагу на присутність в анімаціях ключових кадрів властивості display.

JavaScript

Врешті-решт, додаймо трохи JavaScript, щоб налаштувати слухачі подій, які запускатимуть анімації. А саме – додаймо <div> клас fade-in, коли хочемо, щоб він з'явився, і fade-out, коли хочемо, щоб він зник.

const divElem = document.querySelector("div");
const htmlElem = document.querySelector(":root");

htmlElem.addEventListener("click", showHide);
document.addEventListener("keydown", showHide);

function showHide() {
  if (divElem.classList[0] === "fade-in") {
    divElem.classList.remove("fade-in");
    divElem.classList.add("fade-out");
  } else {
    divElem.classList.remove("fade-out");
    divElem.classList.add("fade-in");
  }
}

Результат

Цей код візуалізується так:

Дивіться також