Evil Icons: как мы изобретали SVG-иконки



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

    Как я уже сказал, нам нравились иконочные шрифты. Наверное, нравились бы и дальше, если бы не десяток проблем. Зато шрифт легко подключить и использовать: кладем его и стили в папку, пишем в верстке <span class="icon-search"></span> — и в нужном месте появляется пиктограмма. Удобно!

    Идея и задача


    Удобно, только будущее за вектором. Когда мы задумались о переходе на SVG, решили начать не с технологий, как мы, программисты, любим, а с интерфейса. Чего мы хотим от графики для веб-проектов? Как нам было бы удобно работать с ней? Отличается ли это удобство для разработчика и дизайнера? Если делать набор SVG-иконок как самостоятельный продукт, какие преимущества у него должны быть?



    Вопросы помогли понять задачу. Мы хотим:
    • Легко подключать иконки к любому проекту — от блога на WP до веб-приложения на Rails.
    • Использовать декларативный стиль: «Вот здесь должна быть иконка шестеренки».
    • Менять цвет и трансформировать иконки с помощью CSS.
    • Поменьше работать руками, побольше перекладывать на машину.

    Начало работы


    Из всех возможных способов подключения я выбрал спрайт как самый надежный. Выглядит он примерно так:

    <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="display:none">
    <symbol id="foo-icon" viewBox="0 0 50 50">...</symbol>
    <symbol id="bar-icon" viewBox="0 0 50 50">...</symbol>
    ...
    </svg>
    

    А иконка foo показывается вот так:

    <svg><use xlink:href="#foo-icon"></use></svg>
    

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

    Мы разрабатываем на Ruby on Rails, поэтому автоматизировали процесс средствами этого фреймворка. Я написал rake-задачу для генерации спрайта из набора SVG-файлов (после их предварительной очистки с помощью svgo) и пару хелперов для подключения скомпилированного спрайта и рендеринга иконок.

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

    Дизайн


    Передаю клавиатуру romanshamin — дизайнеру проекта.

    Привет, Хабр!

    Я сформулировал задачу так: сделать практичный набор пиктограмм. Только базовые иконки, нужные в обиходе большинства сайтов. Лупа — поиск, крестик — закрыть, вот это все. Второе ограничение: не выпендриваться с авторским стилем. Наоборот, пусть стиль иконок растворится в мейнстриме и так подойдет большинству.

    Важный сосед — текст


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

    1. Набор стал крупнее. 16—24 пикселей сейчас, против 12—14 в прошлом.
    2. Шрифты стали разнообразнее. Прощай, Ариал.
    3. Благодаря экранам с высокой плотностью пикселей для основного текста стали чаще выбирать светлые начертания. Light и extra light соперничают с regular.



    Источник: Smashing Magazine. Раздел Average Font Size For Body Copy за 2009 и 2013.


    Для сайтов и приложений привычнее шрифты без засечек, поэтому исключаю антиквы. Итак, портрет: светлый гротеск большого размера. Выбираю Helvetica Neue Light — пусть на первых порах отдувается за все шрифты интернета.

    А как же нормальные и полужирные начертания?

    Нужно больше ограничений


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

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


    Размер, оптика и четкость


    Теперь нужно выбрать размер базовых квадрата и круга. Эти фигуры определят оптический вес каждой пиктограммы. Оптический вес — интересная штука. Если поставить рядом квадрат высотой 15 пикселей и круг такого же диаметра, то круг будет казаться меньше. Чтобы компенсировать эффект, круг нужно увеличить. Для нашего квадрата близким по оптическому весу будет круг диаметром 17 пикселей.


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

    Мы же имеем дело с экраном — средой низкого разрешения. Даже на ретине опытный глаз различает пиксели. В графическом мире пиксели как гравитация — влияют на все видимое глазом. Фигура на экране выглядит четко, если границы ее контуров совпадают с границами пиксельной сетки экрана. Если пиксели не учитывать, получится размазня (на рисунке слева). Поэтому нужно выбрать базовый размер — чтобы знать достаточно ли пикселей для создания четких форм.


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

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


    Победил круг размером 17 пикселей. Контурный круг прошел мой ценз для надписей от 15 до 20 пикселей. Текст вне этого диапазона выглядит или тоньше контура иконки, или толще. Залитый круг, у которого нет ограничения на толщину штриха, подходит к надписям от 13 до 22 пикселей — дальше уже становится маловат.

    Забавно, что в скрипте число 17 я поставил как значение по-умолчанию для диаметра. Знать бы сразу что угадал — сэкономил бы время :-)

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

    Спасибо за внимание!

    Проблемы, с которыми мы столкнулись


    В момент написания гема я избавился от svgo в пользу малоизвестного svg_optimizer, написанного на Ruby. Впоследствии эта замена стала причиной появления зубцов при рендеринге иконок — оптимизатор чересчур старательно оптимизировал структуру SVG и дублировал код иконки. В результате вместо одной получались две, наложенные друг на друга.

    После этого прокола svg_optimizer заменили на svgo, а у svgo отключили плагин mergePaths, портящий форму иконок.

    Еще мы обернули иконку в дополнительный элемент, на это есть две причины. Первой проблемой стало нежелание jQuery менять классы SVG-элемента (с ванильным JS все работает отлично). А вторая проблема заключается в том, что события непосредственно на SVG-элементе не ловятся — нужно слушать их на обертке.

    Из нерешаемого в данный момент: в Сафари контуры слегка «пожеваные».

    Evil Icons


    Мы сразу делали иконки по принципам разработки больших продуктов. Определяли целевую аудиторию и конкурентные преимущества. Проект изначально предполагался как свободный и бесплатный. Несмотря на это, мы спрашивали себя: если бы мы брали за иконки деньги, то что в них должно быть хорошего, чтобы человек согласился заплатить? Это помогало держать ум в тонусе и не отвлекаться на «просто прикольные фичи».

    Мы сделали предварительный анонс и получили первую обратную связь. Благодаря ей буквально за пару дней иконки стали заметно лучше. Пользуясь случаем, благодарим всех, кто указывал на косяки и помогал их исправлять. Из-за вас, ребята, мы любим опенсорс, спасибо! :-)

    Теперь настало время поделиться с хабражителями.

    Представляем Evil Icons — набор бесплатных SVG-иконок для сайтов и веб-приложений. В комплекте: Node.js пакет и Ruby гем для разработчиков, файлы .ai и .sketch для дизайнеров.

    evil-icons.io

    Скоро выпустим дополнительные стили, дадим возможность добавлять в набор собственные иконки. Подумываем сделать плагины для Grunt и Gulp, а также подключение через CDN.

    * * *

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

    Бесспорно, у SVG свои проблемы. Однако, очень похоже, что ситуация будет выправляться. Браузеры будут все лучше поддерживать SVG, что видно уже сейчас. Экранов с высокой плотностью пикселей станет больше. Через пару лет, может быть, появятся мониторы с еще более высокой плотностью пикселей. Тем, кто работает с растром, придется готовить ассеты в @2x—@Nx и кто знает, насколько большим будет N.

    Evil Icons — это самая доступная на текущий момент возможность перейти на SVG уже в вашем следующем проекте.
    Share post

    Similar posts

    Comments 55

      +1
      Посмотрите в safari 10.10, похоже из-за багов его рендеринга SVG все очень плохо.
        0
        Это мой основной браузер, что там не так?
          0
          Скрин с «помятыми» кругами
            0
            видно что сглаживание странно себя ведет
              0
              Полагаю, это из-за очень сильного округления — там округление до одного знака после запятой, а это заметные искажения.
        • UFO just landed and posted this here
            +5
            Ответ очень прост — не использовать, если у тебя такие старые браузеры ;). Вон в Китае до сих пор IE 6 в трнедах, но это не значит, что все проекты должны под это быть рассчитаны.
              0
              Увы, это зависит от бизнес-задач клиента, а не представлений о том что такое хорошо, а что такое плохо.
                +4
                А я и не говорил, что это хорошо или плохо. Понятно, что если ты разрабатываешь ПО для самолётов, то не можешь использовать наработки веб-программирования, потому что там условия задачи другие. Со старыми браузерами та же история — логично, что нельзя использовать современные наработки, поэтому комментарии в стиле «а как я буду это использовать под IE 6» не имеют смысла. Это всё равно, что спрашивать — а как я буду использовать SVG, разрабатывая ПО для слепых. Никак, в твоём мире веб-разработки этого нет, такие условия задачи.
                  0
                  Ну, шестерка даже в Китае уже где-то в районе 1% болтается, это как на NN 4 ориентироваться, а семерка даже ниже.
                  На IE8 уже как-то можно развернуться: SVG плагин, вебфонты, VML
                    0
                    SVG плагин был и раньше, но он устарел чуть ли не до выхода восьмёрки. Опять же веб-фонты и VML тоже были раньше, и в шестёрке. Raphaël.js был написан для трансляции SVG в VML. Но восмёрка всё же неудобна по многим параметрам вроде корявой поддержки DOM и неподдержки многих современных фич.
                      0
                      Немного уточню:
                      Raphaël.js был написан для трансляции SVG в VML.
                      Не совсем так. Raphaël предоставлял адаптер и его API было в большинстве скопировано с SVG для удобства.
              • UFO just landed and posted this here
                0
                Я пока еще не смог придумать нормальный фоллбек: важны масштабируемость и смена цвета — ради них все и делается. Но я экспериментирую в этом направлении, возможно, что-то получится. Или не получится.
                • UFO just landed and posted this here
                    +2
                    А стоит ли добавлять поддержку IE 8? В тот момент, как мы закончим, он уже совсем уйдёт с рынка.
                      0
                      Экспериментирую just for fun, серьезно возиться пока не планирую.
                    +1
                    Evil Icons в текущем виде — это минимально функциональный продукт. Мы сделали достаточно, чтобы пиктограммы можно было внедрять в современные проекты или прототипы. Поддержку ИЕ, как и множество других задач, мы постепенно будем делать со временем.

                    Если вам нужен ИЕ8 прямо сейчас, иконки в текущей версии вам не подходят, простите.

                    Мы обязательно напишем вам, когда сделаем.
                    • UFO just landed and posted this here
                      0
                      Вот вам пара фаллбеков —
                      Есть еще простой фалбек —
                      1. парсите все иконке на странице
                      2. отправляете на сервер
                      3. сервер рендерит на PhantomJS и отдает png изображение
                      4. проставляете пнг изображения соответствующим иконкам

                      Плюс этого фалбека состоит в том что сервер рендерит именно тот цвет и размер которые нужны для конкретной иконки.
                      И можно рендерить заранее, да. Еще при использовании данного фалбека удобно класть svg иконку внутрь дива. <symb.... Часто именно этот див является спасительным мостиком между html и svg разметками.

                      +1
                      Под mac-chrome svg анимация съедает одно ядро напрочь
                        0
                        А мак у вас какой? У меня на MBP 15" middle 2011 и MBPR 15" late 2013 все хорошо.
                          0
                          MBP 13" 2014 топовый

                          Открыл страничку со спиннером и получил печку с просадом аккумулятора.
                            +1
                            А у вас сафари не открыт, случаем? Потому что потребление ресурсов для отдельных сайтов в Activity Monitor показывается только для сафари.
                              0
                              Да, это картина для двух браузеров сразу, в которых из сколько ни будь затратных по ресурсам вкладок были открыты только evil icons, сафари кушал >20%, хром с метлой кушал >30%
                                0
                                Спасибо, посмотрю, что там.
                              +1
                              В линуксе такая же фигня
                              image

                            +2
                            У меня то же. Mac mini 2,3 GHz Intel Core i7 16 GB 1600 MHz DDR3 Chrome 39.0.2171.95

                            Решение под катом
                            Проблема в пейнт шторме вызванном анимацией (зеленные блоки это перерисовка)

                            image

                            Если записать таймлайн — то можно увидеть примерно ту же картину

                            image

                            Уберем шторм:

                            Не смотря на отдельные композитные слои у svg элементов (оранжевые рамки),

                            image

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

                            Соответственно простое решение — применить анимацию не к svg элементу — а к самой иконке — то есть к диву. Это вынесет сам див на отдельный композитный слой и vo a la! После изменения можете посмотреть таймлайн еще раз — он будет чистым

                            image

                            а пейнт шторм не будет вас больше беспокоить (пейнта нет совсем)

                            image

                              0
                              Отличная работа! Спасибо, фикс будет уже в следующем релизе.
                                0
                                всегда пожалуйста! :)
                            0
                            почему вам стоит переходить на SVG уже в следующем проекте
                            Надеюсь, разработчики habrastorage.org таки согласятся. Грустно без SVG на хабре
                            • UFO just landed and posted this here
                                +1
                                Я только сегодня страдал без маркдауна.
                                • UFO just landed and posted this here
                              –1
                              Один из недостатков SVG огромный размер. Это так на примитивах он выигрывает и то вот относительно несложные формы даже пожатые svgz… могут вызывать вопросы.
                              Хорошо бы еще про это упомянуть.
                                +1
                                А есть примеры SVG с большим размером? Ну вот чтобы хуже других форматов было.
                                  0
                                  Возьмите любые иконки не flat стиля, а чуть с большей детализацией. Размер там вырастает сразу.
                                    0
                                    Использую SVG не только для иконок, а вообще для многой графики. Проблем с размерами еще ни разу не было, в растре гораздо тяжелее все.
                                  0
                                  svgz использовать вовсе не обязательно, gzip траффика стандартное явление, максимум нужно указать что .svg тоже под него попадает, это уже компрессия ~80%.
                                  Плюс мне попадалось минимум два svg оптимизатора, которые позволяют выиграть до ~50% размера исходника.
                                  Если иконка нормально «ложится» на вектор, то больше оверхеда будет вокруг самого факта фетча ресурса, чем ± 10 килобайт его размера.
                                  0
                                  А знает кто-нибудь способ заставить IE понимать ссылки вида #foo-icon для внешних SVG файлов (не инлайновых)?
                                    0
                                    Поясните вопрос, вы про IE8?
                                      0
                                      Нет, IE11. Чтобы так подключать:

                                      <svg><use xlink:href="//assets.example.com/sprite.svg#foo-icon"></use></svg>
                                      

                                      Приходит в голову подгрузить файл через ajax и вставить в <body>, но это довольно уродливо.
                                        +1
                                        Подключать SVG как внешний файл — не очень хорошая идея, потому что у Blink были проблемы с таким подключением. В целях кеширования мы хотели сначала сделать подключение через js (который будет вставлять строку со спрайтом в документ), но передумали. Потому что оверхед на получение файла со спрайтом может быть больше, чем вес самого спрайта. В таком случае иконки будут показаны не сразу. В конце-концов, сотня килобайт — пустяки по сравнению с растровой графикой.
                                          +1
                                          Вот, например, bugs.webkit.org/show_bug.cgi?id=105904
                                      +1
                                      В кои-то веки взвешенная статья про svg — а не «ура! вектор бесконечно масштабируется, потому маст хев!»

                                      Но я все-таки ориентировался бы в качестве основного на начертание regular, а не light. Даже на ретине светлое смотрится нарочито модненько и непрактично, есть ощущение, что скоро эта мода пройдет (я надеюсь!)
                                        0
                                        Тут, конечно, больше дело вкуса. В первой итерации мы сделали минимально необходимые вещи, в будущем набор regular все равно будет. Потому что каким бы легким не был легкий набор, он не во всех случаях подходит.
                                        0
                                        Смотрел пару дней назад, но так и не понял до конца как подключить к nodejs проекту. Может объясните для особо одарённых?
                                        0
                                        А как вы планируете бороться с размытостью векторных иконок из-за «нецелых пикселей»?

                                        Суть проблемы: даже если контуры иконки идеально попадают в пиксели, в верстке случаются моменты, когда вычисленные размеры всех блоков до иконки не являются «целопиксельными». Например, при использовании размеров в «em» или безразмерного line-height, высота блока может получиться 100,5px. И тогда иконка не будет выровнена по пикселям. То же самое по ширине.

                                        Такое происходит в IE и Firefox, в Chrome инлайн SVG всегда попадает в пиксели.

                                        Размытие иконки в IE
                                        Размытие SVG из-за дробных пикселей


                                        Если вставлять SVG-иконку через background-image, она всегда попадает в пиксели, но ее уже нельзя перекрашивать или анимировать.
                                          0
                                          В ближайшее время не планируем бороться :)

                                          вычисленные размеры всех блоков до иконки не являются «целопиксельными»

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

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

                                          при использовании размеров в «em» или безразмерного line-height

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

                                          image

                                          Мы сделали все, чтобы разработчик получил четкие иконки: правильно их нарисовали и дали предустановленные размеры, которые гарантируют попадание в пиксельную сетку. Задавайте иконкам фиксированные размеры и они будут четкими.
                                            0
                                            Если мы правильно поняли, когда изучали проблему, это происходит, только если изменять ширину браузера.

                                            Не только:

                                            image

                                            Так пиктограммы отображаются на Вин 7 и 8. В ИЕ11 они также размыты. Мониторы 1920 и 1440 пикселей в ширину. Окна браузеров максимизированы и их ширина не менялась после загрузки страницы.

                                            Иногда можно добиться нормального отображения, изменяя ширину окна браузера.
                                            Чтобы пиктограммы всегда отображались нормально, нужно добавить пару свойств:

                                                transform: translateX(0); /* ФФ */
                                            -ms-transform: translate(.5px, -.3px); /* ИЕ */
                                            

                                            Возможно, есть способ и получше :)
                                              0
                                              Оу, очень круто и хитро. Спасибо! Жаль, что при изменении ширины окна для IE это не работает. Если у вас есть аккаунт на гитхабе, напишите мне в личку — я упомяну вас в фиксе.
                                                0
                                                Решение для ИЕ оказалось слишком магическим :\ Надеюсь, у пользователей с ним всё будет хорошо.
                                                Зато лисичке помогли надёжно, судя по тестам.
                                                В личку написал.
                                          0
                                          SVG Rendering In Browsers — статья на английском об особенностях рендеринга SVG.
                                            0
                                            svg4everybody — polyfills для использования <use/> с внешними файлами (Safari 6, IE 6+)

                                            Only users with full accounts can post comments. Log in, please.