Функция плавности анимации: animation-timing-function

animation-timing-function задаёт кривую плавности анимации - по умолчанию ease, как и у transition. Но у анимации есть возможность, которой нет у transition: указать СВОЮ кривую для каждого отрезка внутри @keyframes. Работает это не так, как кажется на первый взгляд - кривая, заданная НА каком-то проценте, управляет отрезком ПОСЛЕ него, а не до него.

Попробуй прямо сейчас: мячик падает вниз, ускоряясь (animation-timing-function: ease-in - задан на элементе, управляет первым отрезком 0-50%), а потом отскакивает вверх, замедляясь к концу (animation-timing-function: ease-out - задан ПРЯМО на keyframe 50%, управляет ВТОРЫМ отрезком, 50-100%). Убери "animation-timing-function: ease-out;" из строки 50% - мячик начнёт подниматься вверх так же, как падал (снова ease-in, потому что больше некому переопределить отрезок) - естественного отскока не получится, мячик будто падает дважды подряд.

Кривая на весь цикл анимации

Самый простой способ - указать animation-timing-function прямо на элементе, как и у transition из урока про кривые плавности: те же ease/linear/ease-in/ease-out/ease-in-out, steps() и cubic-bezier() работают одинаково. По умолчанию, если ничего не указать, действует ease. Эта кривая применяется К КАЖДОМУ ЦИКЛУ анимации целиком, если только внутри @keyframes не задано что-то своё для конкретного отрезка (об этом - в следующем разделе).

.spinner { animation-name: spin; animation-duration: 2s; animation-timing-function: linear; } - вся анимация spin крутится с одной и той же равномерной скоростью.

Забыть про эту строку - не ошибка, а просто другое поведение:

.spinner { animation-name: spin; animation-duration: 2s; } - animation-timing-function не указан, применяется значение по умолчанию ease, а не linear - вращение будет слегка неравномерным, с ускорением и замедлением.

Своя кривая для каждого отрезка внутри @keyframes

У анимации, в отличие от transition, есть возможность задать РАЗНУЮ кривую плавности для разных отрезков одного цикла - animation-timing-function можно написать прямо внутри блока конкретного процента в @keyframes. Здесь и кроется неочевидный момент, подтверждённый собственной проверкой: кривая, указанная НА каком-то проценте (например 50%), управляет отрезком ПОСЛЕ него - от 50% до следующего keyframe, а НЕ отрезком, который к 50% ведёт. Если на конкретном проценте ничего не указано, для его отрезка берётся значение с самого элемента (как в первом разделе). А кривая, указанная на ПОСЛЕДНЕМ keyframe (100% или to), не имеет никакого эффекта - применять её больше не к чему, следующего отрезка не существует.

@keyframes drop { 0% { top: 0; } 50% { top: 150px; animation-timing-function: ease-out; } 100% { top: 0; } } .ball { animation: drop 1.6s ease-in infinite; } - падение (0-50%) идёт по ease-in с элемента, а подъём (50-100%) - по ease-out с самого keyframe 50%.

Частая путаница - думать, что кривая на 50% относится к пути ДО него:

@keyframes drop { 0% { top: 0; animation-timing-function: ease-out; } 50% { top: 150px; } 100% { top: 0; } } - кривая на 0% на самом деле управляет отрезком 0-50% (это верно), но многие ожидают, что писать плавность нужно НА том проценте, к которому она "относится по смыслу" - и путают, куда писать при более сложных цепочках из 3+ процентов.

Частые ошибки новичков

ОшибкаЧто происходитКак исправить
Думать, что плавность на keyframe X относится к отрезку ДО X На самом деле она управляет отрезком ПОСЛЕ X, до следующего keyframe Писать плавность на том проценте, С КОТОРОГО начинается нужный отрезок, а не на том, к которому он ведёт
Указать animation-timing-function на последнем keyframe (100% или to) Никакого эффекта - применять кривую больше не к чему, следующего отрезка нет Указывать нужную кривую на ПРЕДПОСЛЕДНЕМ значимом проценте или на элементе
Не указать animation-timing-function нигде Используется ease по умолчанию - может быть не тем, что ожидалось для чёткой покадровой анимации Явно указывать нужную кривую, если важна предсказуемость
Пытаться плавно изменить скорость/плавность анимации через transition при :hover animation-duration и animation-timing-function не анимируются - значение переключается мгновенно, без перехода Для плавного изменения скорости уже запущенной анимации нужен JavaScript, а не CSS transition

Шпаргалка

ЗаписьЧто означает
animation-timing-function по умолчаниюease - как и у transition
animation-timing-function: linear; (на элементе)Вся анимация играет с равномерной скоростью
50% { animation-timing-function: X; }X управляет отрезком ОТ 50% до следующего keyframe
100% { animation-timing-function: X; }Не имеет эффекта - следующего отрезка нет
Кривая не указана на конкретном процентеБерётся значение с элемента (fallback)
transition: animation-duration 1s;Не работает - значение меняется мгновенно, не анимируется

Коротко: отвечаем на вопросы

Я написал animation-timing-function прямо на нужном keyframe, а плавность как будто применяется к предыдущему отрезку. Почему?

Это известная особенность спецификации, которую разработчики называют "confusing API" - кривая, указанная НА каком-то проценте, управляет отрезком ПОСЛЕ него, до следующего keyframe, а не отрезком, который к этому проценту ведёт. Если нужна своя кривая для самого первого отрезка (от 0%), её нужно указывать на элементе или на самом 0% keyframe - а не ждать, что она сработает "задним числом" для пути, который уже был пройден.

Можно ли плавно ускорить или замедлить уже запущенную CSS-анимацию, например через :hover и transition?

Нет - реальный ответ с Хабра подтверждает, что animation-duration не анимируется. То же самое верно и для animation-timing-function: значение меняется мгновенно, без интерполяции, даже если на свойство навешан transition. Собственная проверка показала, что смена длительности с 4s на 1s происходит без единого промежуточного значения. Для настоящего плавного изменения скорости анимации нужен JavaScript, управляющий, например, playbackRate у Web Animations API.

Категория
Поделиться в социальных сетях

Отправить комментарий

0 Комментарии

Отправить комментарий (0)

#buttons=(Хорошо) #days=(20)

Мы используем файлы cookie для улучшения работы сайта. Подробнее
Хорошо
×