Статья про ремонт квартиры, про эффективное использование графических ресурсов современных устройств. От смарт часов до телевизоров на стену.


Сказ о том, как вставлять фоновые повторяющиеся изображения в страницу, что бы всем было хорошо.


Ну, что готовы поиграть ..? Тогда погнали


Вводные


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

Как будем решать ?




Level 1 Банально примитивный


Самый простой способ, которым воспользуется большое количество верстальщиков это забрать фоновую картинку, всю разом (чаще всего png форматом, поскольку он по умолчанию в Figma и Avocode выставлен) и плюхнуть её блоку через background-image;
(* ну, ты ведь не такой, мой юный читатель ?)



body { background: url(‘bg.png’) 50% 50% no-repeat;
 background-size:contain;
 }

Если разработчик немного опытнее, и знает что jpg сжимается, как правило, лучше png, то фон будет формата jpg.




Level 2 Мне не плевать на производительность


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


Если макет был реализован в PhotoShop, то придется обрезать. Потому что там background задается нераздельным элементом


Пример вынимания повторяющегося фона из Avocode



В Figma это реализовано несколько проще. Хотя это будет зависеть, от того как дизайнер вам создавал этот фон.


*Кто желается поиграться, я оставлю свой пример для скачивания.
В примере два варианта с png и svg паттернами о которых пойдет речь дальше.


Ок, теперь вместо одного большого фонового изображения у нас есть повторяющийся элемент плитки (CSS Tiling).


body {
    background: url("bg.jpg");
}

Отлично, мы молодцы. Вот уже вместо (586кБ) пользователь забирает с сервера всего ( ~24кБ); Profit !



Level 3 Сожму тебя по максимуму


Давайте, подумаем, а можно ли это ещё как-то улучшить положение? Хмм, а рисунок то не сложный, может можно его перегнать в вектор?



И тут спрятался демон соблазна.


Стремясь к лучшему, мы часто портим хорошее «Уильям Шекспир»

Когда этого делать не рационально, или вообще нельзя:


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

* Тут надо объяснить


Если вы экспортируете SVG графику предположим из Figma или Illustrator, a не кодите её сами в редакторе, то скорее всего элементы размытия или тени вам будут вставлены в SVG в формате base64 и это ни как не улучшит ваш performance, поскольку такой файл, по факту, может весить больше аналога jpg формата.


Всегда проверяйте, что вам предлагают. Разуметься некоторые из этих элементов можно создать и кодом, при помощи тегов filter, use, object… но для правильной работы с ними нужен прокачанный навык, а он редкость.


Хорошо, давайте создадим SVG и посмотрим что у нас получиться.


* Ради чистоты эксперимента приведенные изображения ни как не сжимались сторонними утилитами и сохранялись в 100% качестве при помощи PhotoShop, Illustrator и Avocode.



Ок, да наша svg оказывается меньше jpg/png исходно варианта.


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

Разница между размерами файлов png/jpg кажется не сильно существенной, и она начинает играть роль, только если рисунок большого размера.


*Я предпочитаю png, а фоновый цвет задавать свойством background-color, но это от задачи.


Меня несколько смутило, что полученные png/jpg созданные из svg по размеру меньше SVG (даже очищенного от системного мусора (pattern-bg_cleared.svg) средствами SVGO)


Но, потом, до меня дошло, что если захочется уменьшить/увеличить размер плитки фона для мобильных или телевизоров, то это потребует подключение новых паттернов (pattern-bg_min) а SVG останется всё тем же (15кБ — всегда), да и размытия изображения не наблюдается

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


body { background-image: url(picture.svg); }

Выглядит, довольно привычно.


Ок, усложняем. Предположим что стили, которые в body будут подключать в критический CSS, давайте тогда отдавать вектор прям в стилях. *дабы не захламлять страницу, возьму другой паттерн, поменьше.


body {
background: url:(‘ <svg width="64" height="64" viewBox="0 0 64 64" xmlns="http://www.w3.org/2000/svg"><path d="M8 16A8 8 0 108 0a8 8 0 000 16zm0-2A6 6 0 108 2a6 6 0 000 12zm33.414-6l5.95-5.95L45.95.636 40 6.586 34.05.636 32.636 2.05 38.586 8l-5.95 5.95 1.414 1.414L40 9.414l5.95 5.95 1.414-1.414L41.414 8zM40 48a8 8 0 100-16 8 8 0 000 16zm0-2a6 6 0 100-12 6 6 0 000 12zM9.414 40l5.95-5.95-1.414-1.414L8 38.586l-5.95-5.95L.636 34.05 6.586 40l-5.95 5.95 1.414 1.414L8 41.414l5.95 5.95 1.414-1.414L9.414 40z" fill="#000" fill-rule="evenodd"/></svg>’);
}

Стало довольно много CSS, и он перестал работать.Плохо


Так, необходимо теперь правильно это всё закодировать. Вставку незакодированного SVG поддерживают только браузеры на Webkit. Спасибо, Юлии Бухваловой, за полезный ресурс кодирования, который нам в этом поможет.




body {
background-color: #DFDBE5;
background-image: url("data:image/svg+xml,%3Csvg width='64' height='64' viewBox='0 0 64 64' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M8 16c4.418 0 8-3.582 8-8s-3.582-8-8-8-8 3.582-8 8 3.582 8 8 8zm0-2c3.314 0 6-2.686 6-6s-2.686-6-6-6-6 2.686-6 6 2.686 6 6 6zm33.414-6l5.95-5.95L45.95.636 40 6.586 34.05.636 32.636 2.05 38.586 8l-5.95 5.95 1.414 1.414L40 9.414l5.95 5.95 1.414-1.414L41.414 8zM40 48c4.418 0 8-3.582 8-8s-3.582-8-8-8-8 3.582-8 8 3.582 8 8 8zm0-2c3.314 0 6-2.686 6-6s-2.686-6-6-6-6 2.686-6 6 2.686 6 6 6zM9.414 40l5.95-5.95-1.414-1.414L8 38.586l-5.95-5.95L.636 34.05 6.586 40l-5.95 5.95 1.414 1.414L8 41.414l5.95 5.95 1.414-1.414L9.414 40z' fill='%239C92AC' fill-opacity='0.4' fill-rule='evenodd'/%3E%3C/svg%3E");
}

Примеры для наглядности, Как SVG можно в CSS запаковывать.



Курсор – это SVG



Всё вроде как хорошо, но я точно уверен, что буду использовать данный паттерн на всех страницах своего сайта? Точно его надо засовывать в критический CSS, а может правильнее будет в обычный — подключаемый CSS и надеяться, что браузер его закеширует? Может всё таки можно его подключать только для определенных станиц? Кто-то умный говорил, что код помещенный в HTML собирается браузером быстрее и имеет больший приоритет по скорости выдачи. Так много вопросов… надо пробовать.



Level 4 Я выжму из тебя все соки


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


<svg width="100%" height="100%">
     
</svg>

Чтобы создать pattern, мы должны поместить его в тег defs (определения) svg, например, так:
<svg width="100%" height="100%">
    <defs>
        <pattern>
             
        </pattern>
    </defs>
</svg>

Уже неплохо, дальше необходимо добавить атрибутов при помощи, которых мы можем взаимодействовать с pattern и как-то его вызывать.


<svg width="100%" height="100%">
    <defs>
        <pattern id="pattern-bg" x="0" y="0" width="80" height="80" viewBox =”0 0 80 80” patternUnits="userSpaceOnUse">
             
        </pattern>
    </defs>
</svg>

Мы дадим ему идентификатор, чтобы была возможность ссылаться на него позже, также укажем начальные координаты отсчета X и Y, значения ширины и высоты которое задаем в графическом редакторе, зададим значения области видимости ViewBox (можно взять из готового svg)


Немножко от темы, про ViewBox
Очень уж мне понравился пример визуализации масштабирования и координат ViewBox SVG Амелии Ваттенбергер https://wattenberger.com/guide/scaling-svg


… и установим, атрибут patternUnits в значение userSpaceOnUse (это определяет систему координат, о которой можно узнать больше на MDN )



Если вкратце, при patternUnits=”userSpaceOnUse” фигура на холсте SVG полностью заполняется плитками паттерна. Если последняя плитка не входит целиком по длине или по ширине, то она обрезается.



Теперь надо определиться с изображением, которое будет повторяться


<svg width="100%" height="100%">
    <defs>
        <pattern id="pattern-bg" x="0" y="0" width="80" height="80" viewBox =”0 0 80 80” patternUnits="userSpaceOnUse">
             
            <circle fill="#bee9e8" cx="20" cy="20" r="12.5">
            </circle>
            
        </pattern>
    </defs>
</svg>


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


<svg width="100%" height="100%">
    <defs>
        < pattern id="pattern-bg" x="0" y="0" width="80" height="80" viewBox =”0 0 80 80” patternUnits="userSpaceOnUse">             
            <circle fill="#bee9e8" cx="20" cy="20" r="12.5"></circle>   
        </pattern>
    </defs>
     
    <rect x="0" y="0" width="100%" height="100%" fill="url(#pattern-bg)"></rect>
</svg>

Вы можете видеть, что атрибут fill (который обычно содержит цвет заливки теперь указывает на URL-адрес, содержащий идентификатор нашего вновь определенного шаблона: fill = «url (#pattern-bg)».


Наш повторяемый паттерн занимает всё пространство по ширине, но хотелось бы ещё и что б по высоте всё заняло. Для этого обернем наш svg в с высотой height:100vh;

<div style="height: 100vh">
  <svg width="100%" height="100%">
    <defs>
        <pattern id="pattern-bg" x="0" y="0" width="80" height="80" viewBox =”0 0 80 80” patternUnits="userSpaceOnUse">                         <circle fill="#bee9e8" cx="20" cy="20" r="12.5"></circle>
        </pattern>
    </defs>
    <rect x="0" y="0" width="100%" height="100%" fill="url(#pattern-bg)"></rect>
</svg>
</div>

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


Данный метод вставки, в отличие от метода через background позволяет вам контролировать детализацию элементов SVG. Появляется некая свобода что отдавать на рендер пользователю, а что нет. А если есть желание экспериментировать, и добавить немного JS то можно ещё и морфинга (изменения фигур) достичь.


Из проблем такого метода вставки повторяющейся плитки, обнаружил, что нету возможности (ну или это я не смог найти, и знающие напишут в комментариях), как менять размер плитки при изменении размера экрана браузера. По факту за это отвечают атрибуты width/height тега pattern но достучаться до них через CSS и медиа выражения не получилось.Ну всегда есть JS, если очень надо


Cheats Я не умею графику, но хочу прикоснуться к прекрасному


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



* маленькая добавка (куда же без порно в наше время то )

https://svgporn.com/


Team Support Что там с поддержкой?


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



Пользователи IE ваших стараний не увидят поддержка данного тега к IE так и не пришла, и уже не придет, но в остальном c SVG всё не так плохо, даже до 9 IE caniuse.com

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


Для любителей почитать документацию, тоже оставлю пару ссылок


https://developer.mozilla.org/ru/docs/Web/SVG/%D0%AD%D0%BB%D0%B5%D0%BC%D0%B5%D0%BD%D1%82/pattern
https://developer.mozilla.org/ru/docs/Web/SVG/Tutorial/Patterns

Для тех, кто долистал до конца, оставлю свой пример, как можно развлекаться с встроенным SVG, из начала статьи



Сделаем web лучше.

PS Отдельное спасибо Alexandr_TT И его сайту https://svg-art.ru/ в продвижении SVG на просторах русскоязычного Интернета

Комментарии (1)


  1. JustDont
    10.01.2020 16:41

    Точно так же (через defs/symbol) можно работать и с иконками. И делать из символов паттерны, кстати.
    И ссылаться не на тот же файл, а на другой, благо там везде url, что в атрибуте fill, что в элементе use, которым «применяются» символы. И тогда важные графические ресурсы (полагая, что они у вас все в векторе) можно будет разложить на сборники символов по степени важности, с тем, чтоб самое важное загрузилось первым и как можно быстрее.