Использование SVG в качестве Placeholder’a

Original author: José M. Pérez
  • Translation
image

Генерация SVG из изображений может использоваться для Placeholder’ов.

Я занимаюсь оптимизацией изображений и картинок для их быстрой загрузки. Одна из самых интересных областей исследования это Placeholder’ы: что показывать, когда изображение еще не загружено.

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

В этом посте мы рассмотрим следующие темы:

  • Обзор различных типов Placeholder’ов
  • Placeholder на основе SVG (контуры, фигуры и силуэты)
  • Автоматизация процесса.



Перевод выполнен при поддержке компании EDISON Software, которая профессионально занимается созданием корпоративных сайтов на WordPress, а так же дизайнит персонажей для фирменного стиля.


Обзор различных типов Placeholder’ов


В прошлом посте я писал о Placeholder’e и ленивой загрузке изображений, а также говорил об этом. Когда вы делаете ленивую загрузку изображений, неплохо подумать о том, что делать в качестве Placeholder’а, поскольку это может иметь большое влияние на восприятие пользователя. Недавно я описывал несколько вариантов:

image

Несколько вариантов заполнения области изображения перед его загрузкой.

Сохранение пространства для изображения пустым: в мире адаптивного дизайна это предотвращает сдвиг контента. Эти изменения компоновки плохи с точки зрения пользователя, и для представления. Браузер вынужден выполнять вычисления компоновки каждый раз, когда он извлекает размеры изображения, оставляя для него пространство.

  • Placeholder: Представьте, что мы показываем изображение профиля пользователя. Мы могли бы отобразить силуэт на месте изображения. Он отображается не только при загрузке основного изображения, но также и при неудачном выполнении запроса или когда пользователь вообще не установил изображение профиля. Эти изображения, как правило, основаны на векторах, и из-за их небольшого размера являются хорошим кандидатом, который должен быть встроен.
  • Solid colour: возьмите цвет из изображения и используйте его в качестве цвета фона для Placeholder’а. Это может быть доминирующим цветом, наиболее ярким… Идея состоит в том, что он основан на загружаемом изображении и должен помочь сделать переход между “без изображения” на изображение более плавным.
  • Размытое изображение: также называется техникой размытия. Вы создаете малую версию изображения, а затем переходите к полной. Начальное изображение маленькое как в пикселях, так и в размерах. Для удаления артефактов изображение масштабируется и размывается. Ранее я писал об этом в этих статьях How Medium does progressive image loading, Using WebP to create tiny preview images и More examples of Progressive Image Loading.
  • Оказывается, есть много других вариантов, и многие умные люди разрабатывают другие методы создания Placeholder’ов.

Один из них это использование градиентов вместо сплошных цветов. Градиенты могут создавать более точный предварительный просмотр конечного изображения с минимальными расходами (увеличение полезной нагрузки).

image

Использование градиентов в качестве фона. Скриншот от Gradify, который больше не в сети. Код на GitHub.

Другой метод — использование SVG на основе изображения.

Placeholder’ы на основе SVG


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

Контуры


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

image

Codepen

Drawing images using edge detection and SVG animation

Формы


SVG также может использоваться для рисования областей из изображения без использования контуров / границ. В некотором смысле, мы будем векторизовать растровое изображение для создания Placeholder’a.

Когда-то я пытался сделать что-то подобное с треугольниками. Вы можете увидеть результат в моих беседах в CSSConf и Render Conf.

image

Codepen выше является доказательством концепции Placeholder’ов на основе SVG, состоящий из 245 треугольников. Генерация треугольников основана на триангуляции Делоне с использованием polyserver Поссана. Как и ожидалось, чем больше треугольников использует SVG, тем больше размер файла.

Primitive и SQIP, метод LQIP на основе SVG


Тобиас Балдауф работает над другим методом низкого качества изображения, используя SVG, называемый SQIP. Прежде чем копаться в SQIP, я дам обзор Primitive, библиотеки, на которой основан SQIP.

Primitive довольно увлекательный, и я определенно рекомендую вам его попробовать. Он преобразует растровое изображение в SVG, состоящий из перекрывающихся фигур. Его небольшие размеры делают его подходящим для наложения его прямо на страницу. Еще один хороший вариант, а также значимый placeholder в начальной загрузки HTML.

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

Чтобы понять, как работает Primitive, я провел его через пару изображений. Я сгенерировал SVG для художественной работы, используя 10 фигур и 100 фигур:

image

к картинкам(Обработка этого изображения с помощью Primitive, используя 10 Фигур и 100 фигур.)

image

При использовании 10 фигур в изображении мы начинаем понимать исходное изображение. В контексте placeholder’a изображений существует возможность использовать этот SVG в качестве placeholder’a. На самом деле, код для SVG с 10 фигурами очень мал, около 1030 байт, который снижается до ~ 640 байт при передаче вывода через SVGO.

<path fill=”#817c70" d=”M0 0h1024v1024H0z”/><path fill=”#03020f” d=”M178 994l580 92L402–62"/><path fill=”#f2e2ba” d=”M638 894L614 6l472 440"/><path fill=”#fff8be” d=”M-62 854h300L138–62"/><path fill=”#76c2d9" d=”M410–62L154 530–62 38"/><path fill=”#62b4cf” d=”M1086–2L498–30l484 508"/><path fill=”#010412" d=”M430–2l196 52–76 356"/><path fill=”#eb7d3f” d=”M598 594l488–32–308 520"/><path fill=”#080a18" d=”M198 418l32 304 116–448"/><path fill=”#3f201d” d=”M1086 1062l-344–52 248–148"/><path fill=”#ebd29f” d=”M630 658l-60–372 516 320"/>

Изображения, сгенерированные с 100 фигурами, больше, как и ожидалось, весом ~ 5 КБ после SVGO (ранее 8КБ). Они имеют большой уровень детализации с небольшой полезной нагрузкой. Решение о том, сколько треугольников использовать, будет во многом зависеть от типа изображения (например, контраста, количества цветов, сложности) и уровня детализации.

Можно было бы создать скрипт, похожий на cpeg-dssim, который изменяет количество используемых фигур до тех пор, пока не будет достигнут порог структурного подобия (или максимального количества фигур в худшем случае).

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

SQIP


По словам Тобиаса:
SQIP-это попытка найти баланс между этими двумя крайностями: использование Primitive для генерации SVG, состоящего из нескольких простых фигур, которые приближают основные объекты, видимые внутри изображения, оптимизирует SVG с помощью SVGO и добавляет к нему фильтр Gaussian Blur. Это производит SVG placeholder, который весит всего ~800-1000 байт, выглядит гладко на всех экранах и обеспечивает визуальную реплику содержимого изображения.

Результат аналогичен использованию крошечного изображения Placeholder’a для технологии размытия (что делают Medium и другие сайты). Разница заключается в том, что вместо использования растрового изображения, например JPG или WebP, Placeholder является SVG.

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

image

Выходные изображения с использованием SQIP для первого изображения и второго.

Выходной SVG составляет ~ 900 байт, и, проверяя код, мы можем обнаружить фильтр feGaussianBlur, применяемый к группе фигур:

image

SQIP также может выводить тег изображения с содержимым SVG Base 64 encoded:

<img width="640" height="640" src="example.jpg” alt="Add descriptive alt text" style="background-size: cover; background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAw…<stripped base 64>…PjwvZz48L3N2Zz4=);">

Силуэты


Мы просто посмотрели на использование SVG для контуров и примитивных фигур. Другая возможность заключается в векторизации изображений, «прорисовывая» их. Несколько дней назад Mikael Ainalem поделился codepen’он, указав, как использовать 2-цветный силуэт в качестве placeholder’a. Результат очень красивый:

image

SVG в этом случае были нарисованы вручную, но техника быстро породила интеграцию с инструментами для автоматизации процесса.

  • Gatsby, статический генератор сайта, использующий React, поддерживает эти прорисованные SVG сейчас. Он использует JS PORT potrace для векторизации изображений.

    image
  • Craft 3 CMS, который также добавил поддержку силуэтов. Он использует PHP Port of potrace.

    image
  • image-trace-loader, загрузчик Webpack, который использует potrace для обработки изображений.

    image

Также интересно посмотреть сравнение вывода между загрузчиком Webpack от Emil (основано на potrace) и отрисованными вручную SVG от Mikael.

image


Я предполагаю, что вывод, генерируемый potrace, использует параметры по умолчанию. Однако их можно настроить. Проверьте параметры для Image-trace-loader, которые в основном передаются в potrace.

Итого


Мы рассмотрели различные инструменты и методы для генерации SVG из изображений и использования их в качестве placeholder’a. Также WebP — фантастический формат для эскизов, SVG также интересный формат для использования в placeholder’ах. Мы можем контролировать уровень детализации (и, следовательно, размер), он очень сжимается и легко управляется с помощью CSS и JS.

Дополнительные ресурсы


  • Geometrize — это порт Primitive, написанный на Haxe. Существует также реализация на JS, которую вы можете попробовать прямо в своем браузере.
  • Primitive.js, который является портом Primitive в JS. Кроме того, primitive.nextgen, который представляет собой порт приложения Primitive для настольных компьютеров с использованием Primitive.js и Electron.
  • Есть несколько аккаунтов Twitter, где вы можете увидеть примеры изображений, созданных с помощью примитива и геометрии. Посмотрите @PrimitivePic и @Geometrizer.
  • imagetracerjs, который является трассировщиком растрового изображения и векторизатором, написанным на JavaScript. Есть также порты для Java и Android.
Edison
Изобретаем успех: софт и стартапы

Comments 53

    +7
    Статья отличная, за перевод спасибо. Но твиты картинками… За что вы так с нами? :)
      +2
      Как по мне, так вариант с простым векторным svg без гаусса лучше всего смотрится в роли плейсхолдера.
      А что-нибудь известно по ресурсам, требуемым для работы primitive и генерации простейшего svg из 10 фигур? Быстрее оно, чем генерация миниатюр через imagemagick?
      • UFO just landed and posted this here
          +1
          Вот потому и вопрос. По ощущениям Делоне явно сложнее, чем свёртка по пикселям, но всё может решить реализация. Primitive на Go vs imagemagick на C.
          А по размеру SVGO явно получше будет с <1kb против нынешних прейсхолдеров в 2kb. Не бесплатно же нам даётся эта экономия места, скорее всего в обмен на ЦПУ.
            0
            SVG ещё и deflate'ом пожмётся при передаче, кроме этого.
              0
              Вообще я бы подумал в сторону хранения вектора в виде набора инструкций для 2D Canvas в бинарном формате. Хотя, конечно, SVG удобнее заменять потом на реальную картинку.
        +4
        Я уж думал, что у меня бразуер сломался. Твиты в виде картинок оказались слишком жестокими для меня.
        • UFO just landed and posted this here
            +5
            Если очень коротко, там можно писать сообщения и читать их. Подписываетесь на людей или темы которые вам интересны, и читаете.
          +7
          +1 в копилку способов нагрузить браузер бесполезной работой.
            0

            можно же и на бэке сделать. Поставить чтобы аватарку нельзя было менять чаще чем раз в 5(10,20, 100500) раз в минуту или делать плейсхолдеры, только когда она "продержалась" 1(2,3,7, 14) дней.
            Отправлять svg гораздо дешевле

              +9
              Он имел ввиду про то, что отрисовка SVG относительно трудозатратная задача, в отличии от тупого вывода jpeg (как ни странно).
                +2

                Действительно, я какую-то глупость написал. Думал что под бесполезной работой подразумевается "генерация svg на фронте", но только сейчас понял что для этого нам нужно само изображение "=.=


                По поводу отображения svg vs jpeg — это стандартная дилемма: память vs вычисления.
                С одной стороны у большинства пользователей уже есть быстрый домашний и (иногда) мобильный интернет, в кафе и торговых центрах есть Wi-Fi и загрузить лишнюю маленькую jpeg не составит труда, но с другой стороны браузеру итак есть что грузить, а тут ещё "лишние" jpeg файлы, пускай и небольшого размера.
                С другой стороны вычислительные мощности бОльшей части устройств смогут справиться с выводом 10 фигур, что будет очень кстати для пользователей мобильных устройств.


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


                В остальных случаях, как и сказал TimsTims можно использовать прогрессивный JPEG

                  +2
                  А что мешает в качестве плейсхолдеров использовать те же инлайнутые JPG или PNG?
            +5
            Кажется, автор заново изобретает велосипед: есть ведь прогрессивная загрузка JPEG, которой почти никто не пользуется…
              +4
              На сколько я понял, основной упор делается на то, что плейсхолдеры можно встроить непосредственно в саму страницу, и они отобразятся сразу, до того как браузер начнёт грузить реальные картинки. В случае с Progressive JPEG, лаг между пустым местом и хоть чем-то напоминающим картинку будет несколько выше.
                +1
                Думаю, при использовании server push, лаг будет незаметен
                • UFO just landed and posted this here
                  0
                  В случае с Progressive JPEG, лаг между пустым местом и хоть чем-то напоминающим картинку будет несколько выше.
                  Отнимаем несколько десятых секунды на отрисовку SVG, иэ тот лаг уже компенсируется при «нормальном» интернете. Даже более того, небольшое зависание браузера и компьютера (100% CPU) перевешивает чашу весов в пользу JPEG.

                  SVG из статьи выигрывает только когда у пользователя очень плохой канал интернета(или ваш сайт очень жирный), и ему загрузить 100% CPU на пару секунд выгоднее, чем ждать +60 секунд на полную прогрузку.
                    0
                    загрузить 100% CPU на пару секунд
                    Не демонизируйте SVG. Во-первых, Primitive на выходе даёт треугольники, отрисовка которых — самое «дешёвое» после цветного прямоугольника, а во-вторых — отрисовка SVG во всех современных браузерах делается с аппаратным ускорением (в том же Хромиуме доступно аж с 2011 года).
                  0
                  Мне кажется описанный метод лучше с точки зрения дизайна. Понятно что не стоит пихать его кругом, но кое где он может прийтись очень даже в тему
                    0
                    Нет. Прогрессивная не работает. Она требует второго запроса на сервер ради хоть чего-то. А плейсхолдер отдается вместе с первым запросом html в теле. Не помню даже где я хоть раз видел у кого она работает.
                      0
                      глупость какую-то написал и не ясно почему…
                        0
                        Главное — развиваться)
                      0
                      Думаю, автор — иллюстратор или верстальщик, а не математик. И какой бы он не соорудил «велосипед» из полигонов, это никогда не сравнится по эффективностью с прогрессивным JPEG, в котором превью является информацией для построения полного изображения. И поэтому не требует лишней передачи данных превью по сети, дополнительного хранения информации для превью в базе данных, дополнительной загрузки скриптов для распаковки и отрисовки этого превью в браузере и т.д. Даже странно, что кому-то такие эксперименты могут быть интересны.
                      +7
                      1. 1-4к — этого вполне достаточно для jpeg-превьюшки терпимого качества.
                      2. Большая часть этого svg — текст, который можно было бы просто закодировать как координаты, прозрачности и цвета для треугольников. Если каждый треугольник описывается 6 u16 и цветом, то это 16 байт на треугольник. 100 треугольников — 1600 байт. Если же обнаглеть и разрешить огрублять позции треугольников до байта, то это 10 байт на треугольник, 100 треугольников — 1кб без сжатия.

                      Но у меня вопрос: если на странице 30 превьюшек, то сколько мегафлопов будет сожрано у пользовательской батарейки в процессе «предрендеринга»?
                        +2

                        Меня беспокоил такой же вопрос.
                        Решил проверить, сделал примитивный тест на codepen, который просто создает svg с n количеством треугольников.
                        Браузер на компьютере подвисает на 3-4 секунды от 100 000 треугольников, после чего функционирует нормально.
                        У телефона такие же проблемы начинаются с 10 000 треугольников (Xiaomi Redmi 3S)

                          +7
                          Итого: 30 превьюшек по 100 треугольников делают нам 3000 треугольников. То есть примерно секунду «подвисания» браузера мобильного телефона или 0.3с десктопа (100% CPU, насколько я понимаю).

                          Я бы сказал, что дофига.
                            +2
                            Ламера вопрос: а если ту же свгшку готовым файлом вставить, а не добавлять треугольники в цикле по одному, разве быстрее не будет?
                              +1

                              Хороший вопрос.
                              Первая моя мысль мысль: выгода была бы такой, что ей можно было бы пренебречь, потому что цикл из 10 000 элементов проходится относительно легко.
                              Вторая что JS страдает при каждом добавлении нового элемента в DOM, поэтому выгода могла быть большой.
                              Третья: готовым файлом — это как?

                                +1
                                Третья: готовым файлом — это как?
                                placeholder.svg
                                Не знаю я, как это в вебе правильно делать. Вероятно как-то так habrahabr.ru/company/edison/blog/342848/?reply_to=10536428#comment_10531650
                                  +1
                                  Вторая что JS страдает при каждом добавлении нового элемента в DOM, поэтому выгода могла быть большой.

                                  По-моему, это было актуально 2-3 года назад.
                                  Сейчас что добавление нового элемента в DOM, что генерация одним большим куском работают одинаково быстро.

                              +1
                              Но у меня вопрос: если на странице 30 превьюшек, то сколько мегафлопов будет сожрано у пользовательской батарейки в процессе «предрендеринга»?

                              Тут кстати есть интересный момент. Если мы знаем, что наша svg содержит только одноцветные треугольники, то отрисовать это с помощью GPU расплюнуть. Поэтому если заморочится и написать JS рисующий через WebGL запросто можно получить ощутимый профит, и не сильно жечь батарею.
                              +3

                              Кстати, для вывода data-uri SVG изображений можно не кодировать в Base64, достаточно закодировать SVG в виде Url, то есть прогнать через какой-нибудь httpencode


                              <img style="background: url(data:image/svg+xml;utf8,[svg_http_encoded]">

                              Это позволит во-первых, немного уменьшить размер текста, во-вторых, браузеру не нужно будет выполнять декодирование base64, что может быть медленным, в-третьих, это лучше сжимается gzip'ом. Если кодировка не важна, можно ее опустить: data:image/svg+xml,[svg_http_encoded]

                                +1
                                Насколько я помню, в IE11 есть странный глюк: если не кодировать в base64, это не будет работать.
                                  +1

                                  Работает вплоть до IE9, было бы круто ели бы вы показали пример если что-то действительно не работает, чтобы не наступить на грабли, если что.

                                  • UFO just landed and posted this here
                                    +1
                                    А разве нет ограничения на размер URL?
                                      +1

                                      А какая разница, мы httpencoded засунем внутрь url() или base64? Но, в целом, ограничение на размер data:URL было у IE8(32кб) и какой-то старой Opera.

                                        +1
                                        Извините, я сейчас не так пристально слежу за темой веб-разработки, не знал что это уже не актуально.
                                    –1
                                    Статья интересная, но на превьюшке для затравки алгоритм отработал странно. Вместо ожидаемого одного треугольника, видим два.
                                    image
                                    Кого это заинтересует + найдётся время изучить предложенные алгоритмы генерации (с текущими исходниками) — думаю там есть что улучшить и написать хорошую статью.
                                      +4
                                      Ну не знаю, мне вполне видится, что там не обойтись одним трехугольником
                                      +3
                                      Я правильно понимаю что вот этот пример: codepen.io/jmperez/pen/oogqdp

                                      На нативном JS написан?
                                        +1
                                        А что, есть много других js-ов?
                                          –1
                                            +3
                                            Вы прям как эталонный хабровчанин отвечаете.

                                            Я таки думал очевидно, что вопрос про фреймворки и библиотеки, ибо я не знаток Codepen, ведь вполне было возможно что там где-то в настройках используемые библиотеки подключаются. Я бегло посмотрел код, не нашёл ничего из того же jQuery, но всё же решил уточнить.
                                          +3
                                          Заливка доминирующим цветом — абсолютно бесполезная ерунда.

                                          SVG-картинка из треугольников — теоретически интересно, практически смысла ноль. Они пишут, что 100 треугольников после оптимизации весят около 5 кб. Я прикинул: аналогичный вес имеет PNG-картинка размером порядка 100-120px по каждой стороне, которая дает отличное представление об изображении и намного меньше ест ресурсы. Вообще говоря, 100px для LQIP — это сильно избыточно, обычно берут что-то в пределах 20-50px.

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

                                          Мой выбор — банальный растровый LQIP весом не более 1 кб, причем даже без блура.
                                            +1
                                            SVG еще отлично жмется в GZIP
                                          • UFO just landed and posted this here
                                              0
                                              Спасибо за статью.
                                                0
                                                Силуэты в svg со страницы с примером занимают 32.9 Кб, после сжатия 11.7 Кб.
                                                Двухцветный png 2.5 Кб. Выглядит пикселезировано.
                                                Двухцветный webp 2.3 Кб. Выглядит пикселезировано.
                                                Четырехцветный png 3.5 Кб. Выглядит сглажено.
                                                Четырехцветный webp 4.7 Кб. Выглядит сглажено.
                                                Двухцветный png в два раза больше приведенного разрешения на странице 6 Кб.
                                                Двухцветный lossless двойного разрешения webp 5.7 Кб.

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

                                                Хотя сравнение с треугольниками вероятно будет не победоносным для растра.
                                                  0
                                                  Интересная постобработка. Простенько, как говорится, и со вкусом.
                                                    0
                                                    идея отличная, но хотелось бы понимать с какого количества/размеров картинок эти способы дают профит
                                                    т.к. нужно загрузить либу генерации svg и потратить время/ресурсы на собственно генерацию

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