Анимация SVG с помощью CSS и SMIL

SVG анимация основы

SVG графику можно анимировать, УРА, с помощью известного и горячо любимого CSS. Плюс к этому есть возможности самого svg описанные в спецификации SMIL Animation. Не забываем что возможности анимации зависят от способа, которым встроен svg на страницу. Наиболее полная поддержка идёт если встраиваете код непосредственно в html страницу или используя тег <object>

<object type="image/svg+xml" data="image.svg">
Your browser does not support SVGs
</object>

Трансформация SVG с помощью CSS

С помощью CSS можно менять цвет, размер, поворачивать, управлять временем анимации. Делать всё то, что делаем с элементами HTML страницы. Стили пишем в svg файл внутри тега <style>. Вот пример простой трансформации svg картинки

<svg width="250" height="150" xmlns="http://www.w3.org/2000/svg">
  <style>
    .primer {
      fill:red;
      stroke:black;
      transition-duration: 2s;
    }
    .primer:hover {
      fill:yellow;
      transform:scale(1.2);
      stroke:red;
    }
  </style>
  <ellipse class="primer" cx="100" cy="50" rx="100" ry="50" />
</svg>

CSS работает в SVG не совсем так как с HTML. Прежде всего это касается атрибутов отвечающих за заливку и обводку. Вместо привычных нам background и border здесь нужно использовать fill, stroke и stroke-width. В качестве css свойств нужно использовать атрибуты SVG. Не все атрибуты можно изменять с помощью CSS. Не прикосновенными остаются атрибуты определяющие размер, координаты(x, y), радиус. В SVG2 список доступных для css атрибутов расширят.

Не смотря на ограничения в текущей версии svg можно управлять расположением и отображением svg элементов используя css свойство transform. При трансформации учитывайте, что в отличии от привычной трансформации в HTML, где точка отсчёта по умолчанию — центр блока, точка отсчёта в SVG — верхний левый угол изображения.

Как подключать CSS стили в SVG

  1. Тег <style>. Этот способ использован в примере выше. Тег <style> должен находиться внутри SVG.
  2. Атрибут style, для конкретного элемента.
    <polyline
      style = "fill:yellow; stroke:red; stroke-width:5;"
      points="30 10, 170 10, 150 90, 10 90"
    />
    
  3. Можно вынести тег <style> за пределы svg. В этом случае SVG должен быть вставлен непосредственно в html.
    <html>
    <head>
      <title>css + svg</title>
    </head>
    <body>
      <style type="text/css">
        /*css*/
      </style>
      <svg>
        <!-- SVG content -->
      </svg>
    </body>
    </html>

     

В качестве селекторов используем обычные class и ID. Также поддерживаются псевдо-классы, такие как  :hover, :focus, :first-child.

SVG анимация

SVG анимация, в отличии от css, имеет доступ ко всем атрибутам элементов и следовательно больше возможностей. Но если вы знаете и умеете JS, то svg анимация не предложит вам ничего такого чего бы вы не сделали используя Javascript. Главное помнить что анимация сделанная с применением Javascript не будет работать если svg добавлен на страницу через тег <img> или как background-image. Подробнее про JS анимацию SVG в другой раз.

Для анимации в SVG нужно создать объект <animate> и выбрать объект для анимации через атрибут xlink:href. В качестве параметра xlink:href принимает URI ссылку.

<circle id="for_animation".../>
<animate xlink:href="#for_animation" .../>

Если  xlink:href не задан, то анимация применится к родительскому элементу

<circle id="for_animation"...>
<animate .../>
<circle/>

Следующим шагом указываем атрибут который будем изменять. Для этого используем attributeName. Имя атрибута может быть одним из SVG атрибутов или css свойством. Есть необязательный параметр attributeType, который подсказывает к чему относится атрибут(css или svg). Если  attributeName не задан, браузер сначала посмотрит доступные css свойства если нет то проверит svg атрибуты. AttributeName принимает только одно значение. Если нужно анимировать несколько атрибутов нужно для каждого добавить <animate>.

Обект и атрибут выбрали. Переходим к анимации. Условия при которых начинается анимация определяются в атрибуте begin. В качестве параметра принимает время или действие после которого должна запуститься анимация. Например begin="3s" запустит анимацию через три секунды после загрузки а begin="click" после клика. Можно комбинировать begin="click + 3s" — через три секунды после клика.

Старт анимации одного элемента можно привязать к анимации другого. Для этого нужно задать для первого элемента id(например id="first_animanion") а для второго указать begin="first_animation.begin + 3s". Таким образом мы запустим анимацию через три секунды после старта анимации с id first_animation. Но это ещё не всё. begin="first_animation.end" запустит анимацию после завершения first_animation, а  begin="first_animation.end - 3s" запустит анимацию за 3 секунды до завершения first_animation.

Атрибуты from и to определяют начальное и конечное значение анимируемого атрибута. Dur определяет время анимации.

Атрибут анимации fill определяет что делать элементу после завершения анимации. Принимает два значения:

  1. freeze — оставляет элемент в том же состоянии в каком элемент оказался в момент завершения анимации
  2. remove — возвращает элемент в первоначальное состояние.

Атрибут repeatCount определяет сколько раз повториться анимация. Можно указать конкретное количество повторений или для бесконечных повторений — indefinite. Не обязательный атрибут. Без него анимация проигрывается один раз.

Атрибут restart управляет перезапуском анимации. Принимает одно из трёх значений

  1. always — возможен перезапуск в любой момент
  2. whenNotActive — перезапуск возможен после завершения анимации
  3. never — перезапуск невозможен.

Пример как всё это выглядит в коде и в браузере.

<svg width="350" height="300" xmlns="http://www.w3.org/2000/svg">
   <circle id="green" cx="300" cy="250" r="30" fill="green" />
   <circle id="red" cx="40" cy="40" r="30" fill="red" />
   <animate
     xlink:href="#red"
     attributeName="cx"
     begin="click"
     from="40"
     to="300"
     dur="4s"
     fill="freeze"
     restart="whenNotActive"
     id="first"/>
   <animate
     xlink:href="#green"
     attributeName="cy"
     begin="first.begin + 1s"
     from="250"
     to="40"
     fill="freeze"
     dur="3s"/>
</svg>