Frontender Magazine

Переосмысление отзывчивого SVG

Если вы ещё не знакомы с техникой отзывчивых иконок Джо Харрисона (Joe Harrison), то, скорее всего, будете впечатлены так же сильно, как и я, когда впервые открыл её для себя. В этой статье я бы хотел исследовать, что мы можем делать с SVG, кроме традиционной практики замены PNG. В частности, мы можем рассматривать SVG как независимый модуль, который включает в себя CSS для кастомизации вариантов отображения; также как и правила для отзывчивого поведения, SVG может содержать в себе JavaScript для логики взаимодействий. Теперь давайте рассмотрим эту технику детальнее.

Отзывчивый SVG: ни кола, ни двора

Сайт с отзывчивыми иконками Харрисона устроен достаточно просто. Он создан на основе всем известной техники спрайтов. Если Вы всё ещё не знакомы со спрайтами, позвольте я объясню. Спрайты — это техника, которая прежде использовалась только для растровых изображений, чтобы решить проблему медленных сетей. Главная идея в том, чтобы объединить множество мелких изображений в единый файл, чтобы таким образом пользователь загружал с сервера только один файл с изображениями.

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

Техника Харрисона делает тоже самое, только в отличие от PNG в спрайтах, используется SVG. Если объединить все изображения в один файл, вот что мы получим:

svg_sprite

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

.icon {
    width: 300px;
    height: 300px;
    background: url(../images/home_sprite.svg);
    background-position: center top;
}

Пример, описанный выше, очень простой, но он имеет определенные недостатки. И избавить его от них не так-то просто. Фактически, для того, чтобы всё работало, нужны две составляющие: внешний CSS и, собственно, SVG спрайт.

Отзывчивый SVG: дырки да заплаты

Поскольку CSS может быть помещён в SVG, давайте рассмотрим представленный пример и сделаем иконку более портативной.

Для начала уберём все пустые места иконок в спрайте. Конечно же, это создаст многослойный бардак из иконок: пример на CodePen.

Далее, давайте переместим все формы и сгруппируем их для каждой иконки, добавляя класс .icon для каждой из групп и порядковый номер, чтобы было легче идентифицировать каждую (например, #home_icon_0, #home_icon_1 и так далее до #home_icon_8):

<svg>
    <g id="home_icon_0" class="icon">
        <!-- пути и формы -->
    </g>

    <!-- ... -->

    <g id="home_icon_8" class="icon">
        <!-- пути и формы -->
    </g>
</svg>

Теперь мы готовы к добавлению media queries, чтобы таким образом мы могли выбирать иконку для отображения. Это возможно с помощью написания CSS непосредственно в тег <svg> используя для этого <defs>.

<svg>
    <defs>
        <style>
        /* Сначала мы прячем все иконки. */
        .icon {
            display: none;
        }

        /* Показываем первую. */
        #home_icon_0 {
            display: block;
        }

        /* Показываем нужную иконку и прячем остальные в зависимости от размера вьюпорта. */
        @media screen and (min-width: 25em) {

            #home_icon_0 {
                display: none;
            }

            #home_icon_1 {
                display: block;
            }
        }

        @media screen and (min-width: 30em) {
            #home_icon_1 {
                display: none;
            }

            #home_icon_2 {
                display: block;
            }
        }

        /* И так далее */

        </style>
    </defs>

<!-- Тут находится группа иконок -->

</svg>

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

Отзывчивый SVG: вооружён и очень опасен

Пример выше выглядит уже гораздо лучше, но вопрос всё ещё остается открытым:

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

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

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

<svg>
    <defs>
        <style>
        @media screen and (max-width: 65em) {

            #door-shadow, #tube-shadow, .backyard {
                display: none;
            }

            #door-body {
                fill: white;
            }

            #door-handle {
                fill: #E55C3C;
            }

            #door-body, #door-handle {
                -ms-transform: translate(0,0);
                -webkit-transform: translate(0,0);
                transform: translate(0,0);
            }

            #window {
                -ms-transform: translate(0,0) scale(1);
                -webkit-transform: translate(0,0) scale(1);
                transform: translate(0,0) scale(1);
            }

            #house-body {
                -ms-transform: scaleX(1) translate(0, 0);
                -webkit-transform: scaleX(1) translate(0, 0);
                transform: scaleX(1) translate(0, 0);
            }

            #tube-body {
                -ms-transform: translate(0, 0);
                -webkit-transform: translate(0, 0);
                transform: translate(0, 0);
            }

            #tube-upper {
                -ms-transform: translate(0, 0);
                -webkit-transform: translate(0, 0);
                transform: translate(0, 0);
            }
        }

        /* И так далее */

        </style>
    </defs>

<!-- Тут находится группа иконок -->

</svg>

Как только мы добавим трансформации, наша иконка начнет вести себя точно так же, как и пример Джо Харрисона, с той лишь разницей, что у нас все будет содержаться в одном файле.

Вы можете изучить пример на CodePen.

Адаптируем иконки к размерам родительского контейнера

И ещё один момент. Также возможно изменять иконку в зависимости от изменения высоты или ширины родительского элемента.

Для этого я попробовал загрузить SVG в виде элемента img, который был вложен в div. Но ни одно из медиавыражений в SVG-файле не сработало.

Моя следующая попытка была загрузить иконку в элемент object, вложенный в div. После того, как я это проделал, все медиавыражения заработали. Более того, теперь можно было сделать так, чтобы объект заполнял всё пространство родителя. (Не забудьте установить такие атрибуты как ширина и высота равными 100% для svg, или уберите их вовсе).

<div style="width: 100%; margin: 0 auto;">
    <object>
        <embed src="responsive3.svg" style="width: 100%; height: auto;" />
    </object>
</div>

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

Пример ниже демонстрирует, как иконка реагирует на изменение размеров контейнера.

Помещённые внутри SVG медиавыражения определяют поведение иконок, в зависимости от размеров в которых будет отображаться SVG. Вот пример на CodePen одного и того же SVG который вмещает восемь блоков разного размера.

Добавляем JavaScript в SVG

И это ещё не все хорошие новости! SVG может содержать не только таблицы стилей, но и JavaScript. Таким образом мы можем рассматривать SVG как отдельный и независимый модуль, к которому может применяться любой язык разметки предыдущих версий.

Инлайн JavaScript отлично работает, когда SVG добавлен на страницу с помощью <object>. Мы живем в замечательном мире, верно?

Поддержка браузеров

Последний и самый сложный пример отлично работает в следующих браузерах:

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

Заключение

Отзывчивые SVG иконки могут использоваться во многих случаях, включая следующие:

Я должен отметить, что нет ничего плохого в использовании практики со спрайтами, которую предложил Джо Харрисон. Она работает, и она необходима в некоторых случаях!

У вас появились интересные идеи? Хотите поделиться своим опытом использования этой техники? Напишите нам об этом в комментариях.

Развлекайтесь!

Если вы заметили ошибку, вы всегда можете отредактировать статью, создать issue или просто написать об этом Антону Немцеву в skype ravencry.

Илья Пухальский
Саша Ющак
Переводчик:
Саша Ющак
GitHub:
A1ex5andr
Сaйт:
http://coderbit.net/
Twitter:
@SashaYu

Комментарии (2 комментария, если быть точным)

Автар пользователя
psdcoder

В Chrom'e не работает последняя демка с svg в object'e: link (По крайней мере в 35 версии). Видны только border'ы. В Firefox'e все ок.

Автар пользователя
PlayTimeAlex

Техника классная. Только вот с кросбраузерностью немного беда. Тег img работает в последних версиях(29,06,14) ФФ и Хрома(хотя в хроме работа сомнительная так как в последнем варианте фигуры были не на своих местах). Но embed не работал в Хроме. В общем техника очень крутая, но нужна поддержка во всех новых браузерах (ну и в относительно старых). Думаю дизайнеры при респонсиве ей бы место быстро нашли. А так это похоже на технику с выбором лучшей картинки выходя из размера окна, при этом которая не везде работает (разве что джс нас спасет).