Использование SVG-картинок в качестве плейсхолдеров — это очень неплохая идея, особенно в нашем мире, когда чуть ли не все сайты состоят из кучи картинок, которые мы пытаемся асинхронно подгружать. Чем больше картинок и чем более объемные они, тем выше вероятность получения различных проблем, начиная от того, что пользователь не совсем понимает, а что же там собственно грузится, и заканчивая известным скачком всего интерфейса после прогрузки картинок. Особенно на плохом интернете с телефона — там может и на несколько экранов все улететь. Именно в такие моменты заглушки приходят на помощь. Еще один вариант их использования – это цензура. Бывают такие моменты, когда нужно скрыть от пользователя какую-то картинку, но хотелось бы сохранить общий стиль страницы, цвета и место, которое картинка занимает. Но в большинстве статей все рассуждают о теории, о том, что было бы неплохо инлайново вставлять все эти картинки-заглушки в страницы, а мы сегодня посмотрим на практике, как можно генерировать их на свой вкус и цвет с помощью Node.js. Мы создадим handlebars-шаблоны из SVG-картинок и будем из заполнять разными способами, начиная от простой заливки цветом или градиентом и заканчивая триангуляцией, мозаикой Вороного и использованием фильтров. Все действия будут разбираться по шагам. Полагаю эта статья будет интересна начинающим, которым интересно, как это делается, и нужен подробный разбор действий, но и опытным разработчикам возможно приглянутся некоторые идеи. Для начала мы отправимся в бездонное хранилище всевозможной всячины под названием NPM. Поскольку задача генерации наших картинок-заглушек предполагает однократную генерацию их на стороне сервера (или даже на машине разработчика, если речь о более-менее статическом сайте), то преждевременной оптимизацией мы заниматься не будем. Будем подключать все, что приглянется. Так что начинаем с заклинания npm init и приступаем к подбору зависимостей. Для начала это ColorThief. Вы, вероятно, уже слышали о нем. Замечательная библиотека, которая может вычленять цветовую палитру наиболее используемых цветов на картинке. Нам как раз что-то такое и нужно для начала. При установке этого пакета под линуксом возникла проблема — некий отсутствующий пакет cairo, которого нет в каталоге NPM. Эта странная ошибка решилась доустановкой девелоперских версий некоторых библиотек: Как этот инструмент работает будем смотреть в процессе. Но будет не лишним сразу подключить пакет rgb-hex для конвертирования цветового формата из RGB в Hex, что очевидно из его названия. Не будем заниматься велосипедостроением с такими простыми функциями. У заглушек один из самых важных параметров — это пропорции. Они должны совпадать с пропорциями оригинальной картинки. Соответственно нам нужно узнать ее размеры. Воспользуемся пакетом image-size для решения этого вопроса. Поскольку мы будем пробовать делать разные варианты картинок и все они будут в SVG формате, то так или иначе возникнет вопрос шаблонов для них. Можно конечно изворачиваться с шаблонными строками в JS, но зачем все это? Лучше уж взять "нормальный" шаблонизатор. К примеру handlebars. Просто и со вкусом, для нашей задачи будет в самый раз. Мы не будем сразу устраивать какую-то сложную архитектуру для этого эксперимента. Создаем файл main.js и импортируем туда все наши зависимости, а также модуль для работы с файловой системой. ColorThief требует дополнительной инициализации Используя зависимости, которые мы подключили, решение задач "загрузить картинку в скрипт" и "получить ее размер" не составляет особого труда. Допустим у нас есть картинка 1.jpg: Для людей, не знакомых с Node.js стоит сказать, что почти все, что связано с файловой системой, может происходить синхронно или асинхронно. У синхронных методов в названии в конце добавляется "Sync". Мы будем пользоваться ими, чтобы не сталкиваться с излишним усложнением и не ломать себе голову на ровном месте. Перейдем к первому примеру.  Для начала решим задачу простой заливки прямоугольника. У нашей картинки будет три параметра — ширина, высота и цвет заливки. Делаем SVG-картинку с прямоугольником, но вместо этих значений подставляем пары скобок и названия полей, которые будут содержать данные, переданные из скрипта. Вы вероятно уже видели такой синтаксис с традиционным HTML (например во Vue используется что-то похожее), но никто не мешает его использовать и с SVG-картинкой — шаблонизатору все равно, что это будет в конечном счете. Текст – он и в африке текст. Далее ColorThief дает нам один наиболее распространенный цвет, в примере это серый. Для того, чтобы воспользоваться шаблоном, мы читаем файл с ним, говорим handlebars, чтобы эта библиотека его скомпилировала и затем генерируем строку с готовой SVG-заглушкой. Шаблонизатор сам подставляет наши данные (цвет и размеры) в нужные места. Остается только записать результат в файл. Как можно видеть, работать с SVG довольно приятно — все файлы текстовые, можно легко их считывать и записывать. В результате получается картинка-прямоугольник. Ничего интересного, но по крайней мере мы убедились в работоспособности подхода (ссылка на полные исходники будет в конце статьи). Использование градиентов — это более интересный подход. Здесь мы можем использовать пару распространенных цветов с картинки и сделать плавный переход от одного в другой. Такое иногда можно встретить на сайтах, где загружаются длинные ленты картинок.  Наш SVG-шаблон теперь расширился этим самым градиентом. Для примера будем использовать обычный линейный градиент. Нас интересуют только два параметра — цвет в начале и цвет в конце: Сами цвета получаем с помощью все того же ColorThief. Он имеет два режима работы – либо дает нам один основной цвет, либо палитру с тем количеством цветов, которое мы укажем. Достаточно удобно. Для градиента нам нужно два цвета. В остальном этот пример похож на предыдущий: Таким образом можно делать всевозможные градиенты – не обязательно линейные. Но все же это достаточно скучный результат. Было бы здорово сделать какую-нибудь мозаику, которая хоть отдаленно будет походить на исходную картинку. Для начала попробуем просто сделать много прямоугольников и зальем их цветами из палитры, которую нам даст все та же библиотека.  Handlebars умеет много разных вещей, в частности в нем есть циклы. Будем передавать ему массив координат и цветов, а дальше он сам разберется. Мы лишь оборачиваем наш прямоугольник в шаблоне в each: Соответственно в самом скрипте мы теперь имеем полноценную палитру цветов, проходим циклами по координатам X/Y и делаем по прямоугольнику со случайным цветом из палитры. Все достаточно просто: Очевидно, что мозаика хоть и похожа по цветам на картинку, но вот с расположением цветов все совсем не так, как хотелось бы. Возможности ColorThief по этой части ограничены. Хотелось бы получить такую мозаику, в которой бы угадывалась изначальная картинка, а не просто был набор кирпичиков более-менее тех же цветов. Здесь нам придется немного углубиться и получить цвета из пикселей на картинке...  Поскольку у нас в консоли очевидно нет канваса, из которого мы обычно эти данные получаем, воспользуемся подспорьем в виде пакета get-pixels. Он может вытащить нужную информацию из буфера с картинкой, который у нас уже есть. Выглядеть это будет примерно так: Мы получаем объект, в котором содержится поле data — массив пикселей, такой же, как мы получаем из канваса. Напомню, что для того, чтобы получить цвет пикселя по координатам (X,Y) нужно произвести нехитрые вычисления: Таким образом мы можем для каждого прямоугольника взять цвет не из палитры, а прямо из картинки, и использовать его. Получится что-то такое (главное тут не забыть, что координаты на картинке отличаются от наших “нормализованных” от 0 до 100): Для большей красоты мы можем немного увеличить количество "кирпичиков", уменьшив их размер. Поскольку мы не передаем этот размер в шаблон (стоило бы конечно сделать его таким же параметром, как и ширина или высота картинки), изменим значения размеров в самом шаблоне: Теперь у нас есть мозаика, действительно похожая на исходную картинку, но занимающая при этом на порядок меньше места. Но пойдем дальше.  Прямоугольники – это хорошо, но треугольники обычно дают куда более интересные результаты. Так что попробуем сделать мозаику из кучи треугольников. Есть разные подходы к этому вопросу, мы воспользуемся триангуляцией Делоне: Главное преимущество алгоритма, которым мы воспользуемся, в том, что он по возможности избегает треугольников с очень острыми и тупыми углами. Нам для красивого изображения узкие и длинные треугольники совершенно не нужны. Разделим нашу задачу на меньшие. Для начала нужно нагенерировать точек для вершин треугольников. И было бы неплохо добавить немного случайностей в их координаты: Ознакомившись со структурой массива с треугольниками (console.log нам в помощь) находим себе точки, в которых будем брать цвет пикселя. Можно просто посчитать среднее арифметическое для координат вершин треугольников. Затем сдвигаем лишние точки с крайней границы, чтобы они никуда не вылезали и, получив настоящие, не нормализованные, координаты достаем цвет пикселя, который станет цветом треугольника.  Остается только собрать координаты нужных точек в строку и отправить ее вместе с цветом в Handlebars для обработки, как мы и делали раньше. В самом шаблоне теперь у нас будут не прямоугольники, а полигоны: Триангуляция – это очень занятная вещь. Увеличивая количество треугольников можно получать просто красивые картинки, ведь никто не говорит, что мы обязательно должны их использовать только в качестве заглушек. Существует задача, зеркальная предыдущей – разбиение или мозаика Вороного. Мы уже использовали ее при работе с шейдерами, но и здесь она тоже может пригодиться.  Как и с остальными известными алгоритмами, мы имеем готовую реализацию: Дальнейшие действия будут очень похожи на то, что мы делали в предыдущем примере. Разница лишь в том, что теперь мы имеем другую структуру – вместо массива треугольников у нас сложный объект. И параметры немного другие. В остальном все почти то же самое. Массив базовых точек генерируется так же, пропустим его, чтобы не делать листинг слишком длинным: В результате мы получаем мозаику из выпуклых многоугольников. Тоже весьма интересный результат. Последний пример, который мы посмотрим – это размытая мозаика. В наших руках вся мощь SVG, так почему бы не воспользоваться фильтрами?  Берем первую мозаику из прямоугольников и добавляем к ней стандартный фильтр “размыливания”: В результате получается размытая, “зацензуренная” превьюшка нашей картинки, занимает она почти в 10 раз меньше места (без сжатия), векторная и тянется на любой размер экрана. Таким же образом можно размылить и остальные варианты наших мозаик. В этой статье мы посмотрели, как можно генерировать всевозможные SVG-картинки-заглушки на Node.js и убедились, что это не такое уж и сложное занятие, если не писать все руками, а по возможности собирать готовые модули. Полные исходники доступны на гитхабе. Creative frontend developer