transition-timing-function задаёт, с какой скоростью проходит переход - равномерно или с ускорением и замедлением. По умолчанию используется ease: переход стартует быстро и плавно тормозит к концу. Кроме готовых кривых есть ступенчатая steps() для эффекта прыжков и полностью свои кривые через cubic-bezier().
В этом уроке
Попробуй прямо сейчас: наведи курсор на весь блок целиком - все три шарика стартуют одновременно и проезжают одинаковое расстояние, но с разными кривыми: верхний (linear) едет равномерно, средний (ease) быстро стартует и плавно тормозит, а нижний (steps) скачет резкими прыжками. Замени steps(6, jump-end) на steps(3, jump-start) и посмотри на разницу.
Готовые кривые: ease, linear, ease-in, ease-out
У каждой встроенной кривой своё ощущение движения. В переходе длительностью 1 секунда к ровно половине времени элемент по факту проходит: linear - около 48% пути (действительно равномерно), ease (значение по умолчанию) - уже около 78% (основной рывок происходит в начале, а концовка плавно тормозит), ease-in - только около 30% (стартует медленно, разгоняется к концу), ease-out - около 67% (стартует быстро, к концу замедляется, но мягче, чем ease). ease-in-out ведёт себя симметрично: медленный старт и такой же медленный финиш.
.box {
transition: transform 1s ease-out;
}
.box:hover {
transform: translateX(200px);
} - ease-out: быстрый старт и мягкое торможение в конце, ощущается как естественное движение по инерции.
Неправильное ожидание - что ease-in-out используется по умолчанию, если ничего не указывать:
.box {
transition: transform 1s;
} - по умолчанию применяется именно ease, а не ease-in-out - это разные кривые с разным распределением скорости.
Ступенчатая анимация: функция steps()
steps(N, позиция) делит переход не на плавную кривую, а на N резких скачков - классический приём для спрайт-анимаций и эффекта печатной машинки. Позиция определяет, КОГДА происходит скачок внутри каждого отрезка: у jump-end (значение по умолчанию) элемент держит начальное значение до первой границы интервала и прыгает только по её достижении - последний прыжок происходит ровно в момент завершения перехода. У jump-start всё наоборот: первый прыжок происходит мгновенно, в самый момент запуска перехода, ещё до того, как прошло хоть какое-то время. Есть ещё jump-none (прыжков на единицу меньше, финальное значение внутри самой анимации не достигается) и jump-both (прыжков на единицу больше - есть скачок и в начале, и в конце) - более редкие частные случаи.
.sprite {
transition: transform 2s steps(5, jump-end);
}
.sprite:hover {
transform: translateX(200px);
} - пять чётких скачков, первый - только после того, как пройдёт 1/5 времени.
Неправильное ожидание - что jump-start и jump-end выглядят одинаково, раз "количество шагов то же самое":
.sprite {
transition: transform 2s steps(5, jump-start);
} - с jump-start первый скачок происходит сразу же, в момент наведения курсора, а не после паузы - на глаз это заметно более резкий, "уже начавшийся" эффект.
Свои кривые: cubic-bezier()
cubic-bezier(x1, y1, x2, y2) описывает кривую двумя контрольными точками и позволяет задать любую скорость, которой нет среди готовых ключевых слов. У координат X (x1 и x2) есть жёсткое ограничение - только от 0 до 1, потому что они означают течение времени, которое не может идти вспять. А вот координаты Y можно выводить далеко за пределы 0-1: именно так получают эффект отскока (bounce) или лёгкого "перелёта" за целевое значение с последующим возвратом - на практике элемент реально на мгновение проезжает дальше цели, а затем плавно возвращается на своё место. Сами готовые ключевые слова - это просто именованные частные случаи cubic-bezier: ease равен cubic-bezier(.25, .1, .25, 1), а linear - cubic-bezier(0, 0, 1, 1).
.box {
transition: transform .8s cubic-bezier(.34, 1.56, .64, 1);
} - валидно: Y-координата 1.56 выходит за пределы 0-1, элемент на мгновение "перелетит" целевое положение и плавно вернётся - получается эффект отскока.
Неправильное ожидание - что так же свободно можно выходить за пределы и по оси X:
.box {
transition: transform .8s cubic-bezier(-0.5, 0, 1.5, 1);
} - X-координаты -0.5 и 1.5 вне диапазона 0-1, всё значение невалидно, и transition-timing-function просто не применится.
Частые ошибки новичков
| Ошибка | Что происходит | Как исправить |
|---|---|---|
| Думать, что ease-in-out - это значение по умолчанию | По умолчанию применяется именно ease - это другая кривая, с другим распределением скорости движения | Указывать нужную кривую явно, а не полагаться на предположения о значении по умолчанию |
| Ждать, что jump-start и jump-end выглядят одинаково | jump-start прыгает мгновенно в начале перехода, jump-end держит начальное значение до первой границы - на глаз это разные по ощущению анимации | Выбирать позицию исходя из желаемого эффекта, проверяя результат вживую |
| Указать в cubic-bezier() X-координату вне диапазона 0-1 | Значение целиком невалидно и отбрасывается - браузер применит предыдущую или дефолтную функцию плавности | Держать x1 и x2 строго в пределах 0-1; выходить за границы можно только по Y |
| Использовать эффект отскока (Y вне 0-1) на layout-свойствах вроде width/height в контейнере с overflow: hidden | Элемент временно "перелетает" целевое значение, из-за чего может обрезаться контент или дёрнуться скролл | Применять отскок к transform (не влияет на раскладку страницы), а не к layout-свойствам |
Шпаргалка
| Значение | Что означает |
|---|---|
| ease (по умолчанию) | cubic-bezier(.25, .1, .25, 1) - быстрый старт, плавное торможение |
| linear | cubic-bezier(0, 0, 1, 1) - равномерно, без ускорения и замедления |
| ease-in / ease-out | Медленный старт с разгоном / быстрый старт с плавным финишем |
| steps(N, jump-end) | N резких скачков; старт держится до первой границы (позиция по умолчанию) |
| steps(N, jump-start) | Первый скачок происходит мгновенно в момент запуска перехода |
| cubic-bezier(x1,y1,x2,y2) | Свои кривые; x1/x2 только 0...1, y1/y2 можно выводить за пределы ради эффекта отскока |
Коротко: отвечаем на вопросы
В чём разница между ease, ease-in и ease-out?
ease (значение по умолчанию) быстро стартует и плавно тормозит к концу. ease-in стартует медленно и разгоняется к финалу - используется, когда элемент как будто "убегает" со страницы. ease-out - зеркальная ситуация: быстрый старт и мягкое торможение, обычно для элементов, которые "прибывают" на экран.
Можно ли сделать анимацию с эффектом отскока (bounce) на чистом CSS?
Да, без единой строчки JavaScript: достаточно указать в cubic-bezier() Y-координату больше 1 или меньше 0, например cubic-bezier(.34, 1.56, .64, 1). Элемент на мгновение проедет дальше целевого значения и плавно вернётся обратно - именно так и выглядит "пружинящий" отскок.
Есть ли инструмент, чтобы подобрать кривую cubic-bezier() визуально, а не на глаз?
Да, самый известный - cubic-bezier.com (сделан Лией Веру, соавтором нескольких CSS-спецификаций): там можно перетаскивать контрольные точки мышкой и сразу видеть, как меняется движение, а потом скопировать готовое значение в свой CSS.