Введение в SVG-анимации для верстальщиков

e0ksv-v8kqcvsigvfev1-ve3mrg.jpeg

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

SVG в контексте фронтенда существует уже довольно давно. Можно даже сказать, что «оно было всегда». Хотя еще несколько лет назад мало кто активно использовал SVG в контексте интерфейсов, каких-то визуальных эффектов, анимаций и.т.д. В основном все крутилось вокруг векторных иконок. Это было время, когда люди вообще еще плохо понимали, как верстать что-то нестандартное, в JS еще не было классов, а проблемы кроссбраузерности стояли очень остро. Тогда на CodePen можно было выложить простой пример с каким-то техническим приемом использования того или иного инструмента и он моментально расходился в народе — все учились делать такую же фишку. Эх, вот были времена! Но ничего не стоит на месте, flash закончился, IE переобулся, направление креативной верстки более-менее устаканивается на новом стеке, и мы можем собрать какие-то списки типовых штук, которые хорошо уметь делать. Текущий материал будет полностью посвящен одной из тем — анимированию SVG во фронтенде. Посмотрим, какие есть варианты анимирования, какие возможности самого формата часто применяются, и какие есть вспомогательные инструменты.


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

А теперь приступим.


Векторная графика = инструкции по рисованию

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

Для того, чтобы описывать содержимое SVG-картинки есть свой синтаксис, очень похожий на HTML: теги, атрибуты — все как всегда. Собственно XML-основа у них одна и та же. Как мы потом увидим — можно вставлять одно в другое. Например вот так мы описываем, что на картинке есть четыре круга с определенными координатами, радиусом и цветом:


    
    
    
    

В целом по степени читаемости очищенный от мусора SVG код сравним с HTML. Тут можно организовать все элементы в группы, сделать осмысленные id, классы «как в БЭМ», и.т.д.

Более подробно со списками тегов и атрибутов можно познакомиться на MDN. Там слишком много всего, чтобы описывать каждый момент в рамках статьи — https://developer.mozilla.org/en-US/docs/Web/SVG. Мы же сосредоточимся на практической стороне вопроса, и посмотрим, а как этот теоретический функционал вписывается в реальность.


Интегрируем SVG в страницу

Но для начала нужно интегрировать SVG в страницу. Мы не в вакууме работаем. И есть два направления, как это можно сделать. Первое — код SVG можно вставить прямо в HTML.

Вы можете спросить:, а это получается, что можно использовать CSS и JS и работать с SVG-элементами так же, как и с любыми другими элементами на странице? — Да. Именно так. Правда с некоторыми оговорками. Иногда внутри SVG что-то все же будет работать не так, как на остальной странице. К этому еще вернемся. Такой подход в контексте анимаций удобен прежде всего тем, что мы можем все контролировать: мы можем легко связать любые события и любые стили на странице с тем, что происходит в SVG, так как это та же самая страница.

Разумеется, есть инструменты для любых сборщиков, которые подставляют код SVG картинок в нужные места страниц при их сборке, так что можно работать с картинками как с отдельными файлами, а на выходе иметь все в одном. Они гуглятся на любой вкус и цвет по запросу «название сборщика + inline svg». А многие шаблонизаторы и сами все умеют, ничего дополнительно прикручивать не нужно.


Важный момент: при вставке SVG-картинок в страницу, они все попадают в одну область видимости. Если мы используем id в них, то будет шанс, что они продублируются и начнется магия. За этим стоит следить при подготовке картинок к анимированию и добавлять какие-то префиксы к id. Или придумывать что-то с shadow DOM, чтобы все изолировать. Тут уже все в зависимости от процессов в проекте.

Второй вариант — использовать SVG как отдельный файл. Это может быть картинка

А может быть и не картинка. Можно вставить SVG с помощью тегов object или даже iframe. Она может быть и фоном в CSS. Но во всех этих случаях речь будет идти про сторонний файл. Отдельный от нашей страницы. И доступ к нему будет в любом случае урезанным.

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


Стоит помнить, что в контексте SPA никто не мешает загружать большие SVG скриптами и подставлять их код в HTML по мере необходимости. Так можно совместить лучшее из обоих миров — маленький изначальный HTML и полный доступ ко всему внутри SVG в конечном счете.


CSS + SMIL + JS

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

На этом можно было бы и закончить, но мы раскроем тему неного шире, т.к. определенно есть люди, которым это будет полезно. В контексте фронтенда есть три технологии, которые дают нам возможность анимировать SVG: CSS, JS и SMIL. У каждой есть свои возможности и ограничения, так что мы познакомимся со всеми.


Анимирование SVG с помощью CSS

CSS-анимации — это самый простой вариант. Все мы с ними работали в «обычной» верстке. Они хорошо подходят для простых движений или изменений цвета элементов на : hover/: focus. Или по навешиванию классов-состояний. В основном в контексте SVG они используются именно для иконок или каких-то небольших деталей в элементах интерфейса. Там как раз что-то такое незамысловатое обычно и нужно. Простая задача — простой инструмент.

В CSS у нас есть два варианта анимирования свойств элементов — transition и keyframes. Если вы не знаете, что такое transition и keyframes, то лучше притормозить, сходить на MDN и почитать про них — раз и два.

В целом логика работы CSS-анимаций в контексте SVG принципиально не отличается от HTML, просто добавляются специфичные для SVG свойства у элементов.


Как и в случае с HTML, в SVG не все атрибуты являются анимируемыми со стороны CSS, поэтому упомянутые выше технологии для анимирования SVG не являются полностью взаимозаменяемыми даже в условно простых задачах. Хотя если большую часть времени придерживаться стандартной для CSS логики «меняем только transform и opacity», то можно этого и не заметить.

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

...

Формально по стандарту это делать не обязательно, все будет работать и без него, но по факту — порой такая мелочь сильно упрощает работу, отключая некоторые особо умные оптимизации браузеров. Но это не серебряная пуля. У нас на сегодняшний день нет 100% рабочего способа сказать всем браузерам, что вот этот код должен исполняться в соответствии с логикой мира CSS, а вот тот код — в соответствии с логикой мира SVG.

Поначалу ломают мозг новые единицы измерения. Точнее безразмерные координаты в SVG, которые добавляются к привычным единицам измерения в CSS. Но к этому быстро привыкаешь. Это не проблема. Действительно хороший пример такой двойственности, который путает новичков в анимировании — это трансформации. Да и не только новичков он путает. Создатели стандартов до сих пор не могут определиться со всеми деталями, хотя уже лет десять обсуждают, если не больше. В CSS есть transform, и в SVG есть transform. И там, и там, есть набор базовых трансформаций — translate, rotate, scale и skew. И интуитивно кажется, что все там должно работать одинаково, не так ли?

Здесь есть два основных вопроса. Первый состоит в том, что transform-origin в мире SVG всегда находится в координатах [0,0] всей координатной плоскости картинки. Не самого элемента, к которому трансформация применяется, а именно всей плоскости. Соответственно для поворота какого-то элемента в SVG сцене вокруг произвольной точки, нужно все сдвинуть в центр координат, совместив точку с ним, повернуть, и потом сдвинуть обратно. Если вы хоть немного касались области компьютерной графики, то, вероятно, видели нечно похожее, только другими словами, а вот для простых верстальщиков эта концепция оказывается чем-то новым и непонятным. В CSS все это скрыто от наших глаз:

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

И еще трансформации на SVG-элементах складываются в стек. Можно несколько раз применить к элементу одни и те же трансформации. Подвинуть, повернуть, еще подвинуть, еще повернуть, и.т.д. А потом они применяются в обратном порядке. А CSS так не умеет. Там можно применить только по одной трансформации каждого базового типа к элементу. Иногда это приводит к странному поведению элементов, так что нужно быть внимательным с этой нестыковкой и не нагромождать разную логику трансформаций на один элемент.


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

Инструменты для отладки CSS анимаций в браузерах работают с SVG. Если нужно отладить движения, замедлить, покрутить анимацию туда-сюда — все есть:

8um4nmiqhhethjhivwr07s8--ui.jpeg

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

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


Анимирование SVG с помощью SMIL

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


SMIL описывается в отдельной от SVG спецификации, так что если вы пользуетесь W3C-валидатором, то не забывайте выбирать, на какие документы из списка document type ему нужно ориентироваться. Если не указать, то будет много ложных сообщений об ошибках из-за «несуществующих» тегов и атрибутов.

Основных тегов здесь три:


  1. animate — для плавных изменений значений атрибутов.
  2. animateTransform — по сути то же самое, но только для трансформаций.
  3. animateMotion — для движения объектов по кривым.

Еще есть тег set, с помощью которого можно резко заменить какое-то значение на другое. Это не совсем про анимации, но такое тоже есть.

Наличие в каком-то смысле дублирующих друг друга тегов animate и animateTransform, а еще устаревшего animateColor, само по себе намекает на то, что технология прошла долгий и тернистый путь в своем развитии, и содержит в себе то наследие из времен, когда flash был жив. Но несмотря на это использовать SMIL в простых задачах не так уж и сложно, разве что копипасты обычно получается довольно много:

Мы не будем здесь разбирать все возможные атрибуты у этих тегов, если кому-то интересно, то есть хорошее введение в SMIL на CSS-tricks: https://css-tricks.com/guide-svg-animations-smil/.

Если обобщить, то можно делать:


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

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

Идея всего этого сама по себе была неплохая. Но что-то пошло не так. Я не знаю всех деталей, думаю никто не знает, но технология долго находилась в каком-то непонятно-экспериментальном статусе и сдохла несколько лет назад. Тогда ее не поддерживал IE, в Firefox и Safari была куча проблем, в Chrome вроде что-то было, но стало deprecated. Появились рекомендации от разработчиков разных браузеров на тему перехода со SMIL на CSS или JS. Многие пророчили, что SMIL отправится по пути flash на свалку истории. А потом все забили. А потом IE превратился в Edge, Edge сменил движок, и внезапно оказалось, что SMIL «работает» во всех вечнозеленых браузерах. Вечнозеленый зомби, если можно так сказать.

На сегодняшний день SMIL вроде как бы действительно работает. Так что использовать можно. Но технология не развивается. Соответственно есть много вопросов по практическому использованию.

Во-первых — это кроссбраузерность. Технология, конечно, работает, но проблемы на этом поле никто не исправляет. Например не все браузеры умеют использовать значения больше 1 в кривых Безье. Да, в 2022 году. И таких глупостей там предостаточно. И есть большой затык в том, что инструменты разработчика в этих самых браузерах тоже не имеют нормальной связи с этой технологией. У нас даже нет адекватных линтеров, а от формального W3C-валидатора пользы не очень то и много. Поэтому отладка порой становится очень болезненной — нужно заранее знать, куда смотреть, и высматривать ошибки глазами в коде. Это занятие очень на любителя, особенно в крупных сценах.

Если мы почему-то используем SVG в виде стороннего файла, то возникает вопрос кеширования. Обычно люди считают, что кеширование — это хорошо. Но загвоздка в том, что состояния анимаций тоже кешируются в браузере. Если анимация закончилась, то при перезагрузке страницы есть ненулевая вероятность, что она не начнется заного. Не раз видел верстальщиков, у которых при верстке с вебпаком все работает, а потом при раскатывании сайта на серьезном сервере — начинаются какие-то залипания анимаций. Это в теории решается со стороны сервера, на уровне заголовков, запрещающих кеширование, но на деле оно как-то не всегда работает. Плюс мы завязываем какие-то визуальные штуки на конфигурацию сервера, что само по себе выглядит костылем. Можно все решить на клиенте добавлением случайных параметров в url картинки, чтобы браузер ее перезагружал каждый раз. Но это тоже костыль. Плюс все это создает проблемы синхронизации анимаций с действиями пользователя, т.к. картинка по сети загружается не мгновенно. Каждый раз ее загружать не удобно. Все это нарастает как снежный ком. Так что использование SVG со SMIL-анимациями в виде отдельной картинки в целом идея спорная. Подойдет не всегда. Если вы знаете, как это можно красиво приготовить — напишите в комментариях.

С inline svg все немного проще — там можно запускать SMIL-анимации с помощью атрибута begin='indefinite' и метода beginElement, есть доступ ко всему из скриптов, но при этом по мере развития проектов растет и количество подставок и прослоек, чтобы все работало с постепенной загрузкой контента в рамках условного SPA. Тоже не идеальный вариант на самом деле, но он явно удобнее, чем с отдельными файлами. По крайней мере все надежно и предсказуемо.

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


Анимирование SVG с помощью JS

Следующий вариант анимирования — это скрипты. В целом здесь, как и в случае с CSS, все очень похоже на анимирование обычных HTML-элементов. Мы получаем их через методы вроде querySelector и используем setAttribute чтобы менять значения атрибутов у тегов. Ради каких-то оптимизаций возможны игры с innerHTML или еще что-то такое. Но в целом ничего сверхъестественного. Это все и не удивительно, т.к. речь идет о привычных нам встроенных во все браузеры инструментах.

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

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


Инструменты для интерполяции значений во времени

Главный класс инструментов, который используется в 99.9% случаев анимирования SVG с помощью скриптов — это инструменты для интерполяции значений. Это логично: если у нас есть задача менять со временем атрибуты элементов, то нужно иметь какой-то способ описывать логику этих изменений, и, в частности, логику расчета промежуточных значений между начальным и конечным состоянием элементов в анимациях.

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

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

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

function updateElement() {
    // обновляем атрибуты элементов

    requestAnimationFrame(updateElement);
}

Как пример:

На сегодняшний день стандартным FPS в браузерах является 60 кадров в секунду. Или 120, для экранов, которые это умеют. Это не то, чтобы константа. Зависит от монитора. Для нас важно, что при использовании requestAnimationFrame наша функция будет вызываться эти самые 60, или 120, или какая там будет частота у монитора, раз в секунду. Но не больше. Это важно. Если мы сделаем бесконечный цикл, то он будет крутиться с такой скоростью, на которую способен процессор в компьютере, а requestAnimationFrame — это своего рода тормозилка, снимающая нагрузку с железа. Если же наша функция будет тяжелой, будет что-то делать слишком долго, то следующие кадры будут пропускаться до тех пор, пока наши расчеты не закончатся. Будет видимая просадка FPS, но все будет работать в штатном режиме, по крайней мере устройство пользователя не повесится. Скорее всего.


Экраны с высокой частотой обновления обычно предполагают хорошее железо, которое скорее всего и не заметит наших анимаций. Но. Если поделить 1000ms на условные 60 кадров в секунду для типовых экранов среднестатистических ноутбуков, то получится 16.5ms на кадр. Это время, за которое наша страница должна обновляться, чтобы все было ок. Не стоит забывать, что компьютер пользователя вообще-то говоря занят и другими делами, помимо наших анимаций. Поэтому при оценке алгоритмической сложности происходящего и тестах на слабом железе не стоит думать, что у реальных пользователей все ресурсы компьютера будут на самом деле доступны все 16.5ms. Что-то будет занято другими процессами. Так что близко к этой границе лучше не подходить.

Функция requestAnimationFrame при вызове колбека передает туда в качестве параметра текущее время. Сложно сказать, насколько это время точное, т.к. в современных браузерах из соображений безопасности даже performance.now () вроде как имеет случайную погрешность, но в контексте анимаций можно считать, что это плюс-минус текущее время. Соответственно можно использовать его и время начала анимации, чтобы произвести нехитрые расчеты прогресса конечной анимации, у которой есть начало и конец (тут пример условного JS-псевдокода, чтобы не раздувать примеры в статье, полный код будет на гитхабе):

function updateElement(time) {
    const timeFraction = (time - startTime) / duration;

    if (timeFraction >= 1) {
        // анимация закончилась
    } else {
        const progress = easingFunction(timeFraction);
        // обновляем атрибуты элементов,
        // используя прогресс анимации от 0 до 1
        requestAnimationFrame(updateElement);
    }
}

В целом расчет прогресса анимации не представляет из себя ничего сложного, если иметь культуру использования значений от 0 до 1. Часть пройденного времени анимации — всегда от 0 до 1. Прогресс — от 0 до 1. Временные функции принимают на вход значения от 0 до 1 и возвращают значения от 0 до 1. Если это сложные функции, то они должны по крайней мере возвращать 0 для 0 на входе, и, соответственно, 1 для 1 на входе. Когда весь код построен на такой логике, в нем появляется возможность подставлять что угодно куда угодно, и становится проще его воспринимать, т.к. мы всегда знаем, какие примерно значения, в каких пределах используются. В CSS у нас все временные функции именно такие, если вы не задумывались. Как примеры временных функций на JS, подчиняющихся этой логике:

function easeLinear(x) {
    return x;
}

function easeInCubic(x) {
    return x * x * x;
}

function easeInExpo(x) {
    if (x === 0) {
        return 0;
    } else {
        return Math.pow(2, 10 * x - 10);
    }
}

Неплохая подборка стандартных временных функций с примерами есть на https://easings.net/. Если вам эта тема интересна, то можно посмотреть примеры в ней.

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


Библиотеки

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

1xgkcd2u0bau5aq-mcvbc0nwemo.jpeg

GSAP появился уже лет 10 назад, и в то время он в контексте креативной веб-разработки конкурировал с технологией flash. Чем больше на него смотришь, тем больше понимаешь, почему он такой, какой есть. Яростный маркетинг про новый стандарт анимации в вебе со всякими перлами для новичков в духе «анимация на GSAP работает быстрее анимации на JS» (хотя GSAP сам написан на JS и не может работать быстрее самого себя), форум с платной техподдержкой и обучением, монструозная документация, куча готовых функций и плагинов на любые обстоятельства, из которых в повседневной работе в рядовых интерфейсах можно применить от силы 10% — это все наследие из тех времен. Но нужно отдать должное — за столько лет инструмент вылизали, что-то убрали, где-то упростили, и работает он очень даже хорошо. Хотя все еще часто кажется слишком большим относительно решаемых задач.

Но есть также поколение более молодых инструментов, которые появилсь лет на 5 после GSAP, когда flash уже заканчивался, старые IE постепенно отмирали, появлялся ES6, SPA-фреймворки, и становилось понятно, как будет выглядеть верстка будущего. Что в ней нужно. Эти инструменты сохраняют в себе только то, что востребовано в повседневной работе, и это позволяет им оставаться компактными по сравнению с GSAP. Они могут быть хорошим выбором в проектах с более-менее типовой версткой, где нет потребности в каких-то извращениях. Похоже, что самый популярный инструмент в этом поколении — это Anime.js.

ymurak7grweu8cc0qv1o_a5uvb8.jpeg

Эти инструменты, как вы уже поняли, универсальные. Их можно использовать для любых анимаций на странице. Можно атрибуты элементов менять, можно какие-то переменные в скриптах пересчитывать и где-то потом использовать. В WebGL контексте, например. Но есть класс изначально более узкоспециализированных инструментов, которые предназначались для работы с SVG. Мы ведь про это в первую очередь говорим. Например Snap.svg. Подобных инструментов можно много загуглить. Там есть более объемные, есть менее. Какие-то решают только одну задачу, например движение по кривой. Некоторые из них, вроде Raphaël, уходят корнями во времена IE6. Можете себе представить, какой адъ там внутри.

Удобно все подобные инструменты рассматривать как jQuery для SVG. Они дают разные обертки над стандартными методами работы с элементами на странице, закрывают какие-то древние проблемы кроссбраузерности (иногда там внутри генерируется SMIL-разметка), есть какой-то еще относительно часто нужный функционал, который когда-то было неудобно писать. Актуальность этих инструментов в наше время — такая же, как и у jQuery. Есть много готовых решений для разных задач, которые когда-то сделали с их использованием. Если какое-то из них подходит под наши задачи — ок, берем. Но если речь про новый проект с чистого листа, и у нас есть условный GSAP для всего остального на странице — может и не нужны они. Вопросы кроссбраузерности сейчас уже не стоят так же остро, как когда-то, а в сложных задачах сама сложность обычно состоит в хитрых расчетах, а синтаксические сахарные обертки не особо с этим помогают.

Но есть еще один класс инструментов, которые вообще-то решают перпендикулярные анимациям задачи, хотя где-то в своих недрах умеют что-то там анимировать. И почему-то часто всплывает вопрос в духе «а не использовать ли этот инструмент, как основной инструмент для анимаций на странице?». Хороший пример — D3.js.

_fy6kfr0z_usbzy-xsjmxuu47bw.jpeg

Очень модная штука была когда-то, про нее на всех собеседованиях спрашивали. Этот конкретный инструмент — для визуализации данных. Схожие по идеологии инструменты есть и для других задач, но этот у всех на слуху, так что будем ссылаться на него. Наверное не очень корректно сваливать все в одну кучу, но нам удобно представить, что суть всех этих инструментов в том, что это комбайны, в которых собрали все, что в теории может понадобиться в какой-то узкой специализации в плане задач. Если наши задачи находятся только в этой области, например мы только визуализируем данные, то это может быть очень хорошим выбором. Там есть куча штук, которые могут понадобиться для этой работы. И функционал анимаций тоже есть из коробки. Удобно. Но если наши задачи за пределами этой области — то… Ну такое. Если сравнить гибкость и удобство настройки анимаций в том же GSAP и в D3.js, то разница будет заметна. Все же есть инструменты, которые на этом специализируются, а есть те, для которых анимации — это побочный функционал.


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

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

А закончить тему сторонних инструментов можно грубым, но важным моментом, про который часто умалчивают маркетологи и евангелисты:


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


Кроссбраузерность SVG-анимаций

Раз уж мы отмечали слово «кроссбраузерность» уже не раз, стоит остановиться на ней подробнее. В 2022 году вопросы кроссбраузерности во фронтенде уже не стоят так остро, как это было в 2012, но все же есть некоторые нестыковки.

В плане CSS почти все ок. Бывают баги, связанные с рендерингом, но они обычно одинаковые со всеми вариантами анимирования, и скорее связаны с самим форматом SVG, чем со способом изменения атрибутов. Они во многом похожи на проблемы, которые воз

© Habrahabr.ru