В CSS два способа что-то двигать: transition и animation. Первый плавно переводит свойство из старого значения в новое, когда значение поменялось. Второй проигрывает заранее описанные кадры @keyframes сам по себе.
transition: «было - стало»
.button {
background: #2563eb;
transform: translateY(0);
transition: background 200ms ease, transform 200ms ease-out;
}
.button:hover {
background: #1d4ed8;
transform: translateY(-2px);
}
Переход срабатывает только при смене значения: наведение, класс из JS, смена медиазапроса. Части шортхенда: свойство, длительность, функция времени, задержка. Первое время в записи - длительность, второе - задержка.
Чего transition не умеет:
- запуститься сам при загрузке страницы (нужна смена значения или
@starting-style); - анимировать до
height: auto: браузер не интерполирует ключевое словоauto. Рабочий приём - обернуть контент в grid и анимироватьgrid-template-rowsот0frк1fr; - плавно скрыть элемент через
display: noneв старом коде: элемент исчезает мгновенно. Для плавного скрытия анимируютopacityиvisibility.
transitionend срабатывает отдельно на каждое анимируемое свойство. Если переходят два свойства, событий будет два.
animation и @keyframes
@keyframes pulse {
from { transform: scale(1); }
50% { transform: scale(1.08); }
to { transform: scale(1); }
}
.badge {
animation: pulse 1.2s ease-in-out infinite;
}
Полезные свойства:
animation-iteration-count: число повторов илиinfinite;animation-direction: alternate: туда, потом обратно;animation-fill-mode: forwards: после окончания элемент остаётся в состоянии последнего кадра. Без него стили откатываются к исходным;animation-play-state: paused: поставить на паузу;animation-delayбывает отрицательной: анимация стартует «с середины».
Функции времени: ease (по умолчанию), linear, ease-in, ease-out, cubic-bezier(...) и steps(n) для покадровой анимации, например спрайтов.
Что анимировать дёшево
Кадр браузера: стили, layout (геометрия), paint (пиксели), composite (склейка слоёв). Чем раньше в цепочке задевает свойство, тем дороже кадр.
transformиopacityобычно затрагивают только composite и могут крутиться на потоке композитора даже при занятом главном потоке.width,height,top,left,marginменяют геометрию: каждый кадр layout и paint.background-color,box-shadow,colorвызывают перерисовку без layout.
Хочешь сдвинуть элемент - transform: translate(), а не left. Хочешь «вырасти» - scale(), а не width.
will-change: transform заранее подсказывает браузеру вынести элемент в отдельный слой. Это память на каждый слой, поэтому ставят точечно и снимают, когда анимация закончилась. Повесить will-change на всё подряд - антипаттерн.
Доступность
Часть людей плохо переносит движение на экране. Уважай системную настройку:
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}
Мигание чаще трёх раз в секунду опасно для людей с фоточувствительной эпилепсией.
Анимации из JS
Если анимацию нужно запускать и останавливать из кода, есть два пути:
- переключать классы и отдавать движение CSS;
- Web Animations API:
el.animate(keyframes, { duration: 300 })возвращает объектAnimationсpause(),reverse(),finished(промис).
Ручные покадровые расчёты делают через requestAnimationFrame, а не setInterval: rAF синхронизирован с отрисовкой и засыпает в фоновой вкладке.
Частые ошибки
- Анимируют
left/topвместоtransformи получают дёрганье на слабых телефонах. - Пишут
transition: all: анимируется всё, включая то, что не планировали, и это дороже. - Забывают
animation-fill-mode: forwardsи удивляются, что элемент «прыгает» обратно. - Ставят
will-changeглобально.
Что спросят на собесе
- Чем
transitionотличается отanimation? - Какие свойства анимировать дёшево и почему?
- Зачем
will-changeи чем опасен? - Как анимировать раскрытие блока неизвестной высоты?
- Как учесть пользователей, которым мешает движение?