Статьи

Как начать работу с CSS-анимацией

Ниже приведен небольшой отрывок из новой книги Тиффани « Мастер CSS», 2-е издание .

Думайте о CSS-анимации как о более сложной сестре переходов CSS. Анимации отличаются от переходов несколькими ключевыми способами:

  • Анимации не ухудшаются изящно. Если нет поддержки от браузера, пользователю не повезло. Альтернатива — использовать JavaScript.
  • Анимации могут повторяться и повторяться бесконечно. Переходы всегда конечны.
  • Анимации используют ключевые кадры, которые предлагают возможность создавать более сложные и детализированные эффекты.
  • Анимации могут быть приостановлены в середине игрового цикла.

Последние версии всех основных браузеров поддерживают CSS-анимацию. Firefox версии 15 и более ранние требуют префикса -moz- ; более поздняя версия не Internet Explorer версий 10 и 11 также поддерживает анимацию без префикса, как и все версии Microsoft Edge.

Мы можем проверить поддержку CSS-анимации несколькими способами. Первый — проверка наличия CSSKeyframeRule как метода объекта window :

 const hasAnimations = 'CSSKeyframeRule' in window; 

Если браузер поддерживает правило CSS.supports() API CSS.supports() , мы можем использовать это вместо этого:

 const hasAnimations = CSS.supports('animation-duration: 2s'); 

Как и в случае переходов, мы можем анимировать только интерполируемые значения, такие как значения цвета, длины и проценты.

Создание вашей первой анимации

Сначала мы должны определить анимацию, используя правило @keyframes . Правило @keyframes имеет две цели:

  • установка названия нашей анимации
  • группирование наших правил ключевых кадров

Давайте создадим анимацию с именем pulse :

 @keyframes pulse { } 

Наши ключевые кадры будут определены в этом блоке. В анимации ключевой кадр — это точка, в которой изменяется действие. В частности, для CSS-анимации правила ключевых кадров используются для установки значений свойств в определенных точках цикла анимации. Значения, попадающие между значениями в правиле ключевого кадра, интерполируются.

Как минимум, анимация требует двух ключевых кадров: ключевой кадр from , который является начальным состоянием для нашей анимации, и кадр, который является его конечным состоянием. В каждом отдельном блоке ключевых кадров мы можем определить, какие свойства анимировать:

 @keyframes pulse { from { transform: scale(0.5); opacity: .8; } to { transform: scale(1); opacity: 1; } } 

Этот код масштабирует наш объект от половины его размера до его полного размера и изменяет непрозрачность с 80% до 100%.

Однако правило keyframes определяет только анимацию. Само по себе это не заставляет элементы двигаться. Мы должны применить это. Давайте также определим pulse класс, который мы можем использовать, чтобы добавить эту анимацию к любому элементу:

 .pulse { animation: pulse 500ms; } 

Здесь мы использовали сокращенное свойство animation чтобы задать имя и продолжительность анимации. Чтобы анимация воспроизводилась, нам нужно имя правила @keyframes@keyframes случае — pulse ) и длительность. Другие свойства не являются обязательными.

Порядок свойств для animation похож на порядок transition . Первое значение, которое может быть проанализировано, становится значением продолжительности animation-duration . Второе значение становится значением для animation-delay . Слова, которые не являются ключевыми словами в масштабе CSS или значениями ключевых слов свойства анимации, считаются @keyframe набора правил @keyframe .

Как и при transition , animation также принимает список анимации. Список анимации представляет собой список значений, разделенных запятыми. Мы могли бы, например, разделить нашу анимацию импульсов на два правила — pulse и fade :

 @keyframes pulse { from { transform: scale(0.5); } to { transform: scale(1); } } @keyframes fade { from { opacity: .5; } to { opacity: 1; } } 

Тогда мы могли бы объединить их как часть одной анимации:

 .pulse-and-fade { animation: pulse 500ms, fade 500ms; } 

Свойства анимации

Хотя использование свойства animation короче, иногда свойства longhand становятся более ясными. Свойства анимации перечислены ниже:

Свойство Описание Первоначальный значение
animation-delay Как долго ждать до запуска анимации 0s (выполняется немедленно)
animation-duration Как долго должен длиться цикл анимации 0s (анимация не выполняется)
animation-name Имя правила @keyframes никто
animation-timing-function Как рассчитать значения между начальным и конечным состояниями ease
animation-iteration-count Сколько раз повторить анимацию 1
animation-direction Должна ли анимация воспроизводиться наоборот normal (без реверса)
animation-play-state Работает ли анимация или приостановлена running
animation-fill-mode Указывает, какие значения свойств применяются, когда анимация не запущена none

Свойства animation-delay и animation-duration функционируют как transition-delay и transition-duration . Оба принимают единицы времени в качестве значения, в секундах ( s ) или миллисекундах ( ms ). Отрицательные значения времени действительны для animation-delay , но не для animation-duration .

Давайте перепишем наш набор правил .pulse используя свойства. Это дает нам следующее:

 .pulse { animation-name: pulse; animation-duration: 500ms; } 

Свойство animation-name довольно просто. Его значением может быть либо none либо имя правила @keyframes . Анимационные имена имеют несколько ограничений. Ключевые слова CSS, такие как initial , inherit , по default и none них запрещены. Большинство знаков препинания не будут работать, в то время как буквы, символы подчеркивания, цифры и символы эмодзи (и другие символы Юникода) обычно работают. Для ясности и удобства сопровождения рекомендуется давать описательным именам анимации и избегать использования свойств CSS или смайликов в качестве имен.

Цикл или не цикл: Свойство animation-iteration-count

Если вы будете следовать своему собственному коду, вы заметите, что эта анимация происходит только один раз. Мы хотим, чтобы наша анимация повторялась. Для этого нам понадобится свойство animation-iteration-count .

Свойство animation-iteration-count принимает большинство числовых значений. Целые числа и десятичные числа являются допустимыми значениями. Однако при использовании десятичных чисел анимация останавливается на полпути последнего цикла анимации, заканчиваясь в состоянии to . Отрицательные значения animation-iteration-count обрабатываются так же, как 1 .

Чтобы анимация работала бесконечно, используйте ключевое слово infinite . Анимация будет проигрываться бесконечное количество раз. Конечно, infinite действительно означает, что до тех пор, пока документ не будет выгружен, окно браузера не закроется, стили анимации не будут удалены или устройство не выключится. Давайте сделаем нашу анимацию бесконечной:

 .pulse { animation-name: pulse; animation-duration: 500ms; animation-iteration-count: infinite; } 

Или, используя сокращенное свойство animation :

 .pulse { animation: pulse 500ms infinite; } 

Воспроизведение анимации: свойство animation-direction

Однако, с нашей анимацией все еще есть проблема. Это не столько пульс, сколько повторение нашей анимации. Мы хотим, чтобы этот элемент увеличивался и уменьшался. Введите свойство animation-direction .

Свойство animation-direction принимает одно из четырех значений:

  • normal : начальное значение, проигрывание анимации, как указано
  • reverse : переворачивает состояния from и to и воспроизводит анимацию в обратном порядке
  • alternate : воспроизводит четные циклы анимации в обратном порядке
  • alternate-reverse : воспроизводит нечетные циклы анимации в обратном порядке

Чтобы продолжить наш текущий пример, reverse уменьшил бы наш объект в 0,5 раза. Использование alternate увеличило бы масштаб нашего объекта для нечетных циклов и уменьшило бы для четных. И наоборот, использование alternate-reverse уменьшило бы наш объект для нечетных циклов и увеличило для четных. Так как это тот эффект, который мы хотим, мы установим для нашего свойства animation-direction значение alternate-reverse :

 .pulse { animation-name: pulse; animation-duration: 500ms; animation-iteration-count: infinite; animation-direction: alternate-reverse; } 

Или, используя сокращенное свойство:

 .pulse { animation: pulse 500ms infinite alternate-reverse; } 

Использование процентных ключевых кадров

Наш предыдущий пример был простой импульсной анимацией. Мы можем создавать более сложные последовательности анимации, используя ключевые кадры в процентах. Вместо использования from и to процентные ключевые кадры указывают конкретные точки изменения в течение анимации. Ниже приведен пример использования анимации с именем wiggle :

 @keyframes wiggle { 25% { transform: scale(.5) skewX(-5deg) rotate(-5deg); } 50% { transform: skewY(5deg) rotate(5deg); } 75% { transform: skewX(-5deg) rotate(-5deg) scale(1.5); } 100% { transform: scale(1.5); } } 

Мы использовали приращения 25%, но эти ключевые кадры могут составлять 5%, 10% или 33,2%. Во время воспроизведения анимации браузер будет интерполировать значения между каждым состоянием. Как и в нашем предыдущем примере, мы можем назначить его селектору:

 /* Our animation will play once */ .wiggle { animation-name: wiggle; animation-duration: 500ms; } 

Или используя сокращенное свойство animation :

 .wiggle { animation: wiggle 500ms; } 

Здесь есть только одна проблема. Когда наша анимация заканчивается, она возвращается к исходному, предварительно анимированному состоянию. Чтобы предотвратить это, используйте свойство animation-fill-mode .

Свойство animation-fill-mode

Анимации не влияют на свойства до их начала или после того, как они перестают играть. Но, как вы видели на примере wiggle , когда анимация заканчивается, она возвращается в состояние до анимации. В режиме animation-fill-mode мы можем заполнить эти состояния до начала и окончания анимации.

Свойство animation-fill-mode принимает одно из четырех значений:

  • none : анимация не действует, когда она не выполняется
  • forwards : когда анимация заканчивается, значения свойств конечного состояния будут по-прежнему применяться
  • backwards : значения свойств для первого ключевого кадра будут применены в течение периода задержки анимации
  • both : применяются эффекты как forwards и backwards

Поскольку мы хотим, чтобы наш анимированный элемент оставался в своем окончательном масштабированном состоянии, мы будем использовать animation-fill-mode: forwards . ( animation-fill-mode: both также будут работать.)

Эффект animation-fill-mode: backwards наиболее очевиден, если для свойства animation-delay установлено значение 500ms или выше. Когда animation-fill-mode установлен в backwards , применяются значения свойств первого ключевого кадра, но анимация не выполняется, пока не истечет задержка.

Приостановка анимации

Как уже упоминалось, анимация может быть приостановлена. Переходы могут быть отменены на полпути или полностью остановлены переключением имени класса. Анимации, с другой стороны, могут быть приостановлены на полпути в цикле воспроизведения с использованием animation-play-state . У него есть два определенных значения — running и paused — и его начальное значение running .

Давайте рассмотрим простой пример использования animation-play-state для воспроизведения или приостановки анимации. Во-первых, наш CSS:

 .wobble { animation: wobble 3s ease-in infinite forwards alternate; animation-play-state: paused; } .running { animation-play-state: running; } 

Здесь у нас есть два блока объявления: wobble , который определяет анимацию колебания, и running , который устанавливает состояние воспроизведения. Как часть нашего объявления animation , мы установили значение animation-play-state paused . Чтобы запустить нашу анимацию, мы добавим класс running к нашему элементу. Давайте предположим, что наша разметка включает кнопку « Запустить анимацию» с id trigger :

 const trigger = document.querySelector('#trigger'); const moveIt = document.querySelector('.wobble'); trigger.addEventListener('click', function() { moveIt.classList.toggle('running'); }); 

Добавление .running к нашему элементу переопределяет значение animation-play-state установленное в .wobble , и вызывает воспроизведение анимации.

Обнаружение, когда анимация начинается, заканчивается или повторяется

Подобно переходам, анимации запускают событие, когда они заканчиваются: animationend . В отличие от переходов, анимации также запускают animationstart и события animationiteration когда они начинают повторяться. Как и в случае переходов, вы можете использовать эти события для запуска другого действия на странице. Возможно, вы бы использовали animationstart для контекстного отображения кнопки « Остановить анимацию» или animationend для открытия кнопки « Воспроизведение» .

Мы можем слушать эти события с помощью JavaScript. Ниже мы слушаем событие animationend :

 const animate = document.getElementById('animate'); animate.addEventListener('animationend', function(eventObject) { // Do something }); 

Здесь также функция-обработчик события получает объект события в качестве единственного аргумента. Чтобы определить, какая анимация закончилась, мы можем запросить свойство animationName объекта события.

Примечание о доступности

Переходы и анимация могут улучшить пользовательский опыт, делая взаимодействие плавным, а не скачкообразным, и в противном случае приносят удовольствие в интерфейс. Но они все еще имеют риски доступности. Например, большие анимации вращения могут вызвать головокружение или тошноту у людей с вестибулярными расстройствами, такими как головокружение [5]. Мигающая анимация может вызвать приступы у некоторых людей с светочувствительной эпилепсией [6]. Используйте их экономно, и настоятельно рекомендуем дать пользователям возможность отключить их. Мы обсуждаем один способ сделать это — медиа-запрос с prefers-reduced-motion — в главе «Условное применение CSS».

Примечание о производительности

Некоторые свойства создают более эффективные переходы и анимацию, чем другие. Если анимация обновляет свойство, которое запускает перекомпоновку или перерисовку, она может плохо работать на устройствах с низким энергопотреблением, таких как телефоны и планшеты.

Свойства, запускающие перекомпоновку, влияют на макет. К ним относятся следующие анимируемые свойства:

  • border-widthborder-*-width свойства border-*-width )
  • border (и свойства border-* )
  • bottom
  • font-size
  • font-weight
  • height
  • left
  • line-height
  • marginmargin-* свойства)
  • min-height
  • min-width
  • max-height
  • max-width
  • padding (и свойства padding-* )
  • right
  • top
  • vertical-align
  • width

Когда эти свойства анимированы, браузер должен пересчитать размер и положение затронутых — и часто соседних — элементов. Используйте трансформации, где вы можете. Переходные или анимационные трансляционные преобразования (например, transform: translate(100px,200px) ) могут заменять свойства top , left , right и bottom . В некоторых случаях анимацию height и width можно заменить преобразованием scale .

Иногда запуск перекомпоновки (или обновления макета) неизбежен. В этих случаях минимизируйте количество затронутых элементов и используйте приемы (например, отрицательные задержки), чтобы сократить воспринимаемую продолжительность анимации.

Свойства, которые вызывают перекрашивание, обычно являются такими, которые вызывают изменение цвета. Это включает:

  • background
  • background-image
  • background-position
  • background-repeat
  • background-size
  • border-radius
  • border-style
  • box-shadow
  • color
  • outline
  • outline-color
  • outline-style
  • outline-width

Изменения в этих свойствах дешевле рассчитать, чем те, которые влияют на макет, но они все же имеют свою стоимость. Изменения параметров box-shadow и border-radius особенно дороги для расчета, особенно для устройств с низким энергопотреблением. Будьте осторожны при анимации этих свойств.