Статья про ремонт квартиры, про эффективное использование графических ресурсов современных устройств. От смарт часов до телевизоров на стену.
Сказ о том, как вставлять фоновые повторяющиеся изображения в страницу, что бы всем было хорошо.
Ну, что готовы поиграть ..? Тогда погнали
Вводные
Поступила задача на создание страницы сайта с повторяющимся изображением.
Как будем решать ?
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 Сожму тебя по максимуму
Давайте, подумаем, а можно ли это ещё как-то улучшить положение? Хмм, а рисунок то не сложный, может можно его перегнать в вектор?
И тут спрятался демон соблазна.
Стремясь к лучшему, мы часто портим хорошее «Уильям Шекспир»
Когда этого делать не рационально, или вообще нельзя:
- У вас сложный повторяющийся рисунок. Его перерисовка займет n часов, а если вы этой задачей ещё и напряжете дизайнера, то карма у вас точно уменьшиться.
- В изображении присутствуют тени или элементы с размытием, и вы не согласны от них отказаться.
* Тут надо объяснить
Если вы экспортируете 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)
… и установим, атрибут 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 Я не умею графику, но хочу прикоснуться к прекрасному
Если по каким-то причинам вы не умеете нарисовать повторяющийся элемент паттерна в графическом редакторе, или у вас нету на это времени. Вот сайты с готовыми решениями, в которых добрые люди делятся с миром хорошими вещами.
- http://www.heropatterns.com/
- https://philiprogers.com/svgpatterns/
- http://thepatternlibrary.com/
- http://btmills.github.io/geopattern/
* маленькая добавка (куда же без порно в наше время то )
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 на просторах русскоязычного Интернета
JustDont
Точно так же (через defs/symbol) можно работать и с иконками. И делать из символов паттерны, кстати.
И ссылаться не на тот же файл, а на другой, благо там везде url, что в атрибуте fill, что в элементе use, которым «применяются» символы. И тогда важные графические ресурсы (полагая, что они у вас все в векторе) можно будет разложить на сборники символов по степени важности, с тем, чтоб самое важное загрузилось первым и как можно быстрее.