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

https://medium.freecodecamp.org/using-svg-as-placeholders-more-image-loading-techniques-bed1b810ab2c
  • Перевод
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 479,91
Изобретаем успех: софт и стартапы
Поделиться публикацией
Комментарии 46
  • +7
    Статья отличная, за перевод спасибо. Но твиты картинками… За что вы так с нами? :)
    • +2
      Как по мне, так вариант с простым векторным svg без гаусса лучше всего смотрится в роли плейсхолдера.
      А что-нибудь известно по ресурсам, требуемым для работы primitive и генерации простейшего svg из 10 фигур? Быстрее оно, чем генерация миниатюр через imagemagick?
      • +1
        Тут вопрос скорее об объёмах хранилища. Не думаю, что быстрее, это ведь надо не только прочесть картинку, но и выполнить ряд триангуляций.
        • +1
          Вот потому и вопрос. По ощущениям Делоне явно сложнее, чем свёртка по пикселям, но всё может решить реализация. Primitive на Go vs imagemagick на C.
          А по размеру SVGO явно получше будет с <1kb против нынешних прейсхолдеров в 2kb. Не бесплатно же нам даётся эта экономия места, скорее всего в обмен на ЦПУ.
          • 0
            SVG ещё и deflate'ом пожмётся при передаче, кроме этого.
            • 0
              Вообще я бы подумал в сторону хранения вектора в виде набора инструкций для 2D Canvas в бинарном формате. Хотя, конечно, SVG удобнее заменять потом на реальную картинку.
      • +4
        Я уж думал, что у меня бразуер сломался. Твиты в виде картинок оказались слишком жестокими для меня.
        • +6
          Я вот всё никак не пойму - как этим твиттером пользоваться?
          • +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, лаг будет незаметен
                • +2
                  Server push для картинок? Месье знает толк в извращениях!
                • 0
                  В случае с Progressive JPEG, лаг между пустым местом и хоть чем-то напоминающим картинку будет несколько выше.
                  Отнимаем несколько десятых секунды на отрисовку SVG, иэ тот лаг уже компенсируется при «нормальном» интернете. Даже более того, небольшое зависание браузера и компьютера (100% CPU) перевешивает чашу весов в пользу JPEG.

                  SVG из статьи выигрывает только когда у пользователя очень плохой канал интернета(или ваш сайт очень жирный), и ему загрузить 100% CPU на пару секунд выгоднее, чем ждать +60 секунд на полную прогрузку.
                  • 0
                    загрузить 100% CPU на пару секунд
                    Не демонизируйте SVG. Во-первых, Primitive на выходе даёт треугольники, отрисовка которых — самое «дешёвое» после цветного прямоугольника, а во-вторых — отрисовка SVG во всех современных браузерах делается с аппаратным ускорением (в том же Хромиуме доступно аж с 2011 года).
                • 0
                  Мне кажется описанный метод лучше с точки зрения дизайна. Понятно что не стоит пихать его кругом, но кое где он может прийтись очень даже в тему
                  • 0
                    Нет. Прогрессивная не работает. Она требует второго запроса на сервер ради хоть чего-то. А плейсхолдер отдается вместе с первым запросом html в теле. Не помню даже где я хоть раз видел у кого она работает.
                  • +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, было бы круто ели бы вы показали пример если что-то действительно не работает, чтобы не наступить на грабли, если что.

                                • +1
                                  Для IE11 надо экранировать больше символов, включая „<“ и „>“. (Но меньше чем стандартная url-кодировка.) Например, так делает URL-encoder for SVG авторства @yoksel. См. мой пулл-реквест в кодировщик с перечнем экранируемых символов. Там же используется трюк с тем, что можно не экранировать пробелы, если заключить URL в кавычки.

                                  P.S. Кусок «;utf8», кстати, лишний. Я вообще не нашёл способа изменить кодировку в data-url'е.
                                • +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
                                        • 0
                                          Постарался посмотреть на это глазами себя как пользователя (пользователи, конечно, все разные один я одинаковый, так что это всё сугубое имхо). Кроме двуцветных силуэтов все варианты плейсхолдеров имеют общее узкое место — они вообще ничего не говорят о картинке, то есть при любых других достоинствах они передают чуть менее чем ноль информации, лучше уж тогда текстовое описание показать.
                                          Ну и вообще сама идея «давайте грузить больше, медленнее, но с видимостью плавности» мне совсем не близка.
                                          А потом кто-то поверх всех ваших оптимизаций вешает ведро js-лапши, и плакали все ваши старания.

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

                                          Самое читаемое