Как использовать анимацию SVG в CSS

Опубликовано: 2022-03-11

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

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

SVG-анимация с использованием CSS: основные понятия

Прежде чем анимировать SVG с помощью CSS, разработчикам необходимо понять, как SVG работает внутри. К счастью, это похоже на HTML: мы определяем элементы SVG с синтаксисом XML и стилизуем их с помощью CSS, как если бы они были HTML.

Элементы SVG специально созданы для рисования графики. Мы можем использовать <rect> для рисования прямоугольника, <circle> для рисования кругов и т. д. SVG также определяет <ellipse> , <line> , <polyline> , <polygon> и <path> .

Примечание . Полный список элементов SVG включает даже элемент <animate> , который позволяет создавать анимацию с использованием синхронизированного языка интеграции мультимедиа (SMIL). Однако его будущее неясно, и команда Chromium рекомендует использовать подход на основе CSS или JavaScript для анимации SVG, когда это возможно.

Доступные атрибуты зависят от элемента, поэтому в то время как <rect> имеет атрибуты width и height , элемент <circle> имеет атрибут r , который определяет его радиус.

Три основных элемента SVG (прямоугольник, круг и линия) и их доступные атрибуты. Прямоугольник определяется координатами x и y его верхнего левого угла, а также его шириной и высотой, например, <rect x="25" y="25" width="150" height="100 "/>. Круг определяется координатами x и y его центра (cx, cy), за которыми следует его радиус (r), например, <circle cx="75" cy="75" r="50"/>. Линия определяется с использованием начальных координат (x1 и y1) и конечных координат (x2 и y2), например, <line x1="40" y1="140 x2="140" y2="40"/>.
Выберите основные элементы SVG; координаты относительно начала координат (верхний левый угол области просмотра SVG).

В то время как большинство элементов HTML могут иметь дочерние элементы, большинство элементов SVG не могут. Единственным исключением является групповой элемент <g> , который мы можем использовать для применения стилей и преобразований CSS к нескольким элементам одновременно.

Элемент <svg> и его атрибуты

Еще одно важное различие между HTML и SVG заключается в том, как мы позиционируем элементы, в частности, с помощью атрибута viewBox данного внешнего элемента <svg> . Его значение состоит из четырех чисел, разделенных пробелом или запятой: min-x , min-y , width и height . Вместе они определяют, какую часть нашего SVG-рисунка мы хотим, чтобы браузер отображал. Эта область будет масштабироваться, чтобы соответствовать границам области просмотра , как определено атрибутами width и height элемента <svg> .

Когда дело доходит до почтового ящика, соотношение атрибутов width и height области просмотра действительно может отличаться от соотношения частей width и height атрибута viewBox .

По умолчанию соотношение сторон холста SVG будет сохранено за счет большего, чем указано, viewBox , что приведет к уменьшению рендеринга в виде почтового ящика в окне просмотра. Но вы можете указать другое поведение с помощью атрибута preserveAspectRatio .

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

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

Хотя вы можете кодировать изображения SVG вручную, для более сложных изображений может потребоваться программа векторной графики (наше руководство по анимации SVG демонстрирует оба метода). Я предпочитаю редактор Affinity Designer, но любой редактор должен обеспечивать достаточную функциональность для описанных здесь простых операций.

CSS-переходы и анимация

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

См. пример Pen Transition от Филипа Дефара (@dabrorius) на CodePen.

Мы можем определить переходы с помощью свойства transition , которое принимает имя свойства, которое мы хотим изменить, продолжительность перехода, функцию синхронизации перехода (также известную как функция плавности) и продолжительность задержки перед началом эффекта. :

 /* property name | duration | easing function | delay */ transition: margin-right 4s ease-in-out 1s;

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

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

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

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

См. пример Pen Animation от Филипа Дефара (@dabrorius) на CodePen.

Чтобы анимировать свойства CSS для нескольких ключевых кадров, сначала нам нужно определить ключевые кадры с помощью @keyframes at-rule. Время ключевых кадров определяется в относительных единицах (процентах), потому что на данный момент мы еще не определили продолжительность анимации. Каждый ключевой кадр описывает значения одного или нескольких свойств CSS в данный момент времени. CSS-анимация обеспечит плавный переход между ключевыми кадрами.

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

Единственное отличие состоит в том, что первым параметром является наше имя @keyframes , а не имя свойства:

 /* @keyframes name | duration | easing-function | delay */ animation: my-sliding-animation 3s linear 1s;

Анимация переключателя меню «Гамбургер»

Теперь, когда у нас есть общее представление о том, как работает анимация SVG, мы можем приступить к созданию классической анимации — переключателя меню, который плавно переключается между значком «гамбургер» и кнопкой закрытия («X»):

См. Pen Hamburger от Филипа Дефара (@dabrorius) на CodePen.

Это тонкая, но ценная анимация. Он привлекает внимание пользователя, информируя его о том, что с помощью значка можно закрыть меню.

Мы начнем нашу демонстрацию с создания элемента SVG с тремя строками:

 <svg class="hamburger"> <line x1="0" y1="50%" x2="100%" y2="50%" class="hamburger__bar hamburger__bar--top" /> <line x1="0" y1="50%" x2="100%" y2="50%" class="hamburger__bar hamburger__bar--mid" /> <line x1="0" y1="50%" x2="100%" y2="50%" class="hamburger__bar hamburger__bar--bot" /> </svg>

Каждая строка имеет два набора атрибутов. x1 и y1 представляют собой координаты начала строки, а x2 и y2 представляют собой координаты конца строки. Мы использовали относительные единицы для установки позиций. Это простой способ обеспечить изменение размера содержимого изображения в соответствии с содержащим его элементом SVG. Хотя в данном случае этот подход работает, у него есть один большой недостаток: мы не можем поддерживать пропорции элементов, расположенных таким образом. Для этого нам пришлось бы использовать атрибут viewBox элемента <svg> .

Обратите внимание, что мы применили классы CSS к элементам SVG. Есть много свойств, которые можно изменить с помощью CSS, поэтому давайте применим базовые стили к нашим элементам SVG.

Мы установим размер элемента <svg> , а также изменим тип курсора, чтобы указать, что он активен. Но чтобы установить цвет и толщину линий, мы будем использовать свойства stroke и stroke-width . Возможно, вы ожидали использовать color или border , но, в отличие от самого <svg> , подэлементы SVG не являются элементами HTML, поэтому они часто имеют разные имена свойств:

 .hamburger { width: 62px; height: 62px; cursor: pointer; } .hamburger__bar { stroke: white; stroke-width: 10%; }

Если мы отрендерим в этот момент, мы увидим, что все три линии имеют одинаковый размер и положение, полностью перекрывая друг друга. К сожалению, мы не можем изменить начальную и конечную позиции независимо друг от друга с помощью CSS, но мы можем перемещать целые элементы. Давайте переместим верхнюю и нижнюю панели с помощью свойства CSS transform :

 .hamburger__bar--top { transform: translateY(-40%); } .hamburger__bar--bot { transform: translateY(40%); }

Перемещая полосы по оси Y, мы получаем приличный гамбургер.

Теперь пришло время закодировать наше второе состояние: кнопку закрытия. Мы полагаемся на класс CSS .is-opened , применяемый к элементу SVG для переключения между двумя состояниями. Чтобы сделать результат более доступным, давайте завернем наш SVG в элемент <button> и будем обрабатывать клики на этом уровне.

Процесс добавления и удаления класса будет обрабатываться простым фрагментом JavaScript:

 const hamburger = document.querySelector("button"); hamburger.addEventListener("click", () => { hamburger.classList.toggle("is-opened"); });

Чтобы создать наш X, мы можем применить другое свойство transform к нашим батончикам для гамбургеров. Поскольку новое свойство transform переопределит старое, нашей отправной точкой будет исходное общее положение трех полос.

Оттуда мы можем повернуть верхнюю полосу на 45 градусов по часовой стрелке вокруг ее центра и повернуть нижнюю полосу на 45 градусов против часовой стрелки. Мы можем уменьшить среднюю полосу по горизонтали, пока она не станет достаточно узкой, чтобы ее можно было скрыть за центром X:

 .is-opened .hamburger__bar--top { transform: rotate(45deg); } .is-opened .hamburger__bar--mid { transform: scaleX(0.1); } .is-opened .hamburger__bar--bot { transform: rotate(-45deg); }

По умолчанию свойство transform-origin для элементов SVG обычно равно 0,0 . Это означает, что наши полосы будут вращаться вокруг верхнего левого угла области просмотра, но мы хотим, чтобы они вращались вокруг центра. Чтобы исправить это, давайте установим для свойства transform-origin значение center для класса .hamburger__bar .

Анимация свойств CSS с помощью transition

Свойство CSS transition сообщает браузеру о плавном переходе между двумя разными состояниями свойств CSS. Здесь мы хотим анимировать наши изменения свойства transform , которое определяет положение, ориентацию и масштаб полос.

Мы также можем контролировать продолжительность перехода, используя свойство transition-duration . Чтобы анимация выглядела быстро, мы установим короткую продолжительность 0,3 секунды:

 .hamburger__bar { transition-property: transform; transition-duration: 0.3s; ... }

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

 const hamburger = document.querySelector("button"); hamburger.addEventListener("click", () => { hamburger.classList.toggle("is-opened"); });

Здесь мы выбираем внешний элемент SVG по его классу .mute , используя querySelector querySelector() . Затем мы добавляем прослушиватель событий щелчка. Когда запускается событие клика, мы переключаем класс .is-active только на самом <svg> — не глубже в иерархии. Поскольку мы сделали анимацию CSS применимой только к элементам с классом .is-active , переключение этого класса будет активировать и деактивировать анимацию.

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

 body { display: flex; justify-content: center; align-items: center; background-color: #222; height: 100vh; }

Таким образом, мы создали полнофункциональную анимированную кнопку, используя базовый CSS и короткий фрагмент кода JavaScript. Преобразования, которые мы применили, легко изменить для создания различных анимаций. Читатели могут просто разветвить CodePen, который включает в себя немного дополнительного CSS для полировки, и проявить творческий подход.

Работа с данными SVG из внешних редакторов

Наше гамбургер-меню очень простое. Что, если мы хотим сделать что-то более сложное? Вот где кодирование SVG вручную становится трудным, и программное обеспечение для редактирования векторной графики может помочь.

Наша вторая SVG-анимация — это кнопка отключения звука со значком наушников. Когда музыка активна, значок будет пульсировать и танцевать; когда он отключен, значок будет перечеркнут:

См. кнопку «Отключить звук пера» — 5 — красная зачеркнутая филипом Дефаром (@dabrorius) на CodePen.

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

Возможно, вы захотите очистить код SVG перед работой с ним. Вы можете сделать это с помощью svgo, инструмента оптимизатора SVG на основе Node.js с открытым исходным кодом. Это удалит ненужные элементы и упростит редактирование кода вручную, что вам нужно будет сделать, чтобы добавить классы и объединить разные элементы.

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

Рекомендуется изменить размер SVG, чтобы для viewBox можно было установить некоторые простые в использовании значения. В данном случае я преобразовал его в viewBox 100 x 100 пикселей.

Давайте удостоверимся, что значок расположен по центру и имеет соответствующий размер. Мы применим класс mute к нашему базовому элементу SVG, а затем добавим следующие стили CSS:

 .mute { fill: white; width: 80px; height: 70px; cursor: pointer; }

Здесь width немного больше height , чтобы избежать обрезки во время вращения нашей анимации.

Наша отправная точка SVG-анимации

Теперь чистый SVG содержит один элемент <g> , который содержит три элемента <path> .

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

Элемент g SVG — это контейнер, используемый для группировки других элементов SVG. Мы используем его для одновременного применения пульсирующих и танцующих трансформаций ко всем трем путям.

 <svg class="mute" viewBox="0 0 100 100"> <g> <path d="M92.6,50.075C92.213,26.775 73.25,7.938 50,7.938C26.75,7.938 7.775,26.775 7.388,50.075C3.112,51.363 -0.013,55.425 -0.013,60.25L-0.013,72.7C-0.013,78.55 4.575,83.3 10.238,83.3L18.363,83.3L18.363,51.6C18.4,51.338 18.438,51.075 18.438,50.813C18.438,33.275 32.6,19 50,19C67.4,19 81.563,33.275 81.563,50.813C81.563,51.088 81.6,51.338 81.638,51.6L81.638,83.313L89.763,83.313C95.413,83.313 100.013,78.563 100.013,72.713L100.013,60.263C100,55.438 96.875,51.362 92.6,50.075Z" /> <path d="M70.538,54.088L70.538,79.588C70.538,81.625 72.188,83.275 74.225,83.275L74.225,83.325L78.662,83.325L78.662,50.4L74.225,50.4C72.213,50.4 70.538,52.063 70.538,54.088Z" /> <path d="M25.75,50.4L21.313,50.4L21.313,83.325L25.75,83.325L25.75,83.275C27.788,83.275 29.438,81.625 29.438,79.588L29.438,54.088C29.45,52.063 27.775,50.4 25.75,50.4Z" /> </g> </svg>

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

В этом случае наши начальный и конечный ключевые кадры (на 0% и 100% анимации соответственно) используют слегка сморщенный значок наушников. Для первых 40% анимации мы немного увеличиваем изображение и наклоняем его на 5 градусов. Затем для следующих 40% анимации мы уменьшаем ее до 0,9x и поворачиваем на 5 градусов в другую сторону. Наконец, для последних 20% анимации преобразование значка возвращается к тем же начальным параметрам, чтобы зацикливаться плавно.

 @keyframes pulse { 0% { transform: scale(0.9); } 40% { transform: scale(1) rotate(5deg); } 80% { transform: scale(1) rotate(-5deg); } 100% { transform: scale(0.9) rotate(0); } }

CSS-оптимизация анимации

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

Поскольку наш 100% ключевой кадр задает весь список transform , если бы мы полностью опустили rotate() , его значение по умолчанию было бы равно 0:

 100% { transform: scale(0.9); }

Во-вторых, мы знаем, что хотим, чтобы наши ключевые кадры 0% и 100% совпадали, потому что мы зацикливаем анимацию. Определив их одним и тем же правилом CSS, нам не нужно будет помнить об изменении их обоих, если мы хотим изменить эту общую точку в цикле анимации:

 0%, 100% { transform: scale(0.9); }

Наконец, скоро мы применим transform: scale(0.9); к классу mute__headphones , и когда мы это сделаем, нам вообще не нужно будет определять начальный и конечный ключевые кадры! По умолчанию они будут использовать статический стиль, используемый mute__headphones .

Теперь, когда мы определили ключевые кадры анимации, мы можем применить анимацию. Мы добавляем класс .mute__headphones в элемент <g> , чтобы он воздействовал на все три части значка наушников. Во-первых, мы снова устанавливаем для transform-origin значение center , так как мы хотим, чтобы значок вращался вокруг своего центра. Мы также масштабируем его, чтобы его размер соответствовал начальному ключевому кадру анимации. Без этого шага переключение со статического «приглушенного» значка на анимированный всегда будет приводить к резкому скачку размера. (В любом случае, переключение обратно на приглушенный вызовет скачок в масштабе — и, вероятно, поворот — если пользователь щелкнет, когда масштаб больше 0,9x. Мы мало что можем сделать с этим эффектом с помощью одного лишь CSS.)

Мы применяем анимацию, используя свойство CSS animation , но только при наличии родительского класса .is-active , аналогично тому, как мы анимировали наше меню-гамбургер.

 .mute__headphones { transform-origin: center; transform: scale(0.9); } .is-active .mute__headphones { animation: pulse 2s infinite; }

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

 const muteButton = document.querySelector(".mute"); muteButton.addEventListener("click", () => { muteButton.classList.toggle("is-active"); });

Следующая часть, которую мы добавим, — это зачеркнутая линия, которая появляется, когда значок неактивен. Поскольку это простой элемент дизайна, мы можем закодировать его вручную. Вот где полезно иметь простые и разумные значения viewBox . Мы знаем, что края холста находятся в точках 0 и 100, поэтому легко вычислить начальную и конечную позиции для нашей линии:

 <line x1="12" y1="12" x2="88" y2="88" class="mute__strikethrough" />

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

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

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

Поскольку мы применили класс непосредственно к зачеркнутому элементу <line> , мы можем стилизовать его с помощью CSS. Нам просто нужно сделать так, чтобы линия не была видна, когда иконка активна:

 .mute__strikethrough { stroke: red; opacity: 0.8; stroke-width: 12px; } .is-active .mute__strikethrough { opacity: 0; }

При желании мы можем добавить класс .is-active непосредственно в SVG. Это заставит анимацию запускаться сразу после загрузки страницы, поэтому мы фактически изменим начальное состояние значка с неанимированного (без звука) на анимированное (без звука).

SVG-анимация на основе CSS никуда не денется

Мы лишь немного коснулись методов CSS-анимации и того, как работают окна просмотра. Стоит знать, как писать код SVG вручную, чтобы простые анимации оставались простыми, но также важно знать, как и когда использовать графику, созданную во внешних редакторах. В то время как современные браузеры позволяют нам создавать впечатляющие анимации, используя только встроенные функции, для (очень) сложных случаев использования разработчики могут захотеть изучить библиотеки анимации, такие как GSAP или anime.js.

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


Особая благодарность Mike Zeballos за технический обзор этой статьи!