11.01.2017

10 принципов плавной веб-анимации

Как получить заветные 60fps при работе с CSS-анимациями

1-ppaptjxyyiemji8fio88taКак устроена анимация в вебе

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

Для плавности в 60 fps каждый кадр должен быть отрендерен меньше, чем за 16 ms. Это очень мало, поэтому приходится искать способы оптимизации.

Существуют десятки способов веб-анимации. Можно использовать принцип киноленты — кадры меняются в течение секунды и создают иллюзию движения. Твиттер недавно использовал этот подход для анимации лайка.

1-fug1af-xgf0ie6eiuab-fa

Тот же эффект достигается анимированием каждого мелкого элемента по отдельности или с использованием SVG, но это было бы излишне сложно и, возможно, в ущерб плавности.

1-6bgvscgs5cxxqpjn9qqlca

Во многих случаях можно использовать CSS-переходы (CSS transitions) для автоматической анимации элементов. Эта техника называется твининг (tweening) — переход между двумя значениями. Преимущество подхода в простом редактировании — вы можете отменить или сделать обратное действие без необходимости отстраивать всю логику. Идеально для простых понятных эффектов, типа заходной анимации, ховеров и других простых взаимодействий — сделал и забыл!

А вот для зацикленных фоновых элементов идеально подойдет CSS-свойства на основе keyframe (keyframe-based CSS animation property). Например, так сделаны шестеренки в логотипе Gyroscope — они вращаются нон-стоп.

1-dkga2qewb_zi0nnj0m2xpa

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

Полезно по теме (англ.): All you need to know about CSS Transitions

#1 Не трогайте ничего кроме прозрачности и трансформов!

Даже если очень хочется

Один только этот принцип даст 80% эффекта, даже на мобильном устройстве. Наверняка вы уже слышали этот совет, но тем не менее ему редко следуют. Это веб-эквивалент жизненному: «питайся правильно и занимайся спортом» — хороший совет, который все игнорируют.

Это очень просто, если войдет в привычку, но может стать настоящим вызовом для тех, кто привык анимировать в классическом CSS (animating traditional CSS properties).

Например, если нужно уменьшить объект, используйте transform: scale() вместо изменения ширины. Если нужно сдвинуть объект, вместо margin или padding (браузер будет перестраивать весь layout страницы для каждого кадра), возьмите transform: translateX или transform: translateY.

Почему это работает?

Если для нас поменять ширину или отступы — пара пустяков, то компьютер (браузер) при этом проделывает большую и лишнюю работу.

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

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

Полезно по теме (англ.): Moving elements with translate (Paul Irish)

#2 Спрячьте на видном месте

Чтобы скрыть элемент, используйте pointer-events: none вместе с прозрачностью

Тут есть подводные камни, связанные с кросс-браузерностью, но если вы разрабатываете под WebKit и другие современные браузеры, метод существенно облегчит работу.

Давным-давно, когда для анимации еще использовался jQuery animate(), самая большая сложность в эффекте fade in\out состояла при переключении между display: none\display: block в нужное время. Если слишком рано — не увидим окончание эффекта, слишком поздно — получим прозрачный элемент поверх страницы. При этом каждый шаг требовал callback для очистки после финиша анимации.

CSS-свойство pointer-events делает объект полностью неактивным (некликабельными). Можно легко включать-выключать объект через CSS без прерывания анимации и влияния на рендеринг. А в паре с прозрачностью мы получаем тот же эффект, что и от display:none, только без дополнительного рендеринга страницы, а значит без потерь для производительности. Если нужно скрыть элемент — просто поставьте ему opacity: 0 и pointer-events: none.

Особенно хорошо работает для элементов с position: absolute, т.к. они точно не влияют на другие объекты на странице.

Так вы получаете больше свободы действий — тайминги могут не совпадать между собой, не страшно если элемент останется кликабельным на секунду дольше, чем он виден или наоборот станет кликабельным только после полного появления на странице.

#3 Не запускайте всё одновременно

Действуйте как хореограф

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

Распределяйте тайминги так, чтобы анимации не стартовали одновременно. Обычно, 2-3 объекта могут двигаться параллельно и без лагов, особенно если они стартуют с небольшой задержкой относительно друг друга. Если больше — может тормозить.

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

В material design есть интересные решения по этой теме. Не единственный верный путь, но есть над чем подумать и что потестить.

1-l3nlhjxvevs6mwszct34fg

Полезно по теме (англ.): Google Material Design · Motion

#4 Плавно увеличивайте transition delays для правильной координации

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

Обычно я меняю один родительский класс (например в body) и он запускает все transitions, каждый из которых имеет свой динамический transition-delay. Вы просто меняете значение одной переменной, а не десятки таймингов в вашем JS.

Такой своеобразный эффект домино — простой и понятный способ органично скоординировать элементы. Это крутой прием, потому что он и выглядит хорошо, и не слишком ресурсоемкий (помним, что одновременно должны работать 2-3 эффекта).

1-1-ojmr242qurcnke-rlfgq

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

Пример кода

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

1-nyyitmsoxlxoprk7xnqjga

Пример простого SASS цикла

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

1-t5s3eym3rw-zrm8dmrfrqw

Пример JS цикла

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

#5 Используйте глобальную переменную для слоу-мо

А потом всё ускоряйте

В проектировании анимаций тайминги — это все. Сама идея эффекта — это 20% работы. 80% — это поиск нужных комбинаций параметров для синхронной и плавной реализации.

Особенно когда вы «ставите хореографию» одновременно нескольких объектов, пытаясь выжать производительность, очень полезно взглянуть на всю картину в слоу-мо.

Если вы используйте JS или препроцессор типа SASS, будет не сложно сделать немного вычислений и поработать с переменными.

Бывает сложно заметить 5-и миллисекундные лаги на нормальной (полной) скорости, но если замедлить анимацию, то они бросятся в глаза.

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

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

#6 Смотрите видео вашего боевого UI

Иногда взгляд с другой точки зрения позволяет увидеть вещи более ясно, и видео — отличный способ сделать это. Кто-то собирает видео анимации в After Effects, а потом внедряет на сайт. У меня обычно происходит наоборот — делаю видео из анимации на сайте.

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

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

Когда анимация выглядит без лагов на видео и ты готов его запостить, только тогда эффект готов.

#7 Проверьте как загружается контент

Сделать предзагрузку или тормознуть жирные HTTP-запросы

Главный виновник тут — картинки. Будь то один тяжелый фон, 50 аватарок или просто куча контента, которая тянется до самого футера. Кроме того, при первой загрузке страницы, в фоне запускается еще куча всякого: счётчики веб-аналитики, всевозможные библиотеки и сторонние виджеты. Иногда чудеса производительности начинаются, если просто дожидаться загрузки всех компонентов, а потом запускать анимацию (через 100-200ms).

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

Тяжелые страницы действительно требуют «усилий» по загрузке. При этом анимация может хорошо бегать на прототипе со статичным контентом, а вот на боевом проекте с реальными данным начинает лагать. Поэтому когда вам кажется «это точно должно работать» или оно плавно работает в тестовой среде, мой совет — посмотрите на Network Activity в браузере.

#8 Осторожнее со скроллом

Вроде бы круто выглядит (на самом деле нет)

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

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

Но самое ужасное — это ломать стандартное поведение скролла-бара, к которому мы привыкали десятилятиями, что называется scrolljacking. Пожалуйста, не делайте этого никогда. И уж точно не стоит играть со скроллом на мобильной версии.

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

#9 Тестируйте на телефоне как можно раньше и чаще

1-vtk6jzkoccd-mmqspmrbfwПочти все сайты делаются на компьютере, соответственно там же они и тестируются в первую очередь. Мобильной версии уделяется внимание по остаточному принципу. Но часть технологий (canvas) или приемов веб-анимации просто не смогут также хорошо работать на телефоне.

При этом, если код грамотно оптимизирован (см. принцип #1), мобильная версия может работать даже плавнее, чем десктопная. Когда-то оптимизация под телефон была очень сложна из-за низкой производительности устройств, а сегодня последний iPhone работает быстрее, чем большинство рядовых ноутбуков. Поэтому если следовать советам выше, скорее всего вы по умолчанию получите хорошую производительность на мобильном.

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

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

#10 Постоянные тесты на устройствах

Размер экрана, плотность пикселей и сами устройства

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

Хотя Chrome и Safari оба сделаны на Webkit, каждый браузер имеет свои собственные капризы. Обновление Chrome может фиксить старые баги и в то же время — добавить новых. Поэтому с браузерами всегда нужно быть начеку.

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

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

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

1-tf_ssjmgapkjcwwa0b201g

Перевод статьи: 10 principles for smooth web animations (Anand Sharma)