Спрайти SVG

<svg>
    <symbol id="icon-envelope" viewBox="0 0 22 16">
        <path
            style="fill:none;stroke:#ffa3ea;stroke-width:1"
            d="M 18,2.5 h -16.5 l 10,8 10,-8 v 12 h -20 v -8"></path>
    </symbol>
</svg>

SVG, абревіатура від Scalable Vector Graphics (Масштабована Векторна Графіка), — це далеко не нова технологія, яка чудово підтримується серед усіх відомих мені браузерів, широко використовується в Мережі. Виникає спокуса відмовитися від растрової графіки цілком — не люблю я ті пікселі :)

Нажаль, не всі види графіки можна масштабувати. Наприклад фото. У комп'ютерній графіці є ще одна сфера, де досі безроздільно панували пікселі — спрайти. Далі, коли йтиметься про спрайти, ми матимем на увазі CSS-спрайти.

Спрайти і SVG

Спрайти дозволяють зменшити кількість HTTP-запитів для графічних елементів, коли всі вони містяться в одному файлі, а потрібний демонструється позиціонуванням за допомогою CSS того єдиного файла в обмеженому розміром елемента віконці. Спрайти SVG, строго кажучи, спрайтами називаються лише умовно — вони організовані інакше. Графічні елементи задекларовані у вузлі дерева DOM і викликаються в потрібному місці. Розгляньмо зразок, взятий з ярликів соціальних мереж внизу сторінки.

Визначення шаблонів

Дивись, будь ласка, код SVG:

Визначення шаблонів
<svg style="display:none">
    <symbol id="icon-fb" viewBox="0 0 16 16">
        <path
            style="stroke-width:1;fill:none;[...]"
            d="M 1.327921,0.5 C 0.8706167,0.5 0.5,0.8705 [...] z"></path>
    </symbol>
    <symbol id="icon-tw" viewBox="0 0 16 16">
        <path
            style="stroke-width:1;fill:none;[...]"
            d="m 11.546616,8.0162225 c 0.499719,0 0.929144 [...] z"></path>
    </symbol>
</svg>

Кількість HTTP-запитів зменшена до 0 (нуля), бо елемент вбудовано в документ HTML. Хочу підкреслити цю вже другу перевагу спрайтів SVG (перша — масштабованість, ясна річ). Так, я знаю, що растрові картинки можна вбудовувати в HTML за допомогою Data-URL, але повторне використання неможливе, тож для спрайтів це малопридатне.

Між іншим, не забудь style="display: none" для SVG. Інакше елемент використає відведені йому за промовчанням 300x150 пікселів на екрані, хоча й не покаже додних зображень, бо елемент <symbol> не вібражаєтья у документі. Атрибут [id] обов'язковий, якщо хочеш пізніше використати той символ — треба ж на щось послатися.

Посилання на шаблон

Використання графічного елемента, визначеного тегом <symbol>:

Використання визначених елементів
<svg>
    <use href="#icon-fb" xlink:href="#icon-fb"></use>
</svg>

Елемент <use> посилається на відповідний <symbol>, визначений і вбудованй деінде в дереві. xlink:href повторюю для підтримки SVG v1.1. Щоправда, v2 має дуже широку підтримку, але ці кілька літер не шкодять, тому тримаю про всяк випадок, тим паче, що не додаю то вручну, а користуюся шаблоном від CMS.

<symbol> проти <defs>

Якщо шукатимеш в Мережі інформацію на тему, то знайдеш два способи визначення: з тегом <symbol> і <defs>. Ті, що пропонують останній варіант, схоже, просто йдуть за порадою з документації MDN:

Рекомендується завжди, коли можливо, визначати елементи, на які посилаються, в тезі <defs>.

І він таки працює. Перевірено. Та я рекомендую перший, бо його створено власне для нашої мети:

Елемент <symbol> використовується для визначення шаблонів графічних об'єктів, які можуть відтворюватися елементом <use>. Використання елементів <symbol> для графіки, що багатократно повторюється в поточному документі, додає структурованості і семантичності.

На додачу <symbol> підримує два корисних атрибути з промовистими назвами: preserveAspectRatio і viewBox.

Ще я бачив комбінований підхід:

<svg style="display:none">
    <defs>
        <symbol id="icon-fb" viewBox="0 0 16 16">
            <path
                style="stroke-width:1;fill:none;[...]"
                d="M 1.327921,0.5 C 0.8706167,0.5 0.5,0.8705 [...] z"></path>
        </symbol>
        <symbol id="icon-tw" viewBox="0 0 16 16">
            <path
                style="stroke-width:1;fill:none;[...]"
                d="m 11.546616,8.0162225 c 0.499719,0 0.929144 [...] z"></path>
        </symbol>
    </defs>
</svg>

Але не бачу для цього виправдань в документації, яку читав на MDN та з інших джерел.

Підсумки

Переваги.

Не бачу тепер причин використання растрових спрайтів замість SVG. Виходжу з того, що ти ж не кодуєш для IE < 9. Кодуєш? Ой, мої співчуття... Для усіх інших найочевдніші переваги наступні:

  1. масштабовані зображення
  2. 0 (нуль) додаткових HTTP-запитів

Наразі все виглядає гарно й кольорово. Може є якісь недоліки чи обмеження?

Обмеження

Люблю SVG не лише за масштабованість, але також за можливість втручання в дерево DOM з допомогою Джаваскрипт і анімацію цих маніпуляцій. Бачив, як схрещуються рисочки кнопки меню?#1

Зробімо щось подібне для наших іконок соціальних мереж... Чекай... Може поганий CSS-селектор чи що... Ні. Не працює :(

Чому? На те є причина. Коли я посилаюся на графічний шаблон (<symbol>) тегом <use>, я не отримую можливості змінювати його. Тобто про відтворені копії треба думати не як про самостійні елементи дерева, а як відображення у дзеркалі того первісного елемента. І ти не маєш доступу до нього ні з CSS ні з JS, а жодна подія, що відбувається з елементом, не транслюється на його шаблон. А й справді, як я сподівався маніпулювати вмістом шаблона, не відобразивши зміни в усіх відтворених зображеннях? Тобто треба просто це прийняти. Якщо хочеш маніпулювати деревом DOM елемента SVG, треба мати повноцінний незалежний вузол SVG.

Хоча я відніс це до обмежень, обмеженням це можна назвати лише порівнюючи із незалежним SVG-вузлом, а не з растровими спрайтами. Порівнюючи з растрами, межі лише розширюються.

Якщо виникли питання, поради, десь є заперечення, хочеться підказати, будь ласка, сконтактуйся зі мною.

Дякую за читання!

#1 Станом на весну 2018 анімації можна бачити в Хромі. FF Nightly анімував атрибут d елемента <path> раніше, але в якийсь момент змінив поведінку. Ймовірно, через відсутність стандарту і надто широкі можливості інтерпретації.