Переходы вглубь: timing и кривые
Базовый transition вы уже видели — теперь разберём каждую его часть и научимся управлять характером движения.
transition — это автоматическая плавная интерполяция между двумя состояниями свойства, когда его значение меняется.
В базовом разделе мы писали transition: 0.3s и радовались, что кнопка плавно меняет цвет. Но transition — это на самом деле короткая запись четырёх отдельных свойств, и от их настройки зависит, будет движение выглядеть дёшево или дорого. Профессиональная анимация почти всегда отличается от любительской именно подобранной кривой и таймингом, а не количеством эффектов.
Зачем разбираться глубже
Переход с дефолтной кривой и случайной длительностью выглядит механически. Интерфейс ощущается «живым», когда движение подчиняется простой физике: вещи трогаются с места не мгновенно и тормозят к концу. Управлять этим позволяют четыре подсвойства transition.
Четыре составляющих transition
| Свойство | За что отвечает | Пример |
transition-property | какое CSS-свойство анимировать | background-color, transform, all |
transition-duration | сколько длится переход | 0.2s, 300ms |
transition-timing-function | характер скорости (кривая) | ease, linear, cubic-bezier(...) |
transition-delay | пауза перед стартом | 0s, 0.1s |
Короткая запись складывает их в одну строку именно в этом порядке: property duration timing-function delay.
.button {
background-color: #3b82f6;
/* свойство | длительность | кривая | задержка */
transition: background-color 0.25s ease-in-out 0s;
}
.button:hover {
background-color: #2563eb;
}
Кривые: ease, linear, cubic-bezier, steps
timing-function описывает, как распределяется скорость во времени. Это самое недооценённое свойство в анимации.
linear— постоянная скорость от начала до конца. Подходит для бесконечных вращений и индикаторов загрузки, но для появления элементов выглядит неестественно.ease(значение по умолчанию) — медленный старт, разгон, плавное торможение. Универсальный выбор.ease-in— медленный старт, быстрый конец. Хорош, когда элемент уходит со сцены.ease-out— быстрый старт, плавное торможение. Хорош, когда элемент появляется: глаз сразу видит движение.ease-in-out— плавно с обоих концов, для перемещений «из точки в точку».
Все они — лишь именованные пресеты функции cubic-bezier(x1, y1, x2, y2). Она задаёт кривую Безье двумя контрольными точками; ось X — это время (от 0 до 1), ось Y — прогресс свойства. Когда стандартных кривых мало, рисуют свою:
.card {
/* «упругий» выход с лёгким перелётом за единицу по Y */
transition: transform 0.4s cubic-bezier(0.34, 1.56, 0.64, 1);
}
.card:hover {
transform: translateY(-8px);
}
Значение y2 = 1.56 больше единицы — поэтому элемент на миг «перелетает» цель и возвращается, создавая ощущение пружины. В DevTools кривую можно редактировать мышью прямо на графике.
Дискретные шаги: steps()
Функция steps(n) делает движение не плавным, а скачкообразным — за n равных ступенек. Это классический приём для покадровой спрайт-анимации и эффекта «печатающегося» текста.
.sprite {
/* 8 кадров за 1 секунду, по одному скачку на кадр */
transition: background-position 1s steps(8);
}
Что вообще можно анимировать
Браузер умеет интерполировать только те свойства, у которых есть «промежуточные» значения: числа, длины, цвета, проценты, transform, opacity. А вот свойства с дискретными значениями анимировать через transition нельзя.
| Анимируется | Не анимируется (переключается скачком) |
opacity, color, background-color | display |
width, height, padding, margin | visibility (но участвует особым образом) |
transform, box-shadow, border-color | font-family, position |
Главная ловушка новичка — попытка плавно «скрыть» блок через display: none. Так не работает: display переключается мгновенно, и перехода opacity вы не увидите. Решают это анимацией opacity вместе с visibility, а сам display при необходимости меняют после завершения.
Переход на нескольких свойствах
Часто нужно анимировать сразу несколько свойств с разными настройками. Их перечисляют через запятую, и каждому можно задать свою длительность и кривую:
.panel {
opacity: 0;
transform: translateY(12px);
transition:
opacity 0.2s ease-out,
transform 0.35s cubic-bezier(0.22, 1, 0.36, 1) 0.05s;
}
.panel.is-open {
opacity: 1;
transform: translateY(0);
}
Здесь прозрачность нарастает быстро (0.2s), а сдвиг идёт дольше и со своей кривой, да ещё с задержкой 0.05s — получается аккуратное «выезжающее» появление. Соблазнительно написать transition: all 0.3s, но all заставляет браузер следить за каждым свойством и легко даёт паразитные анимации (например, мигание тени при перерисовке). Перечисляйте свойства явно.
Как это работает под капотом
Когда значение свойства меняется (по :hover, добавлению класса, инлайн-стилю из JS), браузер сравнивает старое и новое значения. Если для этого свойства задан transition, он не применяет новое значение мгновенно, а на каждом кадре (обычно 60 раз в секунду) вычисляет промежуточное по формуле timing-function(прогресс_времени) и подставляет его. Прогресс времени идёт линейно от 0 до 1, а кривая превращает его в прогресс свойства. Поэтому transition реагирует именно на изменение состояния и не запускается сам по себе при загрузке страницы.
Частые ошибки
- Указали
transitionна состоянии:hover, а не на базовом селекторе. Тогда переход плавный «туда», но резкий «обратно». Объявляйтеtransitionна основном правиле элемента. - Слишком длинная длительность. 600–800мс на ховер кнопки ощущаются как тормоза. Для микровзаимодействий держитесь диапазона 120–300мс.
transition: allповсюду. Бьёт по производительности и даёт неожиданные анимации.- Анимация
width/heightвместоtransform. Работает, но дорого — об этом отдельный урок про производительность.
Итоги
transition— короткая записьproperty duration timing-function delay.- Характер движения задаёт
timing-function;ease-outхорош для появления,linear— для бесконечного вращения. cubic-bezier()рисует любую кривую,steps()— скачкообразное движение для спрайтов.- Анимируются только свойства с промежуточными значениями;
display— нет. - Несколько свойств перечисляйте явно через запятую, избегайте
all.