CSS властивості для створення анімацій та плавних перехідних ефектів
CSS-анімації та переходи - це два окремі інструменти для додавання руху на сторінку, і вони вирішують різні задачі.
Теорія
TL;DR
transitionреагує на зміну стану (hover, focus, toggle класу) і чекає тригераanimationз@keyframesзапускається сама - тригер не потрібен, може зациклюватись, може йти у зворотному напрямку- Головна різниця:
transition- це перехід А до Б;animation- це сценарій з довільною кількістю кроків - Для продуктивності анімуй
transformіopacity- вони працюють на GPU і не перераховують layout - Анімація
width,height,top,leftвикликає reflow на кожному кадрі - замінюй наtransform
Швидкий приклад
/* 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 приймає чотири значення: яку властивість анімувати, скільки часу, яке пом'якшення, і необов'язкова затримка. Кілька властивостей розділяються комами:
.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%, але можна ставити відсоткові точки де завгодно:
@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, абоinfiniteanimation-direction-normal,reverse,alternate(туди і назад)animation-fill-mode- який стан тримати до або після програвання
animation-fill-mode: forwards варто запам'ятати окремо. Без нього елемент повертається до початкового вигляду щойно анімація закінчується. Це ламає більшість ефектів появи (fade-in).
Що анімувати для продуктивності
Браузер малює сторінку шарами. Зміни transform і opacity відбуваються на compositor thread - окремому процесі, який не торкається розрахунку layout. Всі інші властивості (width, height, padding, left, top) змушують браузер перераховувати геометрію або перемальовувати пікселі.
/* викликає 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. Частина користувачів має вестибулярні порушення і анімація викликає дискомфорт. Невеликий медіа-запит вирішує це:
@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
.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
@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 розганяється і гальмує на кожному оберті, виглядає наче бореться сам з собою. Рівномірна швидкість - те що потрібно.
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.