Анимируйте значки SVG с помощью CSS и Snap

Как оптимизировать код SVG и анимировать значок SVG с помощью библиотеки CSS и Snap.svg.



ДЕМО
ИСХОДНИКИ


Работа с файлами SVG (en.wikipedia.org/wiki/Scalable_Vector_Graphics) больше не вариант. Имея огромное количество устройств высокой четкости, невозможно экспортировать разные размеры одних и тех же растровых ресурсов и нацеливать определенные разрешения устройства с помощью медиа-запросов CSS. Нам нужно полагаться на векторную графику, когда это возможно.

Сегодняшний ресурс - это очень простой значок, который мы импортировали как встроенный SVG в наш файл index.html. После оптимизации кода svg мы попытались продвинуть его еще на один шаг, анимировав значок с помощью CSS и Snap.svg (snapsvg.io), который представляет собой библиотеку SVG javascript, созданную командой Adobe. Честно говоря, не так-то просто анимировать svg-файлы с текущими браузерами, поддерживающими статус-кво, но мы научились некоторым трюкам, которыми мы рады поделиться с вами!

Если вы новичок в SVG, вот несколько отличных ресурсов для начала:

Самый простой способ создать иллюстрацию SVG - использовать графические редакторы, такие как Adobe Illustrator или Sketch. Выберите тот, который вам больше нравится, и создайте значок (или загрузите наш исходный файл). Большая часть того, что вы создаете в этих приложениях, поддерживает SVG, а это означает, что создаваемая вами графика может быть переведена в код. Самый простой способ понять это - сохранить значок как файл .svg, а затем открыть его в редакторе кода.

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

В качестве примера: поскольку наш последний значок анимирован в 2 этапа, я создал в Illustrator 2 отдельных слоя: один для эффекта загрузки, а второй - для зданий. Поскольку видимость слоя cd-Building отключена, я могу ожидать, что отображение: none применяется к этому элементу в выходном коде. Кроме того, слои и группы в Illustrator считаются группами в экспортируемом коде SVG (что хорошо). Заголовки слоев в AI будут назначены как значения #id в коде SVG.

Анимируйте значки SVG с помощью Snap и CSS

При экспорте .svg из AI настройки по умолчанию работают нормально. Я просто убеждаюсь, что для свойств CSS установлено значение «Элементы стиля», чтобы отделить стиль от содержимого - автоматически создает классы.

Анимируйте значки SVG с помощью Snap и CSS

Некоторые моменты, которые следует учитывать при создании SVG в графическом редакторе:

  1. То, как вы организуете / группируете слои, повлияет на выходной код.
  2. Дайте слоям имя.
  3. Попробуйте использовать простые формы и удалите ненужные точки привязки (в AI попробуйте объект > Путь > Упростить).
  4. Старайтесь не векторизовать штрихи, вы можете управлять ими позже в своем коде, плюс код станет чище.
  5. Не все, что вы делаете в графическом редакторе, совместимо с SVG. У AI есть специальные фильтры SVG, но браузеры не поддерживают большинство из них.

Оптимизация кода SVG

Лично я не использую инструменты для автоматической очистки SVG-кода (github.com/svg/svgo-gui). Поскольку я всегда начинаю с графического редактора, я обнаружил, что лучший способ экспортировать чистый код - это тщательно продумывать дизайн и следовать советам, которые я предлагал ранее.

После того, как файл .svg был экспортирован, давайте импортируем его в редактор кода и посмотрим на код:


<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 18.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="200px" height="200px" viewBox="0 0 200 200" style="enable-background:new 0 0 200 200;" xml:space="preserve">
   <style type="text/css">
      .st0{fill:none;stroke:#D7DCE0;stroke-width:4;stroke-miterlimit:10;}
      .st1{fill:none;stroke:#A84D54;stroke-width:4;stroke-miterlimit:10;}
      .st2{opacity:0;fill:#FFFFFF;}
      .st3{fill:#223443;}
      .st4{fill:#FFFFFF;stroke:#223443;stroke-width:4;stroke-miterlimit:10;}
      .st5{fill:none;stroke:#223443;stroke-width:4;stroke-miterlimit:10;}
   </style>
   <g id="cd-loading">
      <g id="cd-circle">
         <circle id="cd-loading-circle" class="st0" cx="100" cy="100" r="77.5"/>
         <circle id="cd-loading-circle-filled" class="st1" cx="100" cy="100" r="77.5"/>
      </g>
      <g id="cd-play-btn">
         <rect x="84" y="78.1" class="st2" width="32" height="43.9"/>
         <polygon class="st3" points="84,78.1 116,100 84,121.9     "/>
      </g>
      <g id="cd-pause-btn">
         <rect x="81" y="80.1" class="st2" width="38" height="39.9"/>
         <rect x="81" y="80.1" class="st3" width="11" height="39.9"/>
         <rect x="108" y="80.1" class="st3" width="11" height="39.9"/>
      </g>
   </g>
   <g id="cd-buildings">
      <g id="cd-home-3">
         <rect id="cd-home-3-base" x="66" y="55" class="st4" width="58" height="123"/>
         <polygon id="cd-home-3-roof" class="st4" points="59.4,56 95,15 130.6,56     "/>
         <circle id="cd-home-3-window" class="st4" cx="95" cy="77" r="11"/>
       </g>
      <g id="cd-home-2">
         <rect id="cd-home-2-base" x="98" y="132" class="st4" width="69" height="46"/>
         <rect id="cd-home-2-roof" x="98" y="122" class="st4" width="69" height="10"/>
         <rect id="cd-home-2-door" x="141" y="155" class="st4" width="15" height="22"/>
         <rect id="cd-home-2-window" x="106" y="143" class="st4" width="25" height="9"/>
      </g>
      <g id="cd-home-1">
         <rect id="cd-home-1-base" x="29" y="109" class="st4" width="69" height="69"/>
         <rect id="cd-home-1-roof" x="23" y="100" class="st4" width="80" height="9"/>
         <rect id="cd-home-1-door" x="53" y="151" class="st4" width="20" height="27"/>
         <rect id="cd-home-1-window" x="53" y="121" class="st4" width="20" height="18"/>
         <rect id="cd-home-1-chimney" x="34" y="89" class="st4" width="11" height="11"/>
      </g>
      <line id="cd-floor" class="st5" x1="2" y1="178" x2="198" y2="178"/>
      <g id="cd-cloud-1">
         <line class="st0" x1="5" y1="13" x2="29" y2="13"/>
         <line class="st0" x1="35" y1="13" x2="44" y2="13"/>
         <line class="st0" x1="72" y1="27" x2="14" y2="27"/>
      </g>
      <g id="cd-cloud-2">
         <line class="st0" x1="191" y1="68" x2="159" y2="68"/>
         <line class="st0" x1="153" y1="68" x2="144" y2="68"/>
         <line class="st0" x1="179" y1="83" x2="133" y2="83"/>
      </g>
   </g>
</svg>

Как видите, Illustrator создал для нас несколько классов. Кроме того, структура уже довольно чистая. Дав имена группам и слоям в AI, теперь мы можем легко найти их в структуре кодирования SVG.

Прежде всего, поскольку мы хотим включить этот рисунок как встроенный SVG, мы можем взять только код, заключенный в теги <svg> </svg>, и вставить его в файл .html.

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


<body>
   <svg version="1.1" x="0px" y="0px" width="200px" height="200px" viewBox="0 0 200 200" style="enable-background:new 0 0 200 200;" xml:space="preserve">
      <title>Buildings</title>
      <desc>3 minimal buildings with floating clouds</desc>
      <style type="text/css">
         <!-- ... -->
      </style>
      <!-- SVG elements here -->
   </svg>
</body>

Следующим шагом будет создание большего количества семантических классов в CSS, чтобы заменить те, которые были созданы в Illustrator. Значок довольно минимален, поэтому в CSS будет всего несколько классов:


/* -------------------------------- 

Manage colors

-------------------------------- */
.cd-stroke {
  fill: none;
  stroke-width: 4;
  stroke-miterlimit: 10;
}

.cd-stroke-color-1 {
  stroke: #223443;
  fill: #f8f8f8;
}
.cd-stroke-color-1#floor {
  fill: none;
}

.cd-stroke-color-2 {
  stroke: #D7DCE0;
}

.cd-stroke-color-3 {
  stroke: #A84D54;
}

.cd-fill-color-1 {
  fill: #223443;
}

.cd-pointer {
  fill: #FFFFFF;
  opacity: 0;
}

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

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


<svg id="cd-animated-svg" version="1.1" x="0px" y="0px" width="200px" height="200px" viewBox="0 0 200 200" style="enable-background:new 0 0 200 200;" xml:space="preserve">
   <title>Buildings</title>
   <desc>3 minimal buildings with floating clouds</desc>
   <g id="cd-loading">
      <g id="cd-circle">
         <circle id="cd-loading-circle" class="cd-stroke cd-stroke-color-2" cx="100" cy="100" r="77.5"/>
         <circle id="cd-loading-circle-filled" class="cd-stroke cd-stroke-color-3" cx="100" cy="100" r="77.5"/>
      </g>
      <g id="cd-play-btn">
         <rect class="cd-pointer" x="84" y="78" width="33" height="44"/>
         <polygon class="cd-fill-color-1" points="84,78 116,100 84,122   "/>
      </g>
      <g id="cd-pause-btn">
         <rect class="cd-pointer" x="81" y="80" width="38" height="40"/>
         <rect class="cd-fill-color-1" x="81" y="80" width="11" height="40"/>
         <rect class="cd-fill-color-1" x="108" y="80" width="11" height="40"/>
      </g>
   </g>
   <g id="cd-buildings">
      <g id="cd-cloud-1">
         <line class="cd-stroke cd-stroke-color-2" x1="5" y1="13" x2="29" y2="13"/>
         <line class="cd-stroke cd-stroke-color-2" x1="35" y1="13" x2="44" y2="13"/>
         <line class="cd-stroke cd-stroke-color-2" x1="72" y1="27" x2="14" y2="27"/>
      </g>
      <g id="cd-cloud-2">
         <line class="cd-stroke cd-stroke-color-2" x1="191" y1="68" x2="159" y2="68"/>
         <line class="cd-stroke cd-stroke-color-2" x1="153" y1="68" x2="144" y2="68"/>
         <line class="cd-stroke cd-stroke-color-2" x1="179" y1="83" x2="133" y2="83"/>
      </g>
      <g id="cd-home-3">
         <polygon id="cd-home-3-roof" class="cd-stroke cd-stroke-color-1" points="59.4,55 95,15 130.6,55"/>
         <rect id="cd-home-3-base" class="cd-stroke cd-stroke-color-1" x="66" y="54" width="58" height="124"/>
         <circle id="cd-home-3-window" class="cd-stroke cd-stroke-color-1" cx="95" cy="77" r="11"/>
      </g>
      <g id="cd-home-2">
         <rect id="cd-home-2-base" class="cd-stroke cd-stroke-color-1" x="98" y="132" width="70" height="46"/>
         <rect id="cd-home-2-roof" class="cd-stroke cd-stroke-color-1" x="98" y="122" width="70" height="10"/>
         <rect id="cd-home-2-door" class="cd-stroke cd-stroke-color-1" x="141" y="156" width="16" height="22"/>
         <rect id="cd-home-2-window" class="cd-stroke cd-stroke-color-1" x="106" y="143" width="26" height="10"/>
      </g>
      <g id="cd-home-1">
         <rect id="cd-home-1-chimney" class="cd-stroke cd-stroke-color-1" x="34" y="89" width="11" height="11"/>
         <rect id="cd-home-1-base" class="cd-stroke cd-stroke-color-1" x="29" y="108" width="70" height="70"/>
         <rect id="cd-home-1-roof" class="cd-stroke cd-stroke-color-1" x="23" y="100" width="81" height="9"/>
         <rect id="cd-home-1-door" class="cd-stroke cd-stroke-color-1" x="53" y="150" width="20" height="28"/>
         <rect id="cd-home-1-window" class="cd-stroke cd-stroke-color-1" x="53" y="121" width="20" height="18"/>
      </g>
      <line id="cd-floor" class="cd-stroke cd-stroke-color-1" x1="2" y1="178" x2="198" y2="178"/>
   </g>
</svg>

SVG почти готов к анимации. Я заключил элемент <svg> в контейнер (.cd-svg-container), чтобы выровнять значок по центру. Установка max-width: 100% сделает SVG отзывчивым (как изображения).

Также вы можете заметить overflow: hidden применительно к SVG: это потому, что мы будем анимировать облака внутри и снаружи холста, и по некоторым неизвестным причинам IE показывает их, даже когда они отсутствуют, если мы не применим свойство переполнения. Также вы можете заметить прямоугольник (.cd-pointer) внутри элемента #cd-pause-btn. Он используется для того, чтобы сделать область между двумя прямоугольниками паузы доступной для щелчка, отсюда непрозрачность: 0 и значение заливки.

Мы определили класс .cd-fade-out, который будет применяться в javascript в конце анимации загрузки к элементу #cd-loading.

Наконец, мы использовали класс .no-js на случай, если javascript отключен: в качестве запасного варианта мы показываем не значок загрузки, а здания (это просто имеет больше смысла).


.cd-svg-container {
  width: 90%;
  max-width: 200px;
  margin: 0 auto 100px;
}
.cd-svg-container svg {
  display: block;
  overflow: hidden;
  max-width: 100%;
}
.no-js .cd-svg-container {
  height: 200px;
  background: url("../img/cd-icon.svg") no-repeat center center;
}
.no-js .cd-svg-container svg {
  display: none;
}

/* -------------------------------- 

Main elements - Loading

-------------------------------- */
#cd-loading {
  opacity: 1;
  visibility: visible;
  -webkit-transition: opacity .3s 0s, visibility 0s 0s;
  -moz-transition: opacity .3s 0s, visibility 0s 0s;
  transition: opacity .3s 0s, visibility 0s 0s;
}
#cd-loading.fade-out {
  opacity: 0;
  visibility: hidden;
  -webkit-transition: opacity .3s 0s, visibility 0s .3s;
  -moz-transition: opacity .3s 0s, visibility 0s .3s;
  transition: opacity .3s 0s, visibility 0s .3s;
}

#cd-play-btn, #cd-pause-btn {
  cursor: pointer;
}

#cd-pause-btn {
  pointer-events: none;
}

.play-is-clicked #cd-pause-btn {
  pointer-events: auto;
}

/* -------------------------------- 

Main elements - Buildings 

-------------------------------- */
#cd-home-1-chimney, #cd-home-3-roof {
  visibility: hidden;
}

Анимация SVG

Мы решили анимировать наш svg с помощью JavaScript, а не SMIL, чтобы поддерживать Internet Explorer (если вас интересует методика SMIL, ознакомьтесь с (css-tricks.com/guide-svg-animations-smil) этим руководством Сары Суэйдан). В нашей анимации мы изначально хотели скрыть группу #cd -building. Для этого мы изменили некоторые атрибуты в коде svg.

В качестве примера давайте сосредоточимся на прямоугольном элементе #cd-home-1-door (который является дверным элементом первого дома слева). Мы хотели, чтобы этот элемент был скрыт в начале, а затем анимировал его высоту от 0 до 28 пикселей. Итак, в структуре HTML мы устанавливаем height = "0" (вместо height = "28") и добавляем data-height = "28", чтобы легко получить его с помощью JavaScript.


<rect id="cd-home-1-door" class="cd-stroke cd-stroke-color-1" x="53" y="150" width="20" height="0" data-height="28"/>

Мы использовали метод animate (), предоставленный Snap.svg.


var animatingSvg = Snap('#cd-animated-svg'),
    buildingDoor1 = animatingSvg.select('#cd-home-1-door');

buildingDoor1.animate({'height': buildingDoor1.attr('data-height')}, 300, mina.easeinout);

Дверь расширяется сверху вниз, но мы хотели обратного эффекта! Итак, мы применили к элементу поворот на 180 градусов.


<rect id="cd-home-1-door" class="cd-stroke cd-stroke-color-1" x="53" y="150" width="20" height="0" transform="rotate(180 63 164)" data-height="28"/>

Одно важное замечание: вначале мы пытались применить поворот CSS (вместо использования атрибута преобразования svg), но, к сожалению, преобразования CSS в SVG в настоящее время не поддерживаются в IE11 и ниже.

Функция rotate () принимает три параметра: первый - угол поворота, второй и третий - координаты центра вращения.

После того, как все элементы группы #cd-Building были скрыты, мы сосредоточились на #cd-loading. Во-первых, мы хотели скрыть #cd-pause-btn. Мы присвоили ему масштабное преобразование:


<g id="cd-pause-btn" transform="translate(100 100) scale(0)">
   <!-- .... -->
</g>

Одно важное замечание: функция scale () не принимает в качестве аргумента исходные координаты преобразования (по умолчанию используется верхний левый угол svg).

Чтобы масштабировать объект по заданному коэффициенту вокруг точки, вы можете комбинировать два преобразования:


transform="translate(-centerX(factor- 1) -centerY(factor- 1)) scale(factor)"

Поскольку в нашем случае factor = 0 и (centerX, centerY) = (100, 100), мы применили также преобразование translate (100 100).

После этого нам нужно было анимировать элемент #cd-loading-circle-fill (малиновый круг, который рисуется при нажатии кнопки воспроизведения). Для создания этой анимации мы использовали два атрибута svg: stroke-dasharray и stroke-dashoffset. Представляя круг как пунктирную линию, штрих-дашаррей позволяет вам указать длину штриха и промежутки, а штриш-штрих-смещение позволяет вам изменить место начала дашаррея. В нашем случае мы устанавливаем:


var loadingCircle = animatingSvg.select('#cd-loading-circle-filled'),
    circumf = Math.PI*(loadingCircle.attr('r')*2);

loadingCircle.attr({
   'stroke-dasharray': circumf+' '+circumf,
   'stroke-dashoffset': circumf,
});

Таким образом, тире и промежуток равны длине окружности круга, но мы устанавливаем stroke-dashoffset: circf так, чтобы был виден только промежуток (прозрачный). Чтобы создать эффект загрузки, мы анимировали атрибут stroke-dashoffset:


var playBtn = animatingSvg.select('#cd-play-btn').
globalAnimation;

playBtn.click(function(){ 
   var strokeOffset = loadingCircle.attr('stroke-dashoffset').replace('px', '');
   //animate strokeOffeset desn't work with circle element - we need to use Snap.animate() rather than loadingCircle.animate()
   globalAnimation = Snap.animate(strokeOffset, '0', function( value ){ 
      loadingCircle.attr({ 'stroke-dashoffset': value })
   }, 1500, mina.easein);
});

Одно важное замечание: атрибут stroke-dashoffset для элемента круга нельзя анимировать напрямую с помощью метода animate () (хотя он отлично работает для элемента пути); вот почему мы использовали метод Snap.animate () и сохранили возвращенный объект анимации в переменной globalAnimation, чтобы остановить анимацию, когда пользователь нажимает кнопку паузы:


var pauseBtn = animatingSvg.select('#cd-pause-btn');

pauseBtn.click(function(){
   //pause the animation on the loadingCircle
   globalAnimation.stop();
});

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

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

Ссылка на автора статьи - twitter.com/guerriero_se !


Top

🔖 Выбор по тегам ×

💌 Написать сообщение ×

Все поля обязательны для заполнения!