animation-play-state ставит анимацию на паузу и снимает с неё паузу - по умолчанию running (анимация играет). paused замораживает анимацию ровно в текущей точке прогресса, а не сбрасывает её: возобновление продолжает с той же самой точки, а не с начала. Это же свойство можно переключать через :hover или через JavaScript.
Попробуй прямо сейчас: квадрат стоит на месте, пока курсор не наведён (animation-play-state: paused на базовом селекторе), и крутится, только пока курсор над ним (:hover переключает на running). Убери курсор ПОСРЕДИ вращения, подожди немного и наведи снова - вращение продолжится РОВНО с той точки, где остановилось, а не начнётся заново от 0 градусов, потому что имя анимации (spin) всё это время остаётся подключённым на базовом селекторе - меняется только play-state.
Пауза и возобновление: позиция сохраняется
По умолчанию animation-play-state равен running - анимация просто играет. Значение paused замораживает её РОВНО в той точке прогресса, где она была в момент паузы - собственная проверка подтвердила: currentTime практически не сдвигается, сколько бы реального времени ни прошло, пока стоит пауза. Когда снова ставится running, анимация продолжает С ТОЙ ЖЕ ТОЧКИ, а не перезапускается с начала. Менее очевидный момент, тоже проверенный: если поставить паузу ДО того, как активная фаза анимации вообще началась (то есть во время animation-delay), замораживается и сам ОТСЧЁТ ЗАДЕРЖКИ - анимация не "тайком" продолжает отсчитывать секунды задержки в фоне, пока стоит на паузе.
.loader {
animation-name: spin;
animation-duration: 1s;
animation-iteration-count: infinite;
animation-play-state: paused;
}
/* JS: loader.style.animationPlayState = 'running'; */ - анимация ждёт на месте, пока JS явно не запустит её.
Неправильное ожидание - что снятие паузы перезапускает анимацию:
/* поставили paused на 50% прогресса,
потом вернули running, ожидая,
что анимация начнётся заново с 0% */ - на самом деле она продолжит именно с 50%, а не с начала.
Практика: пауза при наведении курсора
Самый частый приём - анимация играет только пока на неё наведён курсор. Работает это так: animation-name (и вся анимация) остаётся на БАЗОВОМ селекторе, а :hover переключает только animation-play-state с paused на running. Собственная проверка подтвердила: на всём протяжении наведения-и-ухода курсора getAnimations() показывал РОВНО ОДИН и тот же объект анимации - она не пересоздаётся, поэтому и позиция не теряется. Это принципиально ОТЛИЧАЕТСЯ от ловушки урока про animation-fill-mode: там animation-name был назначен ТОЛЬКО внутри .el:hover, и при уходе курсора вся анимация снималась целиком вместе с forwards-состоянием. Здесь наоборот - именно то, что animation-name живёт на базовом селекторе, а меняется только play-state, и делает приём рабочим.
.icon {
animation-name: spin;
animation-duration: 2s;
animation-iteration-count: infinite;
animation-play-state: paused;
}
.icon:hover {
animation-play-state: running;
} - animation-name на базовом селекторе, на :hover меняется только play-state - позиция сохраняется между наведениями.
Ловушка предыдущего урока для сравнения (не делай так, если важна память позиции):
.icon:hover {
animation-name: spin;
animation-duration: 2s;
animation-iteration-count: infinite;
} - вся анимация только на :hover: при уходе курсора она снимается целиком, а не просто ставится на паузу.
Частые ошибки новичков
| Ошибка | Что происходит | Как исправить |
|---|---|---|
| Забыть, что animation-play-state по умолчанию running | Анимация просто крутится без паузы, если явно не указать paused | Указывать paused на базовом селекторе, если анимация должна ждать запуска |
| Ожидать, что снятие паузы перезапускает анимацию с начала | На самом деле она продолжает с ТОЙ ЖЕ точки прогресса, где была поставлена пауза | Учитывать это при проектировании интерфейса - для перезапуска с нуля нужен другой приём (например смена animation-name) |
| Считать, что пауза во время animation-delay ничего не замораживает | На самом деле замораживается и сам отсчёт задержки, а не только активная фаза | Учитывать это, если задержка используется для тайминга нескольких анимаций |
| Вешать всю анимацию (animation-name) только на :hover ради эффекта паузы | При уходе курсора анимация снимается ЦЕЛИКОМ (та же ловушка, что в уроке про animation-fill-mode), а не просто останавливается | Держать animation-name на базовом селекторе, переключать через :hover только animation-play-state |
Шпаргалка
| Запись | Что означает |
|---|---|
| animation-play-state: running; (по умолчанию) | Анимация играет как обычно |
| animation-play-state: paused; | Заморожена ровно в текущей точке прогресса |
| paused → running | Продолжает с той же точки, а не с начала |
| paused во время animation-delay | Замораживает и сам отсчёт задержки |
| .el{play-state:paused} .el:hover{play-state:running} | Играет только при наведении, позиция сохраняется между наведениями |
| el.style.animationPlayState='paused'; | То же самое, но управление через JavaScript |
Коротко: отвечаем на вопросы
Как вообще остановить CSS-анимацию?
Использовать свойство animation-play-state и установить ему значение paused - например через JS-класс, который добавляется по нужному событию. Анимация замрёт ровно в той точке, где была на момент остановки, а не сбросится в начало.
Можно ли поставить на паузу вообще ВСЕ анимации на странице сразу, например пока не загрузились ресурсы?
Да, это реальный приём с Хабра: пока на body висит служебный класс (например js-loading), общее правило вроде body.js-loading * { animation-play-state: paused !important; } замораживает абсолютно все анимации на странице, а JavaScript снимает этот класс, когда сработало событие load. Работает и с задержками - если у какой-то анимации ещё не кончился animation-delay, пауза заморозит и его тоже.