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

- такой же как Forbes, только лучше.

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.

Вы можете помочь и перевести немного средств на развитие сайта



Комментарии (40):

  1. sutarmin
    /#10531400

    Статья отличная, за перевод спасибо. Но твиты картинками… За что вы так с нами? :)

  2. evnuh
    /#10531406

    Как по мне, так вариант с простым векторным svg без гаусса лучше всего смотрится в роли плейсхолдера.
    А что-нибудь известно по ресурсам, требуемым для работы primitive и генерации простейшего svg из 10 фигур? Быстрее оно, чем генерация миниатюр через imagemagick?

    • stychos
      /#10531450

      Тут вопрос скорее об объёмах хранилища. Не думаю, что быстрее, это ведь надо не только прочесть картинку, но и выполнить ряд триангуляций.

      • evnuh
        /#10531462 / +1

        Вот потому и вопрос. По ощущениям Делоне явно сложнее, чем свёртка по пикселям, но всё может решить реализация. Primitive на Go vs imagemagick на C.
        А по размеру SVGO явно получше будет с <1kb против нынешних прейсхолдеров в 2kb. Не бесплатно же нам даётся эта экономия места, скорее всего в обмен на ЦПУ.

  3. ExplosiveZ
    /#10531416 / +1

    Я уж думал, что у меня бразуер сломался. Твиты в виде картинок оказались слишком жестокими для меня.

    • stychos
      /#10531458

      Я вот всё никак не пойму - как этим твиттером пользоваться?

      • Stalker_RED
        /#10531574 / +2

        Если очень коротко, там можно писать сообщения и читать их. Подписываетесь на людей или темы которые вам интересны, и читаете.

  4. JTG
    /#10531466 / +4

    +1 в копилку способов нагрузить браузер бесполезной работой.

    • edge790
      /#10531504 / +1

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

      • TimsTims
        /#10531566 / +5

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

        • edge790
          /#10531600 / +1

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


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


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


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

          • DistortNeo
            /#10531922 / +1

            А что мешает в качестве плейсхолдеров использовать те же инлайнутые JPG или PNG?

  5. TimsTims
    /#10531570 / +4

    Кажется, автор заново изобретает велосипед: есть ведь прогрессивная загрузка JPEG, которой почти никто не пользуется…

    • SHVV
      /#10531808

      На сколько я понял, основной упор делается на то, что плейсхолдеры можно встроить непосредственно в саму страницу, и они отобразятся сразу, до того как браузер начнёт грузить реальные картинки. В случае с Progressive JPEG, лаг между пустым местом и хоть чем-то напоминающим картинку будет несколько выше.

      • SPAHI4
        /#10531908

        Думаю, при использовании server push, лаг будет незаметен

        • Aingis
          /#10532724 / +1

          Server push для картинок? Месье знает толк в извращениях!

      • TimsTims
        /#10532264

        В случае с Progressive JPEG, лаг между пустым местом и хоть чем-то напоминающим картинку будет несколько выше.
        Отнимаем несколько десятых секунды на отрисовку SVG, иэ тот лаг уже компенсируется при «нормальном» интернете. Даже более того, небольшое зависание браузера и компьютера (100% CPU) перевешивает чашу весов в пользу JPEG.

        SVG из статьи выигрывает только когда у пользователя очень плохой канал интернета(или ваш сайт очень жирный), и ему загрузить 100% CPU на пару секунд выгоднее, чем ждать +60 секунд на полную прогрузку.

  6. amarao
    /#10531608 / +2

    1. 1-4к — этого вполне достаточно для jpeg-превьюшки терпимого качества.
    2. Большая часть этого svg — текст, который можно было бы просто закодировать как координаты, прозрачности и цвета для треугольников. Если каждый треугольник описывается 6 u16 и цветом, то это 16 байт на треугольник. 100 треугольников — 1600 байт. Если же обнаглеть и разрешить огрублять позции треугольников до байта, то это 10 байт на треугольник, 100 треугольников — 1кб без сжатия.

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

    • edge790
      /#10531652 / +2

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

      • amarao
        /#10531656 / +4

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

        Я бы сказал, что дофига.

      • ad1Dima
        /#10532258

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

        • edge790
          /#10536428

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

          • DistortNeo
            /#10536446

            Вторая что JS страдает при каждом добавлении нового элемента в DOM, поэтому выгода могла быть большой.

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

    • MrShoor
      /#10532100

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

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

  7. radist2s
    /#10531650 / +2

    Кстати, для вывода 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]

    • lenar
      /#10531758

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

      • radist2s
        /#10532868

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

      • Aingis
        /#10532946

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

        P.S. Кусок «;utf8», кстати, лишний. Я вообще не нашёл способа изменить кодировку в data-url'е.

    • Psychosynthesis
      /#10532108

      А разве нет ограничения на размер URL?

      • radist2s
        /#10532904 / +1

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

        • Psychosynthesis
          /#10533260

          Извините, я сейчас не так пристально слежу за темой веб-разработки, не знал что это уже не актуально.

  8. theonlymirage
    /#10531838

    Статья интересная, но на превьюшке для затравки алгоритм отработал странно. Вместо ожидаемого одного треугольника, видим два.
    image
    Кого это заинтересует + найдётся время изучить предложенные алгоритмы генерации (с текущими исходниками) — думаю там есть что улучшить и написать хорошую статью.

    • TheShock
      /#10532006

      Ну не знаю, мне вполне видится, что там не обойтись одним трехугольником

  9. Psychosynthesis
    /#10532114

    Я правильно понимаю что вот этот пример: codepen.io/jmperez/pen/oogqdp

    На нативном JS написан?

    • Stalker_RED
      /#10532222

      А что, есть много других js-ов?

      • Psychosynthesis
        /#10533254

        Вы прям как эталонный хабровчанин отвечаете.

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

  10. dom1n1k
    /#10532586 / +1

    Заливка доминирующим цветом — абсолютно бесполезная ерунда.

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

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

    Мой выбор — банальный растровый LQIP весом не более 1 кб, причем даже без блура.