Переходы и анимации
transition и transform, кривые ускорения, @keyframes — и главное правило: анимировать дёшево можно только две вещи.
Что ты научишься делать
- Сделаешь кнопку, которая приятно отзывается на наведение
- Поймёшь, почему анимация width тормозит, а transform — нет
- Напишешь свою анимацию появления через @keyframes
#transition: плавно вместо рывком
По умолчанию любое изменение стиля происходит мгновенно. transition говорит браузеру: «меняй это свойство постепенно».
Важная деталь: transition пишут на обычном состоянии, а не на :hover. Тогда переход работает и туда, и обратно.
.btn { background: #6544e0; transition: background 200ms ease-out; /* │ │ └ кривая ускорения │ └ длительность └ что анимируем */}.btn:hover { background: #5334cf;}Так не надо
.btn:hover {
background: #5334cf;
transition: background .2s;
}Переход существует только пока курсор на кнопке. Наведение плавное, а уход — резкий рывок.
Так надо
.btn {
transition: background .2s ease-out;
}
.btn:hover {
background: #5334cf;
}Свойство описано в базовом состоянии, поэтому переход работает в обе стороны.
#Кривые ускорения
Одинаковая длительность, разное ощущение. Равномерное движение выглядит механически: в жизни ничего не трогается с места и не останавливается мгновенно.
| Функция | Характер | Где применять |
|---|---|---|
linear | Равномерно | Полоса загрузки, бесконечное вращение |
ease-out | Быстрый старт, мягкое торможение | Появление, наведение — самый частый выбор |
ease-in | Медленный старт, резкий конец | Исчезновение, уход за край |
ease-in-out | Плавно с обоих концов | Перемещение между двумя точками |
cubic-bezier(.22,1,.36,1) | Резкий старт, долгий выезд | Ощущение «дорогого» интерфейса |
#transform: сдвиг, масштаб, поворот
transform меняет то, как элемент нарисован, не трогая его место в раскладке. Соседи не сдвигаются, ничего не пересчитывается.
transform: translateY(-4px); /* сдвиг */transform: translate(10px, -4px); /* по обеим осям */transform: scale(1.05); /* масштаб */transform: rotate(3deg); /* поворот */transform: skewX(-8deg); /* наклон *//* Несколько сразу — через пробел, порядок важен */transform: translateY(-4px) scale(1.03);#Почему анимировать можно только две вещи
Чтобы показать кадр, браузер проходит четыре этапа: считает стили, считает геометрию (layout), рисует пиксели (paint) и складывает слои (composite).
Меняешь width или top — браузер пересчитывает геометрию всей страницы, потом всё перерисовывает. И так шестьдесят раз в секунду. Результат — рывки.
Меняешь transform или opacity — геометрия не меняется вообще. Работает только последний, самый дешёвый этап, который умеет делать видеокарта.
| Вместо | Пиши |
|---|---|
width, height | transform: scale() |
top, left, margin | transform: translate() |
display: none с анимацией | opacity + visibility |
background-position | transform: translate() у внутреннего слоя |
#@keyframes: анимация по кадрам
transition умеет переходить между двумя состояниями. Когда нужно больше — например, элемент должен появиться, слегка проскочить цель и вернуться, — берут @keyframes.
@keyframes appear { 0% { opacity: 0; transform: translateY(20px); } 100% { opacity: 1; transform: translateY(0); }}.card { animation: appear 500ms ease-out both; /* │ │ │ └ сохранить конечное состояние │ │ └ кривая │ └ длительность └ имя из @keyframes */}| Свойство | Что задаёт |
|---|---|
animation-name | Имя набора кадров |
animation-duration | Длительность одного прохода |
animation-delay | Задержка перед стартом |
animation-iteration-count | Сколько раз повторить, infinite — бесконечно |
animation-direction | alternate — туда-обратно |
animation-fill-mode | both — держать первый кадр до старта и последний после |
#Сколько должна длиться анимация
- 100–200 мс — реакция на действие: наведение, нажатие. Должно ощущаться мгновенным.
- 200–400 мс — появление элемента, раскрытие меню.
- 400–800 мс — «повествовательные» вещи: демонстрация, обучающая подсказка.
- Больше секунды — почти всегда раздражает. Человек уже готов действовать, а интерфейс ещё едет.
#Уважай тех, кому движение мешает
Для части людей анимация — не украшение, а причина тошноты и головокружения. У них в системе стоит настройка «уменьшить движение», и её нужно уважать.
@media (prefers-reduced-motion: reduce) { *, *::before, *::after { animation-duration: 0.001ms !important; animation-iteration-count: 1 !important; transition-duration: 0.001ms !important; scroll-behavior: auto !important; }}Где спотыкаются почти все
transition написан внутри :hover
Почему Переход существует только во время наведения. Возврат происходит рывком.
Как надо Пиши transition в базовом состоянии элемента.
Анимируются width, height, top или margin
Почему Каждый кадр заставляет браузер пересчитывать геометрию страницы. На слабом устройстве анимация начинает дёргаться.
Как надо Переведи на transform и opacity — они не трогают раскладку.
transition: all
Почему Браузер следит за всеми свойствами, включая дорогие. Появляются лишние анимации и подтормаживания.
Как надо Перечисли нужные свойства через запятую.
Нет блока prefers-reduced-motion
Почему Людям с вестибулярными нарушениями сайт становится физически неприятен.
Как надо Добавь медиазапрос, отключающий нефункциональное движение. Это пять строк.
- Переход transition
- Плавное изменение свойства между двумя состояниями.
- Кривая ускорения timing function, easing
- Закон, по которому меняется скорость движения.
- Трансформация transform
- Сдвиг, масштаб, поворот или наклон без влияния на раскладку.
- Ключевые кадры keyframes
- Именованный набор состояний, между которыми идёт анимация.
- Композитинг compositing
- Последний, самый дешёвый этап отрисовки. Именно на нём работают
transformиopacity.
Проверь себя
Тогда переход работает и при наведении, и при уходе курсора.
Изменение ширины запускает пересчёт раскладки и перерисовку. <code>transform</code> обходится последним, самым дешёвым этапом.
Без него элемент до начала и после конца анимации возвращается к обычным стилям — и появление «моргает».
Реакция на действие должна ощущаться мгновенной. Всё, что дольше 300 мс, начинает раздражать.
Верных вариантов несколько.
Только эти два свойства обходятся без пересчёта раскладки. Остальные заставляют браузер работать заново каждый кадр.
Задание
Кнопка и карточка с микро-анимацией
Оживи интерфейс:
- кнопке добавь переход и состояние
:hover, где она сдвигается вверх черезtransform; - карточке — переход и лёгкий подъём при наведении;
- анимируй только
transform,opacityиbox-shadow, безall; - длительность переходов — от 120 до 400 мс;
- добавь блок
prefers-reduced-motion, отключающий движение.
Подсказка 1
Кнопке: transition: transform 180ms ease-out, box-shadow 180ms ease-out; и .btn:hover { transform: translateY(-2px); }
Подсказка 2
Карточке — то же самое, только чуть дольше: 260ms и translateY(-4px).
Подсказка 3
В конце добавь: @media (prefers-reduced-motion: reduce) { *, *::before, *::after { transition-duration: .001ms !important; animation-duration: .001ms !important; } }
Показать решение
<p><button class="btn" type="button">Заказать</button></p><article class="card"> <h3>Раф на кокосовом</h3> <p>Наведи курсор.</p></article>* { box-sizing: border-box; }.btn { padding: 12px 26px; font: inherit; font-weight: 600; color: #fff; background: #6544e0; border: 0; border-radius: 999px; cursor: pointer; transition: transform 180ms cubic-bezier(.22, 1, .36, 1), box-shadow 180ms ease-out;}.btn:hover { transform: translateY(-2px); box-shadow: 0 10px 22px -10px rgb(101 68 224 / .7);}.btn:active { transform: translateY(0);}.card { max-width: 280px; padding: 18px; background: #f5f5fa; border-radius: 14px; transition: transform 260ms cubic-bezier(.22, 1, .36, 1), box-shadow 260ms ease-out;}.card:hover { transform: translateY(-4px); box-shadow: 0 18px 34px -18px rgb(20 24 48 / .4);}.card h3 { margin: 0 0 6px; font-size: 16px; }.card p { margin: 0; font-size: 13px; color: #666; }@media (prefers-reduced-motion: reduce) { *, *::before, *::after { animation-duration: 0.001ms !important; transition-duration: 0.001ms !important; }}