Pull to refresh

Еще один способ использовать SVG в React. На этот раз удобный

Reading time4 min
Views35K

SVG-изображения можно вставлять непосредственно в html код, можно использовать символьные спрайты, теги <img>, <object> и даже <iframe>. Можно подключать SVG через data-url, css-backgrounds, css-filters и еще множеством способов. Но чтобы полноценно использовать всю суперсилу SVG, необходимо вставлять SVG-изображения непосредственно в html-разметку. Хотя на самом деле есть еще один способ. И он удобный.

Суперсила SVG

Одна из самых сильных сторон технологии SVG - возможность манипулировать стилями изображений через css. Это особенно важно для иконок, которые обязательно захочется подсветить на hover без использования дополнительных изображений.

Чтобы иметь возможность перекрашивать SVG через css, необходимо либо вставить SVG непосредственно в html, то есть «заинлайнить» их, либо использовать отдельный файл, символьный спрайт. Спрайт также должен быть вставлен в DOM. Ни одна другая техника не позволяет получить доступ к SVG из css. Рассмотрим эти техники подробнее.

Инлайнинг

Вставка SVG непосредственно в html это одновременно и самая простая и самая мощная с точки зрения кастомизации и манипуляции изображением техника.

<span>
  <svg viewBox="0 0 16 16" width="16" height="16">
    <!-- SVG -->
  </svg>
</span>

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

  1. Если изображение большое, вся страница будет загружаться дольше

  2. Использование этой техники в js-фреймворках, даже с такими крайне мощными инструментами, как SVGR, сильно увеличивает размер js-бандла.

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

Спрайт символов

Другой способ встраивания SVG, который позволяет стилизовать изображения через css, предполагает использование спрайта, состоящего из элементов <symbol>. Контент изображения хранится в отдельном скрытом файле и подключается в нужное место на странице через тег <use>.

<!-- Спрайт символов наверху страницы -->
<svg style="display:none">
  <symbol id="icon" viewBox="0 0 16 16" width="16" height="16">
    <!-- SVG -->
  </symbol>
</svg>

<!-- Использование -->
<span>
  <svg><use href="#icon" /></svg>
</span>

А если вы, как и Microsoft, не поддерживаете IE11, спрайт символов можно загружать по HTTP.

<svg>
  <use href="https://cdn.net/sprite.svg#example"/>
</svg>

К сожаление, использовать такой подход напрямую, то есть загрузить по HTTP содержимое отдельного SVG-изображения не получится, нужен именно спрайт. Вот так работать не будет:

<svg>
  <use href="https://cdn.net/icon.svg"/>
</svg>

К изображениям, подключенным через спрайт, можно получить доступ через css , правда, есть ограничения. Стилям не «пробиться» через shadow-boundary, границу, за которой начинается shadow-DOM и которая описывается тегом <use>.

Хорошие новости в том, что каскад продолжает работать, можно менять атрибуты fill и использовать currentColor в любом свойстве. Для большинства кейсов с иконками этого более чем достаточно:

<!-- Спрайт -->
<svg style="display:none">
  <symbol id="icon">
    <path fill="currentColor">
  </symbol>
</svg>

<!-- Добавляем иконке css-класс -->
<span>
  <svg class="icon"><use href="#icon" /></svg>
</span>

<!-- Красим SVG через css -->
<style>
  .icon {
    color: rebeccapurple;
  }
</style>

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

Компромисс

Здесь не обойтись без компромисса. Нужно решение, которое загрузит на страницу только необходимые SVG-изображения, не будет рендерить их через javascript и позволит стилизовать их через css.

Таким решением может быть браузерный fetch! Нам нужен инструмент, который

  1. Загрузит SVG-изображения в браузере через fetch и закеширует их

  2. Добавит их в символьный спрайт

  3. Предоставит API для использования загруженных изображений в произвольном участке страницы через тег <use>.

Первым о таком решении задумался Chris Coyier еще в 2015 году, но оно так и не было оформлено и ждало своего дня. Сегодня этот день настал.

Handy SVG

Реализация называется Handy SVG, исходники и документация доступны на github, пакет публикуется в npm. Вам достаточно установить его:

npm i handy-svg

И использовать в React:

import {HandySvg} from 'handy-svg';
import iconSrc from './icon.svg';

export const Icon = () => (
    <HandySvg
        src={iconSrc}
        className="icon"
        width="32"
        height="32"
    />
);

Или в любом другом проекте с ванильным javascript:

import {injector} from 'handy-svg/lib/injector';

const src = "https://cdn-server.net/icon.svg";

// Загружает изображения и добавляет их в спрайт
injector.load(src);

// Доступ к id изображения
const id = injector.getId(src);

// Использование
const svg = `<svg class="icon"><use href="#${id}" /></svg>`;

Никаких проблем со стилизацией не возникнет:

.icon {
  color: rebeccapurple;
}

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

Конечно это решение имеет ряд ограничений. Например, почти невозможно избавиться от FONI (Flash of No Icons), состояния загрузки, при котором на странице отсутствуют иконки. Тем не менее, из всех возможных способов работы с SVG-изображениями в вебе, этот представляется наиболее удобным.


Пул-реквесты приветствуются и я буду рад любому фидбеку.

Tags:
Hubs:
Total votes 9: ↑7 and ↓2+8
Comments15

Articles