Заметки с тегом «svg»

Ещё один вариант загрузки набора SVG-изображений

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

Цепочка проб и ошибок привела нас к такой схеме:

  1. Преобразуем SVG-файл в строку текста и присваиваем её какой-либо переменной.
  2. Объединяем JavaScript файл, который мы получили на предыдущем этапе, с другими JS-файлами, которые подключаются в <head>.
  3. В до первого использования SVG добавляем пустой элемент — плейсхолдер и вставляем в него содержимое SVG-строки.

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

<svg xmlns="http://www.w3.org/2000/svg" class="icon_circle">
  <use xlink:href="#circle"></use>
</svg>

Опишу все проделанные шаги подробнее.

Конвертирование SVG в JS

Этой задачей занимается Grunt.

grunt.registerTask('elements', 'Transform a SVG sprites to a JS file',
  function () {
    var LINE_LENGTH = 100, svg = [], i, l, content;

    content = grunt.file.read('assets/images/elements.svg');
    content = content.replace(/'/g, "\\'");
    content = content.replace(/>\s+</g, "><").trim();
    l = Math.ceil(content.length / LINE_LENGTH);

    for (i = 0; i < l; i++) {
      svg.push("'" + content.substr(i * LINE_LENGTH, LINE_LENGTH) + "'");
    }

    grunt.file.write('assets/_/js/elements.js',
      'var SVG_SPRITE = ' + svg.join('+\n') + ';');
  }
);

Из файла assets/images/elements.svg получается файл assets/_/js/elements.js.

Конкатенация

Файл elements.js должен быть загружен в <head> Наверняка у вас есть ещё несколько скриптов, которые тоже требуется загружать в начале страницы. Их можно объединить между собой чтобы уменьшить количество HTTP-запросов к серверу.

Добавление на страницу

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

<div id="elements-placeholder"
  style="border: 0; clip: rect(0 0 0 0); overflow: hidden;
    margin: -1px; padding: 0; position: absolute;
    width: 1px; height: 1px;"></div>

Плейсхолдер нужен для того, чтобы безопасно модифицировать DOM-дерево в процессе загрузки страницы. У него должен быть уникальный id. Я ещё добавил некоторые inline-стили, чтобы защитить страницу от эффекта «упячки», если контуры случайно окажутся не помещены в <defs>.

Заполняем плейсхолдер.

<script>document.getElementById("elements-placeholder").innerHTML = SVG_SPRITE;</script>

Замечания

  1. При формировании JS-файла для простоты полагается, что будет создана глобальная переменная SVG_SPRITE. Чтобы не загрязнять глобальную область, я рекомендую сохранить содержимое SVG в пространство имён приложения.
  2. Имена файлов жёстко прописаны в Grunt-задаче. Есть смысл задачу сделать конфигурируемой, если у вас в проекте несколько наборов SVG-пиктограмм.

Демонстрация

У нас в компании есть проект-шаблон, в котором можно детально посмотреть эту технику подключения SVG.

Комментарии к заметке: 12

Доступность SVG-пиктограмм

Когда вы используете inline-SVG без текстового пояснения (например, как пиктограмму на кнопке), то неплохо было бы снабдить это изображение заголовком. В SVG-документе есть описательные теги: <title>, <desc>, <metadata>. Для наших целей хорошо подходит тег <title>.

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>Document title</title>
</head>
<body>
  <p>This is a test page</p>

  <button>
    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 40 40" width="32" height="32">
      <title>Next page</title>
      <polygon points="15,7 11,11 20,20 11,29 15,33 28,20"/>
    </svg>
  </button>

  <p>Actually, there is no next page here.</p>

</body>
</html>

Содержимое <title> не отображается, но может использоваться вспомогательными технологиями.

Содержимое <title> не отображается, но может использоваться вспомогательными технологиями

Более того, каждый графический элемент может иметь собственный заголовок.

Таким образом, нарушая все запреты о наличии только одного тега <title> на странице, мы получили абсолютно валидный документ с несколькими тегами.

На странице с валидной разметкой могут присутстовать несколько тегов <title>

Комментарии к заметке: 4

Inline SVG для пиктограмм

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

Все изображения собраны в одном SVG-файле shapes.svg.

<svg xmlns="http://www.w3.org/2000/svg" width="0" height="0">
  <defs>
    <rect id="shape-rectangle" x="1" y="1" width="30" height="30"/>
    <circle id="shape-circle" cx="16" cy="16" r="24"/>
  </defs>
</svg>

Тут у нас две фигуры: квадрат и круг. Чтобы в дальнейшем мы могли сослаться на них, у каждой фигуры должны быть уникальные значения id. Забегая вперёд скажу, что их уникальность должна быть не только в пределах SVG, но и в пределах всего документа, где в дальнейшем будет отображаться эта иконка.

На заметку:

Все фигуры определены внутри тега <defs>. Они не будут отображаться, если попытаться открыть этот файл в каком-нибудь редакторе или просмотрщике. На них можно только сослаться. Несколько контуров могут быть объединены в одну фигуру с помощью тега <g>.

Затем в HTML можно сослаться на заготовленные контуры

<svg xmlns="http://www.w3.org/2000/svg"
      style="width: 32px; height: 32px;">
  <use xlink:href="shapes.svg#shape-rectangle"></use>
</svg>

Браузер отобразит квадрат в блоке размером 32 на 32 пиксела. Габариты блока, разумеется, нужно задавать с помощью CSS. В данном примере я указал их в атрибуте style только для наглядности.

<svg xmlns="http://www.w3.org/2000/svg"
      viewBox="-9 -9 50 50"
      style="width: 32px; height: 32px;">
  <use xlink:href="shapes.svg#shape-circle"></use>
</svg>

C кругом чуть сложнее так как он не вписывается в желаемые габариты 32 на 32 пиксела. Поэтому inline SVG нужно указать атрибут viewBox. Браузер сам правильно смасштабирует фигуру до размеров блока.

Подводные камни

При использование inline SVG нужно знать и помнить несколько особенностей.

Поддержка

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

Internet Explorer

Мы обнаружили, что ссылка на внешний SVG-файл с контурами не работает, если страница отображается в IE. Это известное поведение и исправить хаками его нельзя. Остаётся только явно включить этот SVG в HTML страницу. Соответственно при использовании в <use xlink:href="#shape-rectangle"></use> остаётся один якорь (для этого и нужна была уникальность id).

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

<script id="svg-loader">
  $.ajax({url: "shapes.svg", cache: true, dataType: "html"})
    .done(function (data) {
      $("#svg-loader").before(data);
      $(".svg-icon > use").attr("xlink:href", function (i, href) {
        return href.substr(href.indexOf("#"));
      });
    });
</script>

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

Ещё важно, чтобы в SVG разметке не было самозакрывающихся тегов. Нужно явно указывать закрывающий тег, если нет контента. Старые парсеры HTML-супа допускали лишь ограниченное количество самозакрывающихся тегов (<hr/>, <br/>, <input/> и т.п.) — поэтому мы не можем писать <div/>. Конструкции SVG могут поломать весь документ при определённом стечении обстоятельств.

Комментарии к заметке: 8

Изящная деградация для SVG картинок

Алексей Тен на своём сайте опубликовал интересную технику подмены SVG изображения на растровый вариант для браузеров, без поддержки SVG.

<svg width="96" height="96">
  <image xlink:href="svg.svg" src="svg.png" width="96" height="96"/>
</svg>

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

Замечу лишь, что деградация слегка агрессивная по отношению с старым версиям Safari (включая iOS), Opera Mobile и Opera Mini. Эти браузеры не распознают inline SVG, но прекрасно отображают SVG в виде внешнего файла, подключаемого через тег <img>.

PS: Крис Койер в свой статье сообщает, что в IE 9, 10 и 11 всё же есть дополнительный запрос альтернативной картинки, а так же приводит ещё несколько техник.

Оставте свой комментарий

Фоновый градиент с помощью SVG

Замечательный сервис Ultimate CSS Gradient Generator наряду с CSS3 градиентами для всех браузеров создает SVG файл для IE9. Он внедряется в CSS с помощью Data URI в base64 кодировке.

background: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0i
MS4wIiA/Pgo8c3ZnIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2Zy
Igd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgdmlld0JveD0iMCAwIDEgMSIg
cHJlc2VydmVBc3BlY3RSYXRpbz0ibm9uZSI+CiAgPGxpbmVhckdyYWRpZW50IG
lkPSJncmFkLXVjZ2ctZ2VuZXJhdGVkIiBncmFkaWVudFVuaXRzPSJ1c2VyU3Bh
Y2VPblVzZSIgeDE9IjAlIiB5MT0iMCUiIHgyPSIwJSIgeTI9IjEwMCUiPgogIC
AgPHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iIzdkN2U3ZCIgc3RvcC1v
cGFjaXR5PSIxIi8+CiAgICA8c3RvcCBvZmZzZXQ9IjEwMCUiIHN0b3AtY29sb3
I9IiMwZTBlMGUiIHN0b3Atb3BhY2l0eT0iMSIvPgogIDwvbGluZWFyR3JhZGll
bnQ+CiAgPHJlY3QgeD0iMCIgeT0iMCIgd2lkdGg9IjEiIGhlaWdodD0iMSIgZm
lsbD0idXJsKCNncmFkLXVjZ2ctZ2VuZXJhdGVkKSIgLz4KPC9zdmc+);

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

Раскодируем base64

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

echo PD94bWwgdm…IgLz4KPC9zdmc+ | base64 -d > /tmp/g.svg

Файл g.svg.будет содержать SVG и его можно будет отредактировать в любом текстовом редакторе.

<?xml version="1.0" ?>
<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%"
        viewBox="0 0 1 1" preserveAspectRatio="none">
  <linearGradient id="grad-ucgg-generated" gradientUnits="userSpaceOnUse"
          x1="0%" y1="0%" x2="0%" y2="100%">
    <stop offset="0%" stop-color="#7d7e7d" stop-opacity="1"/>
    <stop offset="100%" stop-color="#0e0e0e" stop-opacity="1"/>
  </linearGradient>
  <rect x="0" y="0" width="1" height="1" fill="url(#grad-ucgg-generated)" />
</svg>

Для декодирования можно воспользоваться любым online-сервисом, коих в интернете великое множество.

Задаем размеры изображения

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

<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="200px"
        viewBox="0 0 1 1" preserveAspectRatio="none">

Важно всегда явно задавать единицы измерения величин даже если это пикселы.

Кодируем SVG в base64

base64 -e /tmp/g.svg

Получившийся в результате набор символов копируем из консоли в CSS файл вместо исходного. После этого нужно удалить все переносы строк, которые могли образоваться внутри закодированных данных. Формат base64 игнорирует пробельные символы, но внутри Data URI пробельные символы недопустимы.

Настраиваем параметры фона

Так как фон уже не заполняет весь блок целиком, то нужно не забыть настроить его позиционирование и повторение.

Комментарии к заметке: 2