Введение

Мы живём в век пикселей. Как дизайнеры и разработчики в вебе, пиксели могут быть нам как друзьями, так и врагами. Мы хотим, чтобы всё выглядело красиво и чётко для всех, кто пользуется нашими сайтами, и нам необходимо уменьшать размеры файлов для улучшения производительности. В общем-то, есть только один способ для иконок, логотипов и иллюстраций — это SVG. Масштабируемая векторная графика (Scalable Vector Graphics) позволяет изображению выглядеть чётко на мониторе с любым разрешением, при этом иметь очень маленький размер файла и легко поддаваться редактированию и изменениям.

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

SVG

Scalable Vector Graphics это разметка, основанная на XML, который содержит двумерные векторы. Векторами могут быть простые геометрические формы, сложные контуры, да и всё то же самое, что можно сделать в Иллюстраторе. Этот формат изображений имеет намного больше общего с веб-страницей, чем тот же JPEG. SVG намного мощнее — им легко можно управлять при помощи кода (в текстовом редакторе или с помощью CSS / JS).

  • не зависит от разрешения
  • поддерживается во всех современных браузерах
  • актуален в будущем (W3C стандарт)
  • легко создавать и редактировать
  • изменяется с помощью CSS & JS
  • поддаётся сжатию

Подготовка SVG для использования в вебе это очень простой процесс, не сложнее экспорта JPEG или PNG. Используйте любой привычный для вас графический редактор (Illustrator, Sketch, Inkscape [бесплатен], и тому подобное [или даже Photoshop, если вы используете слои с формами]) с тем размером изображения, который вы планируете использовать. Обычно я работаю в Иллюстраторе, поэтому я объясню некоторые способы подготовки файлов в этой программе, но вообще они применимы и для любой другой программы. Вам, возможно, стоит перевести текст в кривые, поскольку шрифт, скорее всего, будет неправильно отображаться, если, конечно, вы не планируете стилизовать их с помощью веб-шрифта, используемого на странице (что возможно!). Не стоит также превращать все объекты в единые формы, особенно если у вас есть обводка, которой необходимо будет управлять на странице, тем более преобразование объектов зачастую не уменьшает размер файла. Любые имена, присвоенные группам или слоям, будут добавлены к SVG как ID элемента. Это довольно удобно для стилизации, но немного увеличит общий размер файла.

Перед тем как сделать экспорт, необходимо проверить, все ли изображения находятся в целочисленной пиксельной сетке (то есть, например не 23.3px × 86.8px). В противном случае скорее всего изображению не будет хватать чёткости и часть изображения обрежется. В Иллюстраторе это можно сделать следующим образом: Object > Artboards > Fit to Artwork Bounds. Затем жмём save as и выбираем SVG, и оставляем настройки по умолчанию. Здесь можно сделать небольшую оптимизацию, но на самом деле не стоит, так как далее мы будем применять разные улучшающие приёмы, поэтому сейчас мы не будем тратить впустую время на эти настройки.

Приёмы для уменьшения размеров файла.

(Смотрите ресурсы по оптимизации)

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

Чтобы добиться наименьшего размера SVG, логично будет удалить из него всё лишнее. Наиболее известная и полезная программа (по крайней мере я так думаю) для обработки SVG это SVGO. Она удаляет весь не нужный код. Но! Будьте внимательны используя эту программу, если планируете управлять SVG при помощи CSS / JS, так как она может слишком сильно почистить код, что затруднит дальнейшие изменения. Удобство SVGO ещё и в том, что её можно включить в процесс автоматической сборки проекта, но можно также использовать GUI если хочется.

Разбираясь подробнее с правильным удалением всего ненужного, мы можем сделать ещё кое-что в графическом редакторе. Сперва нужно убедиться, что используется настолько мало контуров/форм, насколько это возможно, так же как и точек на этих контурах. Можно объединять и упрощать всё, что поддаётся упрощению, и удалить все ненужные точки. В Иллюстраторе есть плагин VectorScribe с инструментом Smart Remove Brush Tool, который поможет удалить точки и при этом оставить общую форму той же.

Предварительная оптимизация

Pre optimisation

Smart Remove Brush Tool удалил точки

Post optimisation

Дальше будем увеличивать изображение. В Иллюстраторе удобно включить просмотр с пиксельной сеткой View > Pixel Preview и проверить, как располагаются контуры. Чтобы разместить контуры по сетке, потребуется немного времени, но эти усилия окупятся и позволят добиться более чёткого рендеринга (лучше обратить на это внимание заранее).

Точки вне сетки

Points off pixels

Выравнивание по сетке

Pixel snapped

Если есть два и более объекта для выравнивания, то стоит удалить все ненужные перекрытия. Иногда даже если контуры тщательно выровнены, может быть видна тонкая белая линия. Чтобы предотвратить такое, можно немного наложить объекты друг на друга в местах перекрытия. Важно: в SVG z-index имеет определённый порядок, который зависит от объекта, находящегося снизу, поэтому стоит поместить верхний объект в нижнюю часть файла в коде.

Post optimisation

И, наконец, последнее, но немаловажное, то, о чём обычно забывают — это активировать gzip сжатие SVG на вашем сайте в .htaccess файле.

AddType image/svg+xml svg svgz
<IfModule mod_deflate.c>
  <IfModule mod_filter.c>
      AddOutputFilterByType DEFLATE "image/svg+xml" \
                                    "text/css" \
                                    "text/html" \
                                    "text/javascript"
                                    ... etc
  </IfModule>
</IfModule>

В качестве примера того, насколько эффективна эта техника, я воспользуюсь оригинальным логотипом Breaking Borders и оптимизирую его таким образом: увеличиваю размер до того, каким он должен быть; приведу в порядок контуры; удалю максимально возможное количество точек; передвину точки на целочисленные пиксели; сдвину все области перекрытий и отправлю это всё в SVGO.

Оригинал: 1,413b

Breaking Borders Logo

После оптимизации: 409b

Breaking Borders Logo

В итоге размер файла стал меньше на ~71% (и на ~83% при сжатии)

Дополнение: Rob Sterlini заметил, поскольку «b» повторяется, можно использовать элемент <use>, для повторения, что ещё больше уменьшит размер файла — и был абсолютно прав.

После оптимизации с использованием <use>: 311b

Breaking Borders Logo

Размер файла стал меньше на ~78%

Если применить эту технику ко всем файлам SVG, это значительно улучшит ваш сайт.

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

Img

Здесь всё делается так же, как с любым изображением в этом формате. Можно даже использовать SVG как элемент <picture>. Но помните, что возможности преобразований в этом формате ограничены.

<img src="bblogo.svg" alt="Breaking Borders Logo" height="65" width="68">
Breaking Borders Logo

Background-image

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

.logo {
  background-image: url(bblogo.svg);
}

Iframe

Вы можете загрузить SVG как <iframe>. Это позволит сделать многое, но я не уверен, что это лучший вариант использования, чтобы продвинуться вперёд ¯\_(ツ)_/¯.

<iframe src="bblogo.svg">Your browser does not support iframes</iframe>

Embed

<embed> применяется «во внешних приложениях» или «в интерактивном контенте». Вы можете использовать это для SVG, но лучше не стоит.

<embed type="image/svg+xml" src="bblogo.svg" />

Object

<object> возможно лучший вариант, если вам нужно изменять SVG, не встраивая его в HTML.

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

Inline

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

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 68 65">
  <path fill="#1A374D" d="M42 27v-20c0-3.7-3.3-7-7-7s-7 3.3-7 7v21l12 15-7 15.7c14.5 13.9 35 2.8 35-13.7 0-13.3-13.4-21.8-26-18zm6 25c-3.9 0-7-3.1-7-7s3.1-7 7-7 7 3.1 7 7-3.1 7-7 7z"/>
  <path d="M14 27v-20c0-3.7-3.3-7-7-7s-7 3.3-7 7v41c0 8.2 9.2 17 20 17s20-9.2 20-20c0-13.3-13.4-21.8-26-18zm6 25c-3.9 0-7-3.1-7-7s3.1-7 7-7 7 3.1 7 7-3.1 7-7 7z"/>
</svg>

Заключение

Если хочется выжать максимум из SVG, используйте <object>. В качестве альтернативы подходит встроенный SVG, который не повлияет на HTTP-запрос, но не будет кешироваться. Если SVG выступает в роли обычного изображения, то подойдёт <img> или background-image. Можно использовать <iframe> и <embed>, но я не думаю, что это лучшие варианты, и поэтому не буду больше заострять на них внимание.

Object Inline Img Background-image
CSS-манипуляции Да Да Частично Частично
JS-манипуляции Да Да Нет Нет
SVG-анимация Да Да Да Да
Интерактивная SVG-анимация Да Да Нет Нет

Важно: «Частично» означает, что частично работает, но при условии, что в CSS <style> встроены в код SVG. Больше информации об этом в следующей части.

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

Есть два способа изменить стили — во встроенном SVG и через внешнее подключение (например, в списке стилей). Чтобы встроить стили, оберните их в тег <style> и также внутри <![CDATA[ ... ]]>. Лучше делать именно так, потому что иногда XML анализаторы могут конфликтовать с определённым символами (например >). Даже если у вас нет шибко модных символов сейчас, лучше всё равно использовать CDATA, вдруг позже они появятся и всё сломают.

В основном встроенные стили будут работать со всеми реализациями <img> и background-image не поддерживают CSS3-анимацию, даже если они встроены (подробнее в разделе об анимации). background-image не поддерживает встроенные медиазапросы (подробнее в разделе о медиазапросах).

Встроенные стили

Your browser does not support SVGs
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 68 65">
  <style type="text/css">
    <![CDATA[
    .firstb { fill: yellow; }
    .secondb { fill: red; }
    ]]>
  </style>
  <path class="secondb" d="M42 27v-20c0-3.7-3.3-7-7-7s-7 3.3-7 7v21l12 15-7 15.7c14.5 13.9 35 2.8 35-13.7 0-13.3-13.4-21.8-26-18zm6 25c-3.9 0-7-3.1-7-7s3.1-7 7-7 7 3.1 7 7-3.1 7-7 7z"/>
  <path class="firstb" d="M14 27v-20c0-3.7-3.3-7-7-7s-7 3.3-7 7v41c0 8.2 9.2 17 20 17s20-9.2 20-20c0-13.3-13.4-21.8-26-18zm6 25c-3.9 0-7-3.1-7-7s3.1-7 7-7 7 3.1 7 7-3.1 7-7 7z"/>
</svg>

Если хочется использовать внешние стили, с которыми в целом проще работать и поддерживать, то нельзя использовать <img> или background-image. Если вы используете <object>, то стоит добавить сноски к списку стилей, внутри SVG-файла (код прилагаю). Помните: если вы всё это сделаете, SVG не будет знать, к какому родительскому классу он относится (речь об <object>), поэтому не стоит применять этот способ для стилизации. Встроенные SVG не нуждаются в подобных дополнениях, следовательно и работать с таким методом в этом смысле немного проще.

Внешние стили

Your browser does not support SVGs
// Add to very start of SVG file before <svg>
<?xml-stylesheet type="text/css" href="style.css"?>
// In style.css
.firstb { fill: yellow; }
.secondb { fill: red; }

Мои знания Javascript очень маленькие, поэтому я дам только основные советы о том, как можно использовать JS для изменений в SVG. Если вы хотите вставить свои скрипты внутри SVG, то не забудьте обернуть их в <![CDATA[ ... ]]> снова, чтобы избежать ошибок анализа. Скрипты не будут работать если использовать <img> или background-image из-за мер безопасности (то есть чтобы предотвратить запуск потенциально вредоносного кода на вашей странице).

Когда вы работаете с внешним JS (то есть не встроенным в файл SVG), если у вас встроенные SVG, вы можете выбрать его как любой другой DOM-элемент. Если вы используете <object> вы можете выбрать его через contentDocument. Вы можете предложить ещё что-то более креативное, чем следующее, но вот пример:

Внешние скрипты

Your browser does not support SVGs
window.onload=function() {
  var object = document.getElementById("logoObject");
  var svgDocument = object.contentDocument;
  var svgb1 = svgDocument.getElementsByClassName("firstb");
  var svgb2 = svgDocument.getElementsByClassName("secondb");
  svgb1[0].setAttribute("fill", "yellow");
  svgb2[0].setAttribute("fill", "red");
};

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

Если размеры фиксированы, то единственная важная вещь, с которой нужно быть осторожным, если вы используете SVG как background-image — это то, что вам нужно быть уверенным, что background-size прописан, так как браузеры могут немного запутаться и или подрезать изображение, или сжать его, особенно, если отображаются размеры, отличные от оригинального размера изображения.

Когда вы изменяете размер SVG, нужно помнить о некоторых вещах (если используем background-image):

Объект

Работает, как ожидается, при width: 100%;

Your browser does not support SVGs

Встроенные

Ранее требовалась значение max-height для работы, в настоящее время работает, как ожидается. Помните, что Safari не так быстро отрисовывает изображения (если они сложные) при изменении размеров окна.

Img

Работает, как ожидается, при width: 100%;

Breaking Borders Logo

Background-image

Требует padding-bottom: #%;, чтобы сохранить пропорций изображения, иначе не отображается.

Когда требуется анимировать SVG существует несколько различных опций, которые можно использовать — анимирование SVG (основанное на SMIL-стандарте), CSS3-анимация или JS-анимация. SVG- и CSS3-анимации позволят сделать большую часть того, что вам захочется сделать, при этом SVG-анимация чуть мощнее, потому как имеет возможность «контролировать» некоторые особенности (или даёт доступ к некоторым возможностям). JS позволит относительно легко делать неплохую сложную анимацию, особенно используя такие библиотеки как Snap.svg. Это находится вне моих знаний о JavaScript, поэтому я позволю вам потестировать их демо версии, чтобы убедиться подходит ли это вам.

SVG-анимация даёт огромные возможности, но я не собираюсь останавливаться на этом подробно, и, честно говоря, я ни разу не использовал её. Я могу представить, что может быть полезно добавить некоторую хорошую анимацию для ваших иллюстраций, но честно говоря, с практической точки зрения, не каждый проект имеет на это достаточное время и бюджет. Если у вас всё же есть возможность, довольно просто начать экспериментировать с этим, есть много прекрасных онлайн-уроков. В основном можно добавлять дочерние элементы <animate> к любому контуру или форме в ваших SVG-файлах, которые позволят контролировать анимацию. Самое лучшее в этом то, что оно работает со всеми методами использования SVG. Internet Explorer не поддерживает SVG-анимацию, однако вы можете использоваться полифил, такой как FakeSmile для работы с IE.

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

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 68 65">
  <path fill="#4e86b1" d="M42 27v-20c0-3.7-3.3-7-7-7s-7 3.3-7 7v21l12 15-7 15.7c14.5 13.9 35 2.8 35-13.7 0-13.3-13.4-21.8-26-18zm6 25c-3.9 0-7-3.1-7-7s3.1-7 7-7 7 3.1 7 7-3.1 7-7 7z">
    <animate dur="2s" values="#000000; #4e86b1; #000000" keyTimes="0; 0.5; 1" attributeName="fill" repeatCount="indefinite"/>
  </path>
  <path d="M14 27v-20c0-3.7-3.3-7-7-7s-7 3.3-7 7v41c0 8.2 9.2 17 20 17s20-9.2 20-20c0-13.3-13.4-21.8-26-18zm6 25c-3.9 0-7-3.1-7-7s3.1-7 7-7 7 3.1 7 7-3.1 7-7 7z">
    <animate dur="2s" values="#4e86b1; #000000; #4e86b1" keyTimes="0; 0.5; 1" attributeName="fill" repeatCount="indefinite"/>
    </path>
</svg>

SVG-анимация

Object

Your browser does not support SVGs

Inline

Img

Breaking Borders Logo

Background-image

Обычно, когда мы хотим анимировать иконки или иллюстрации в вебе, мы это делаем для добавления интерактива, то есть при наведении мыши и тому подобное. Это подпадает под пункты «Управление с помощью CSS» и «Интерактивная SVG-анимация» из табличек выше, и следовательно, не работает ни с SVG-, ни с CSS3-анимациями при использовании <img> или background-image. Чтобы сделать интерактивную анимацию в SVG можно добавить begin="mouseover" и begin="mouseout". Для CSS3-анимации всё то же самое, что и в любом другом случае — добавьте классы для SVG-элементов и стилизуйте их при (наведении) hover. Обратите внимание на одну вещь — если вы хотите стилизовать анимации из внешнего списка стилей, они будут работать, как ожидается при использовании встроенного SVG. А при использовании <object> нужно сослаться на внешний список стилей из SVG в том числе.

Интерактивная SVG-анимация

Работает только с <object> или встроенным SVG.

Your browser does not support SVGs
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 68 65">
  <path fill="#4e86b1" d="M42 27v-20c0-3.7-3.3-7-7-7s-7 3.3-7 7v21l12 15-7 15.7c14.5 13.9 35 2.8 35-13.7 0-13.3-13.4-21.8-26-18zm6 25c-3.9 0-7-3.1-7-7s3.1-7 7-7 7 3.1 7 7-3.1 7-7 7z">
    <animate fill="freeze" dur="0.1s" to="#000000" from="#4e86b1" attributeName="fill" begin="mouseover"/>
    <animate fill="freeze" dur="0.1s" to="#4e86b1" from="#000000" attributeName="fill" begin="mouseout"/>
  </path>
  <path d="M14 27v-20c0-3.7-3.3-7-7-7s-7 3.3-7 7v41c0 8.2 9.2 17 20 17s20-9.2 20-20c0-13.3-13.4-21.8-26-18zm6 25c-3.9 0-7-3.1-7-7s3.1-7 7-7 7 3.1 7 7-3.1 7-7 7z">
    <animate fill="freeze" dur="0.1s" to="#4e86b1" from="#000000" attributeName="fill" begin="mouseover"/>
    <animate fill="freeze" dur="0.1s" to="#000000" from="#4e86b1" attributeName="fill" begin="mouseout"/>
  </path>
  </svg>

CSS3-анимация

Работает с <object> встроенным, также работает с внешним CSS, как было описано выше.

Your browser does not support SVGs
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 68 65">
  <style type="text/css">
    <![CDATA[
    .firstb { fill: #000; transition: fill 0.1s; }
    .firstb:hover { fill: #4e86b1; }
    .secondb { fill: #4e86b1; transition: fill 0.1s; }
    .secondb:hover { fill: #000; }
    ]]>
  </style>
  <path class="secondb" d="M42 27v-20c0-3.7-3.3-7-7-7s-7 3.3-7 7v21l12 15-7 15.7c14.5 13.9 35 2.8 35-13.7 0-13.3-13.4-21.8-26-18zm6 25c-3.9 0-7-3.1-7-7s3.1-7 7-7 7 3.1 7 7-3.1 7-7 7z"/>
  <path class="firstb" d="M14 27v-20c0-3.7-3.3-7-7-7s-7 3.3-7 7v41c0 8.2 9.2 17 20 17s20-9.2 20-20c0-13.3-13.4-21.8-26-18zm6 25c-3.9 0-7-3.1-7-7s3.1-7 7-7 7 3.1 7 7-3.1 7-7 7z"/>
</svg>

Можно создавать и использовать SVG-спрайты так же, как и в случае с PNG или background-image, чтобы получить преимущества в разрешении, или же можно шагнуть чуть дальше, используя дополнительные возможности SVG. Я не буду на этом подробно останавливаться, так как это становится довольно сложным (это комплексный подход), и я лично никогда не сталкивался с необходимостью использовать его. Основное преимущество — это использовать всю мощь SVG с меньшим числом HTTP-запросов.

Существует два подхода, которые можно использовать — в первом вы определяете все иконки внутри тега <symbol> в SVG, но скрываете их. Затем обращаетесь к каждому, когда это потребуется, используя элемент <use>, ссылаясь на <symbol> с xlink:href="#id". Второй подход — это использовать в SVG viewbox атрибут, чтобы обрезать рабочую область (область в SVG, которая видна) вокруг определённой области. Эти оба способа достаточно продвинутые, поэтому задумайтесь о них, только если решите использовать.

Если вы хотите узнать как внедрить эти техники, то обратитесь к ссылкам на ресурсы, приложенные ниже, особенно к статье Sara Soueidan’s на 24ways.

Если вы хотите использовать SVG-спрайт как PNG-спрайт с CSS, то нужно добавить атрибуты width и height рядом с атрибутом viewBox в SVG-коде. Традиционно, в процессе оптимизации они удаляются, потому что обычно они не нужны. Однако, в этом случае эти параметры требуются для IE9 & 10 чтобы корректно порезать их на части. Это происходит потому, что эти браузеры принимают размеры высоты и ширины, указанные в CSS, за размер изображения, а на самом деле это как раз размеры требуемой части изображения (а не всего изображения в целом).

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

Представьте, что вы большой бренд и вы хотите, чтобы ваш логотип использовался правильно, независимо от отображаемого размера. И вам это удастся. Только вставьте нужное медиавыражение, и вы сможете менять формат в зависимости от размера изображения на дисплее. Это работает со всеми реализациями, кроме background-image, и во всех браузерах (но помните: IE9—11 может игнорировать некоторые контрольные точки). Поэкспериментируйте со слайдером ниже, чтобы увидеть на живом примере, как это может работать:


К слову о запасных вариантах. SVG поддерживается во всех современных браузерах, но если ещё требуется поддержка IE8, то нужно использовать запасные варианты, скорее всего, в виде PNG. Я не буду останавливаться на этом хотя бы потому, что уже пора прекратить пользоваться IE8 ¯\_(ツ)_/¯. В любом случае, если вам всё же нужны запасные варианты, всё усложняется довольно быстро, как и большинство вещей с SVG. Советую прочитать исчерпывающую статью Amelia Bellamy-Royds на CSS-Tricks.