Skip to main content

CSS властивості для створення анімацій та плавних перехідних ефектів

CSS-анімації та переходи - це два окремі інструменти для додавання руху на сторінку, і вони вирішують різні задачі.

Теорія

TL;DR

  • transition реагує на зміну стану (hover, focus, toggle класу) і чекає тригера
  • animation з @keyframes запускається сама - тригер не потрібен, може зациклюватись, може йти у зворотному напрямку
  • Головна різниця: transition - це перехід А до Б; animation - це сценарій з довільною кількістю кроків
  • Для продуктивності анімуй transform і opacity - вони працюють на GPU і не перераховують layout
  • Анімація width, height, top, left викликає reflow на кожному кадрі - замінюй на transform

Швидкий приклад

css
/* transition: реагує на :hover */ .button { background: #3b82f6; transition: background 0.3s ease, transform 0.15s ease; } .button:hover { background: #1d4ed8; transform: scale(1.04); } /* animation: запускається сама, тригер не потрібен */ @keyframes fadeIn { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: translateY(0); } } .modal { animation: fadeIn 0.25s ease-out forwards; }

transition чекає. animation просто йде.

transition: зміни за тригером

transition приймає чотири значення: яку властивість анімувати, скільки часу, яке пом'якшення, і необов'язкова затримка. Кілька властивостей розділяються комами:

css
.card { transition: box-shadow 0.2s ease, transform 0.2s ease; } .card:hover { box-shadow: 0 8px 24px rgba(0,0,0,0.15); transform: translateY(-4px); }

transition: all 0.3s технічно працює. Але коли пізніше додаєш нову властивість і вона раптово починає анімуватись, витратиш час на дебаг. Краще перелічувати властивості явно.

Функції пом'якшення (timing function): ease - стандарт, починає швидко і гальмує наприкінці. linear - рівна швидкість. ease-out - гальмує до кінця, найприродніше відчувається для UI-взаємодій, бо нагадує як зупиняються фізичні об'єкти. cubic-bezier() - повний контроль над кривою.

animation + @keyframes: сценарна анімація

@keyframes описує анімацію як послідовність знімків. from/to - те саме що 0%/100%, але можна ставити відсоткові точки де завгодно:

css
@keyframes pulse { 0% { transform: scale(1); } 50% { transform: scale(1.08); } 100% { transform: scale(1); } } .badge { animation: pulse 1.5s ease-in-out infinite; }

Основні властивості animation:

  • animation-name - який блок @keyframes використовувати
  • animation-duration - тривалість одного циклу
  • animation-timing-function - пом'якшення (ті самі варіанти що в transition)
  • animation-delay - затримка перед першим запуском
  • animation-iteration-count - 1, 3, або infinite
  • animation-direction - normal, reverse, alternate (туди і назад)
  • animation-fill-mode - який стан тримати до або після програвання

animation-fill-mode: forwards варто запам'ятати окремо. Без нього елемент повертається до початкового вигляду щойно анімація закінчується. Це ламає більшість ефектів появи (fade-in).

Що анімувати для продуктивності

Браузер малює сторінку шарами. Зміни transform і opacity відбуваються на compositor thread - окремому процесі, який не торкається розрахунку layout. Всі інші властивості (width, height, padding, left, top) змушують браузер перераховувати геометрію або перемальовувати пікселі.

css
/* викликає reflow на кожному кадрі - повільно */ .bad { transition: width 0.3s ease; } /* залишається на compositor thread - швидко */ .good { transition: transform 0.3s ease; }

Якщо потрібно анімувати ріст елемента - використовуй transform: scaleX() або scaleY() замість width чи height. Для виїзду елемента - transform: translateX() замість left.

will-change: transform підказує браузеру заздалегідь виділити окремий compositor layer для елемента. З практики: додавати will-change усюди підряд - часта помилка оптимізації. Спочатку профілюй, потім додавай тільки там де реально є просідання кадрів. Кожен promoted layer займає пам'ять GPU.

Типові помилки

Анімація layout-властивостей. Зміна height або width у циклі дропає кадри. Для розширення елементів - transform: scaleY().

transition: all. Працює, але анімує непотрібні властивості. Перелічуй явно.

Забутий animation-fill-mode: forwards. Елемент красиво з'являється, потім стрибає назад до opacity: 0. Додай forwards - елемент залишиться в кінцевому стані.

@keyframes для hover-ефектів. Hover - це два стани, саме для цього є transition. @keyframes має сенс коли потрібно три і більше кроків, або коли анімація запускається без дії користувача.

Відсутня підтримка prefers-reduced-motion. Частина користувачів має вестибулярні порушення і анімація викликає дискомфорт. Невеликий медіа-запит вирішує це:

css
@media (prefers-reduced-motion: reduce) { *, *::before, *::after { animation-duration: 0.01ms !important; transition-duration: 0.01ms !important; } }

Питання на співбесіді

Q: Які CSS-властивості безпечно анімувати з точки зору продуктивності?
A: transform і opacity. Вони працюють на compositor thread і не викликають layout чи repaint. Властивості width, height, top, left запускають reflow на кожному кадрі.

Q: Що робить animation-fill-mode: forwards?
A: Каже елементу зберігати стилі з останнього keyframe після завершення анімації. Без цього елемент повертається до початкового вигляду щойно анімація зупиняється.

Q: Чи можна запустити кілька анімацій на одному елементі?
A: Так, через кому: animation: fadeIn 0.3s ease, pulse 1s ease-in-out 0.3s infinite. Кожна анімація має власний тайминг і вони перекриваються незалежно.

Q: Коли transition не спрацює?
A: На властивостях без проміжних значень. Перехід між display: none і display: block неможливий - немає середнього стану. Для показу/приховування анімують opacity разом з visibility, або використовують transform: scale(0).

Q: Яка різниця між animation-direction: alternate і двома окремими блоками @keyframes?
A: alternate автоматично реверсує анімацію на непарних ітераціях з тим самим пом'якшенням. Два окремих блоки дають незалежний контроль над кривими - зручно коли потрібен повільний ease-out в одну сторону і різкий snap у зворотну.

Приклади

Hover-кнопка з transition

css
.btn { background: #3b82f6; color: white; padding: 10px 20px; border: none; border-radius: 6px; cursor: pointer; /* дві властивості з різним таймингом */ transition: background 0.25s ease, transform 0.15s ease; } .btn:hover { background: #2563eb; transform: scale(1.03); } .btn:active { transform: scale(0.97); /* зворотний зв'язок на клік */ }

background займає 0.25s, а transform реагує за 0.15s. Різниця в часі відчувається природніше ніж однакова швидкість для обох: масштаб клацає чітко, а колір слідує за ним.

Спінер завантаження з animation

css
@keyframes spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } } .spinner { width: 24px; height: 24px; border: 3px solid #e5e7eb; border-top-color: #3b82f6; border-radius: 50%; animation: spin 0.75s linear infinite; }

Тут правильний вибір - linear. Спінер з ease розганяється і гальмує на кожному оберті, виглядає наче бореться сам з собою. Рівномірна швидкість - те що потрібно.

Коротка відповідь

Для співбесіди
Premium

Коротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.

Дочитали статтю?