Как работает JS: анимация средствами CSS и JavaScript

https://blog.sessionstack.com/how-javascript-works-under-the-hood-of-css-and-js-animations-how-to-optimize-their-performance-db0e79586216
  • Перевод
Анимация — неотъемлемая часть современных веб-интерфейсов. От того, насколько она уместна, привлекательна и производительна, зависит немалая доля впечатлений пользователя от работы с сайтом или веб-приложением. Сегодня, в переводе тринадцатой части серии материалов, посвящённых особенностям JavaScript и связанных с ним технологий, мы поговорим об анимации, выполняемой средствами CSS и JS, а также обсудим подходы к её оптимизации.


Обзор


Наверняка вы знаете о том, что анимация, помимо чисто утилитарной роли, оказывает огромное влияние на привлекательность веб-приложений. Пользователи всё больше и больше обращают внимание на UX-дизайн проектов, что приводит к тому, что владельцы веб-ресурсов приходят к осознанию важности создания на своих сайтах таких условий, которые позволили бы пользователям комфортно там себя чувствовать. Всё это, помимо визуальной привлекательности страниц и удобства работы с ними, ведёт к тому, что веб-проекты становятся «тяжелее», к тому, что в их интерфейсах растёт число динамических элементов. Всё это требует более сложных анимаций, которые, например, позволяют организовать плавное изменение состояния веб-страниц в ходе работы пользователя с ними. Сегодня анимация не считается чем-то особенным. Пользователи становятся требовательнее, они уже привыкли ожидать наличия у веб-проектов интерактивных отзывчивых интерфейсов.

Однако, анимация интерфейсов — это не так уж и просто. Что анимировать? Когда анимировать? Какие ощущения должна вызывать анимация? Поиск ответов на эти вопросы может потребовать немалых усилий.

JavaScript-анимация и CSS-анимация


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

▍CSS-анимация


CSS-анимация — это самый простой способ заставить что-либо двигаться по экрану. Начнём с простого примера, демонстрирующего перемещение элемента по осям X и Y. Делается это с помощью CSS-трансформации translate, которая настроена на длительность в 1000 мс.

.box {
  -webkit-transform: translate(0, 0);
  -webkit-transition: -webkit-transform 1000ms;

  transform: translate(0, 0);
  transition: transform 1000ms;
}

.box.move {
  -webkit-transform: translate(50px, 50px);
  transform: translate(50px, 50px);
}

При добавлении класса move значение transform меняется и начинается переход. Помимо длительности, мы можем настраивать динамику анимации (easing). Сущность этой настройки сводится к тому, что она влияет на то, как пользователь воспринимает анимацию. О динамике анимации мы поговорим позже.

На иллюстрации ниже показана поддержка CSS-переходов современными браузерами.


Поддержка CSS-переходов современными браузерами

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

Если, как и в предыдущем фрагменте кода, вы создадите отдельные CSS-классы для управления анимацией, затем можно включать и отключать анимацию средствами JavaScript.

Предположим, имеется следующий элемент.

<div class="box">
  Sample content.
</div>

С помощью JavaScript можно запускать и останавливать его анимацию.

var boxElements = document.getElementsByClassName('box'),
    boxElementsLength = boxElements.length,
    i;

for (i = 0; i < boxElementsLength; i++) {
  boxElements[i].classList.add('move');
}

В этом фрагменте кода мы берём все элементы, которым назначен класс box и добавляем к ним класс move для того, чтобы запустить их анимацию.

Подобные возможности совместного использования CSS — для описания анимаций, и JS — для её запуска и отключения, делают приложение хорошо сбалансированным. Разработчик может сосредоточиться на управлении состоянием элементов из JavaScript, просто назначая подходящие классы целевым элементам, позволяя браузеру самостоятельно выполнять анимации, описанные средствами CSS. Если углубиться в подобный сценарий работы с анимацией, то можно прослушивать событие transitionend элемента, но делать так стоит лишь при условии поддержки старых версий Internet Explorer.

Событие transitionend вызывается в конце перехода. Вот как с ним работать.

var boxElement = document.querySelector('.box'); // Получить первый элемент с классом box.
boxElement.addEventListener('transitionend', onTransitionEnd, false);

function onTransitionEnd() {
  // Обработать завершение перехода.
}

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

Ключевые кадры используются для того, чтобы сообщить браузеру о том, какие значения CSS-свойства должны иметь в заданные моменты. Браузер самостоятельно находит промежуточные значения для свойств при переходе от одного ключевого кадра к другому.

Рассмотрим пример.

/**
 * Это - упрощённая версия без префиксов
 * разработчиков браузеров. Если включить их сюда
 * (а в реальном коде это нужно), объём кода
 * значительно возрастёт!
 */
.box {
  /* Выберем анимацию */
  animation-name: movingBox;

  /* Укажем длительность анимации */
  animation-duration: 2300ms;

  /* Укажем - сколько раз мы хотим
      повторить анимацию */
  animation-iteration-count: infinite;

  /* Это приводит к выполнению анимации
      в обратном порядке на каждой нечётной итерации */
  animation-direction: alternate;
}

@keyframes movingBox {
  0% {
    transform: translate(0, 0);
    opacity: 0.4;
  }

  25% {
    opacity: 0.9;
  }

  50% {
    transform: translate(150px, 200px);
    opacity: 0.2;
  }

  100% {
    transform: translate(40px, 30px);
    opacity: 0.8;
  }
}

Вот страница, на которой показана работа этого кода.

Применяя CSS-анимации, саму анимацию описывают независимо от целевого элемента, а затем используют свойство animation-name для выбора необходимой анимации.

CSS-анимации, до сих пор, иногда требуют использования префиксов разработчиков браузеров. Так, префикс -webkit- используется в браузерах Safari, Safari Mobile, и в браузере Android. В браузерах Chrome, Opera, Internet Explorer, и Firefox анимации работают без префиксов. Для того чтобы создать CSS-код с префиксами, можно воспользоваться множеством вспомогательных инструментов, что позволяет разработчику, в исходном коде анимаций, обходиться без префиксов.

▍JavaScript-анимация


Создавать анимации средствами JavaScript, с применением API веб-анимации, сложнее, чем использовать CSS-переходы и CSS-анимации, но этот подход обычно даёт разработчику гораздо большие возможности.

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

var boxElement = document.querySelector('.box');
var animation = boxElement.animate([
  {transform: 'translate(0)'},
  {transform: 'translate(150px, 200px)'}
], 500);
animation.addEventListener('finish', function() {
  boxElement.style.transform = 'translate(150px, 200px)';
});

По умолчанию применение API веб-анимации модифицирует лишь внешний вид элемента. Если требуется, чтобы объект оставался в той позиции, в которую он был перемещён в ходе анимации, нужно, по завершении анимации, модифицировать его стиль. Именно поэтому в вышеприведённом примере мы прослушиваем событие finish и устанавливаем свойство элемента box.style.transform в значение translate(150px, 200px), которое выражает то же самое, что было сделано с объектом с помощью второй трансформации, выполненной средствами JS.

При использовании JavaScript-анимации разработчик обладает полным контролем над стилями элемента на каждом этапе анимации. Это означает, что анимацию можно замедлять, приостанавливать, останавливать, обращать, как угодно манипулировать элементами. Это особенно полезно в ходе создания сложных приложений, при разработке которых используется объектно-ориентированный подход, так как поведение элементов можно должным образом инкапсулировать.

Динамика анимации


Естественные перемещения объектов дают пользователям ощущение комфорта при работе с веб-приложениями, что ведёт к более качественному пользовательскому опыту.

Если говорить о том, как объекты движутся в реальном мире, то можно отметить, что они, например, не перемещаются линейно. В физическом мире движущиеся предметы ускоряются и замедляются, так как на них воздействуют самые разные факторы окружающей среды. Человеческий мозг приучен ожидать от объектов подобных перемещений, поэтому, анимируя веб-приложения, следует это учитывать.

Вот пара терминов, которые пригодятся нам при разговоре о динамике анимации. А именно, поговорим о так называемых функциях плавности. Их применение позволяет влиять на динамику анимации.

  • ease-in — это функция, при применении которой сначала анимация производится медленно, а затем — постепенно ускоряется.
  • ease-out — это функция, при использовании которой анимация начинается быстро, а потом — постепенно замедляется.

Эти функции можно комбинировать. В результате может, например, получиться функция ease-in-out.

Управление динамикой анимации позволяет сделать так, чтобы движение объектов воспринималось как более естественное.

▍Ключевые слова для управления динамикой анимации


CSS-переходы и анимации позволяют разработчику выбирать функции плавности. Существуют различные ключевые слова, воздействующие на динамику анимации. Кроме того, можно создавать и собственные функции плавности. Вот несколько ключевых слов, которые можно использовать в CSS для управления динамикой анимации:

  • linear
  • ease-in
  • ease-out
  • ease-in-out

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

▍Анимация linear


Ключевое слово linear позволяет использовать линейную анимацию. Фактически, эта анимация описывается линейной функцией, при применении которой объект анимируется с постоянной скоростью, без ускорений и замедлений.

Вот как выглядит график линейного CSS-перехода.


Анимация linear

Видно, что с течением времени значение равномерно возрастает. Линейные перемещения, однако, воспринимаются как неестественные. В целом, надо отметить, что подобных анимаций стоит избегать.

Вот как выглядит описание такой анимации:

transition: transform 500ms linear;

▍Анимация ease-out


Как уже было сказано, применение функции ease-out приводит к высокой скорости анимации в начале процесса (её скорость — выше, чем при применении линейной функции), которая замедляется в конце анимации. Вот как выглядит графическое представление такой анимации.


Анимация ease-out

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

Существует множество способов достичь подобного эффекта, но самый простой — воспользоваться ключевым словом ease-out в CSS:

transition: transform 500ms ease-out;

▍Анимация ease-in


Эта анимация противоположна той, которую мы только что рассмотрели. Для неё характерна низкая скорость в начале и увеличение скорости в конце. Вот её графическое представление.


Анимация ease-in

В сравнении с анимацией ease-out, анимация ease-in выглядит необычно, так как она даёт ощущение низкого уровня отзывчивости элемента из-за медленного начала. Ускорение в конце так же создаёт странные ощущения, так как скорость анимации с течением времени растёт, в то время как объекты в реальном мире, перед остановкой, обычно снижают скорость.

Для того чтобы воспользоваться этой анимацией, аналогично предыдущим, можно использовать ключевое слово ease-in:

transition: transform 500ms ease-in;

▍Анимация ease-in-out


Эта анимация является комбинацией анимаций ease-in и ease-out. Вот как выглядит её график.


Анимация ease-in-out

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

Воспользоваться этой анимацией можно с помощью ключевого слова ease-in-out:

transition: transform 500ms ease-in-out;

▍Создание собственных функций плавности


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

На самом деле, за ключевыми словами, о которых мы говорили выше (ease-in, ease-out, linear), стоят кривые Безье, подробности о применении которых для управления анимацией можно почитать здесь и здесь. Уделим им некоторое время, так как именно на них основано создание собственных функций плавности.

▍Кривые Безье


Для построения кривой Безье нужно четыре значения, или, говоря точнее — две пары чисел. Каждая пара описывает координаты X и Y опорной точки кубической кривой Безье. Сама кривая начинается в координате (0, 0), а заканчивается — в координате (1, 1). Настраивать можно свойства опорных точек. Значения X координат опорных точек должны находиться в диапазоне [0 ,1], значения Y также должны попадать в диапазон [0, 1], хотя надо отметить то, что спецификации не вполне проясняют этот момент.

Даже небольшое изменение значений X и Y координат опорных точек приводит к значительному изменению кривой. Взглянем на пару графиков кривых Безье, опорные точки которых имеют очень близкие, но различающиеся координаты.


Первая кривая Безье


Вторая кривая Безье

Как видите, два этих графика очень сильно отличатся друг от друга. Вот как выглядит описание второй кривой в CSS:

transition: transform 500ms cubic-bezier(0.465, 0.183, 0.153, 0.946);

Первые два числа — это координаты X и Y первой опорной точки, вторая пара — координаты второй.

Оптимизация производительности анимаций


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

Как и за всё остальное в этом мире, за анимацию надо платить. При этом анимирование некоторых свойств обходится «дешевле», чем анимирование других. Например, анимирование свойств width и height элемента приводит к изменению его геометрии и может привести к тому, что другие элементы на странице переместятся или изменят размер. Этот процесс называется формированием макета страницы. Об этом мы говорили в одном из предыдущих материалов.

В целом, следует избегать анимации свойств элементов, которые вызывают изменение макета страницы или её перерисовку. Для большинства современных браузеров это означает ограничение анимациями opacity и transform.

▍CSS-свойство will-change


CSS-свойство will-change можно использовать для того, чтобы сообщать браузеру о том, что мы намереваемся изменить свойство элемента. Это позволяет браузеру заранее, до выполнения анимации, применить подходящие оптимизации. Однако не стоит злоупотреблять свойством will-change, так как это приведёт к нерациональной трате ресурсов браузера, что, в свою очередь, приведёт к проблемам с производительностью.

Вот, например, как добавить это свойство для анимаций transform и opacity:

.box {
  will-change: transform, opacity;
}

Данное свойство понимают пока не все браузеры, но, в браузерах Chrome, Firefox и Opera его поддержка имеется.


Поддержка CSS-свойства will-change

▍JavaScript или CSS?


Что выбрать для анимации — API веб-анимации, вызываемое из JS, или CSS? Вероятно, вы помните, что выше мы говорили, что на подобный вопрос нельзя дать однозначного ответа. Однако, для того, чтобы всё-таки определиться с технологией, учтите следующие соображения:

  • CSS-анимации и веб-анимации, при наличии их нативной поддержки, обычно обрабатываются потоком композиции (compositor thread). Он отличается от главного потока браузера (main thread), где выполняются задачи по стилизации элементов, по формированию макета, по выводу данных на экран и по выполнению JS-кода. Это означает, что если браузер выполняет какие-то сложные задачи в главном потоке, анимации будут выполняться нормально, без перерывов.
  • Анимации transforms и opacity могут быть, во многих случаях, обработаны потоком композиции.
  • Если какая-то анимация вызывает перерисовку страницы или изменение макета, поработать придётся главному потоку. Это справедливо и для CSS-анимаций, и для JS-анимаций. Дополнительная нагрузка на систему, вызванная изменением макета или перерисовкой страницы, вероятно, замедлит выполнение задач, решаемых средствами CSS или JavaScript, ставя систему в непростое положение.

▍Выбор объектов для анимации


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

▍Использование анимаций для поддержки взаимодействия с пользователем


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

▍Анимации, вызывающие большую нагрузку на систему


Хуже чем неуместная анимация может быть только анимация, которая «подвешивает» страницу. Пользователям любого веб-проекта такое точно не понравится.

Итоги


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

Предыдущие части цикла статей:

Часть 1: Как работает JS: обзор движка, механизмов времени выполнения, стека вызовов
Часть 2: Как работает JS: о внутреннем устройстве V8 и оптимизации кода
Часть 3: Как работает JS: управление памятью, четыре вида утечек памяти и борьба с ними
Часть 4: Как работает JS: цикл событий, асинхронность и пять способов улучшения кода с помощью async / await
Часть 5: Как работает JS: WebSocket и HTTP/2+SSE. Что выбрать?
Часть 6: Как работает JS: особенности и сфера применения WebAssembly
Часть 7: Как работает JS: веб-воркеры и пять сценариев их использования
Часть 8: Как работает JS: сервис-воркеры
Часть 9: Как работает JS: веб push-уведомления
Часть 10: Как работает JS: отслеживание изменений в DOM с помощью MutationObserver
Часть 11: Как работает JS: движки рендеринга веб-страниц и советы по оптимизации их производительности
Часть 12: Как работает JS: сетевая подсистема браузеров, оптимизация её производительности и безопасности

Уважаемые читатели! Сталкивались ли вы когда-нибудь со случаями, когда анимация по-настоящему мешает работать с каким-нибудь веб-ресурсом?

  • +20
  • 15,9k
  • 8
RUVDS.com 649,85
RUVDS – хостинг VDS/VPS серверов
Поделиться публикацией
Комментарии 8
    0
    Статья интересная, но:
    хочу анимированного зайца!!! =)
      +1
      var в 2018…
        +2
        Ну и что? Вот пишешь такой себе let blabla, а потом раз и все через babel прогоняешь, и в результате везде стоят var. Хоть на дворе и 2018, но браузеры выполняют js код, скомпилированный под стандарт пятилетней давности, если не более.
          0
          Вот пример с методом animate из статьи можно и не прогонять через babel. Браузерная поддержка метода animate куда меньше чем поддержка let/const в 2018. Ну и плюс пример это же не скомпилированный код.
            +1
            Ставишь нормальный пресет, и ничего в var не транспайлится. Кто в 2018 году не умеет в const и let? По моему только легаси браузеры, нет?
              –1
              Некоторые мобильные браузеры не понимают всех новшеств, и ничего тут не поделаешь. Всякие не новые сафари и тд.
          +2

          Глубина статьи в этот раз не на должном уровне, я как-то ожидал, что покажут сишный код и подробно объяснят про слои и внутреннюю магию...

            0
            Спасибо, за статью, немножко освежил знания.
            *По поводу вопроса автора про торможение анимации. Сталкивался. Вот мой вопрос на стековерфлоу. Лично я жду когда браузеры внедрят свойства composite для анимации (Почитать можно тут) Также хочу задать вопрос — в каких случаях вы сталкивались когда без JS невозможно реализовать анимацию?(canvas и WebGL не в счет, там всё намного сложнее) Просто приведенные примеры настолько примитивны, что вгоняют в тоску. (Зачем там JS ?)

            Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

            Самое читаемое