Пандемия, осеннее обострение, зима близко и QR коды на каждом шагу, роботы наступают, рутина работы затягивает. Хочешь покушать — покажи картинку. Скучную и квадратную, для робота, не для человека. Только единицы могут ее читать, и только избранные рисовать. Вон на прошлой неделе как хабр qr кодом бомбануло!
Идея
А что если оживить и “очеловечить” QR код, сделать его не только для робота, но и для человека? Такая мысль мне не приходила, до тех пор пока я не наткнулся на статью про генерацию фотомозаик в виде QR кодов, наткнулся совершенно случайно, в процессе изучения вопроса генерации изображений для приложения по расчету параметров для варки пива. “QR кодная Мона Лиза” из этой статьи меня покорила своей причастностью к текущим реалиям.
И появилась идея. А если есть идея, то можно быстро сделать вечерний спайк на коленке и проверить ее работоспособность - “тыж программист”. Увы, спайк получился так себе, QR код читался через раз и выглядел отвратно, но идея не отпускала. Уж очень хотелось взять этот скучный ковидный QR код и сделать его прикольным, чтобы люди проверяющие его в кафе или торговом центре улыбнулись и настроение их улучшилось.
Так вечерний спайк превратился в ночной ресерч. И к утру, я нашел две вещи достойные внимания и работы над ними. Это были “halftone qr codes” и “Aesthetic QR codes”. А еще перелопатил кучу коммерчески успешных приложений которые умеют рисовать QR по всякому: рисуют классно, красиво, стабильно и… скучно.
Реализация день первый
Так я предложил коллеге по работе устроить крафтовый хакатон выходного дня, отпроситься у домашних, отложить все дела, засесть в, ставшем уже родным, офисе и покодить неведомую фигню на стеке который сами выберем.
Мы выбрали Next.js от Vercel. Так как на React уже что-то писали и очень хотелось пощупать как работает их реализация serverless, а еще хотелось ощутить степень интеграции с гитхабом и прочие неведомые плюшки. Так же рассматривали возможность сделать нативные приложения используя react-native, но вовремя поняли, что за выходные этого не реализовать, а веб приложение всегда можно поместить на рабочий стол в пару тачей.
Название родилось быстро, сошлись на QRART, Мона Лиза сыграла в этом не последнюю роль, да и pixel ART тоже проходил рядом. Купили домен и проксировали его через cloudflare на проект в vercel, для того чтобы еще на уровне dns отсечь ботов и всякую агрессивную нечисть. Проксирование оказалось очень простым и как приятное дополнение мы получили бесплатный https из коробки. Оказалось что можно не поднимать сервачок с убунтой, и не шаманить с letsencrypt, certbot-том и апачем. Что все просто работает, фактически на острие технологий.
За первую половину дня мы напилили спайк, который брал урл, превращал его в qr код используя библиотеку qrcode-generator в npm и отрисовывал на канвас результат.
Технически идея и реализация получилась простая. Мы делаем картинку в градациях серого и поверх нее рисуем точки или по правильному модули QR кода, по факту точки черного и белого цвета. По умолчанию мы используем средний уровень коррекции ошибок, в котором можно восстановить 15 процентов информации. А размер QR кода 6, это значит что в нем будет 41 модуль или точка, такой размер лучше всего подходит для визуального представления картинок. Конечно есть ограничения по размеру данных, но ссылка с ГосУслуг влезает. Вот тут можно посмотреть таблицу вместимости.
Локально все работало просто прекрасно, и мы попробовали задеплоить в облако. И обломались, оказалось что с 2019 года vercel не может подружить библиотеки канваса с лямбдами. Добро пожаловать в мир серверлесс. Ну ничего, ничего, поставили подпорку - все заработало, хакатон как-никак.
Ближе к концу первого дня у нас было приложение которое умело генерить QR код по урлу или тексту, и сращивать его с картинкой, которую предварительно мы переводили в оттенки серого. Спайк работал.
Захотелось локализацию и в Next она есть из коробки, да еще и с определением локалей, что очень и очень удобно. Называется это i18n-routing.
UI нашего спайка выглядел не особо, да и мы не дизайнеры. Но мы очень любим игры, старенькие nes’совские. И оказалось, есть готовая библиотека NES.css, которая стала фундаментом в дизайне нашего приложения и сэкономила кучу времени.
Еще захотелось добавить шаринг во все что можно, для этого мы использовали addthis, но мы не смогли добавить его в Next на уровне кода. Причину искать долго не стали. А просто использовали cloudflare apps и добавили addthis не через код. И оно работает, что удивительно. (Кстати, кто знает напишите, этот гад посылает запрос, но ничего не отрисовывает. Или подскажите в комментах хорошую альтернативу для шаринга)
Для аналитики мы взяли plausible.io это легковесное решение, которое пилят два разработчика и она тоже серверлесс. Но можно и у себя развернуть на большом, настоящем, железном сервачке.
В итоге первого дня разработки у нас было локализованное на два языка приложение, которое выглядело не отвратно, и даже минимально работало. Был выстроен удобный “пайплайн” разработки благодаря интеграции Vercel проекта с репозиторием и деплоем в прод по коммиту в мастер.
А еще был план на следующий день. Который мы просто записали в readme репозитория, да и весь TODO вели там же в одном файлике. Без трелл, планнеров джир и прочих сложных энтерпрайз штуковин.
Реализация день второй
В первый день работы над приложением мы реально кайфанули от разработки и прощупывания стека технологий, даже проблема с канвасом (которую не могут решить с 2019 года sic!) казалась не ущербностью, а просто технической недоработкой, на которую можно закрыть глаза.
На второй день было запланировано сделать галерею шаблонов для QR кода, генерацию из любой картинки. Сохранение как png, шаринг готовой картинки и, может быть, считывание готового QR кода и генерация нового с картинкой. А еще нужно было сделать редирект писем на личную почту с доменной.
С редиректа и стартовали. И тут выяснился интересный момент, что forwardemail который я успешно использовал для нескольких проектов, не заработал в связке с cloudflare и vercel. А improvmx.com заработал без вопросов. Почему так? Мы, следуя выбранной парадигме хакатона - "работает не трогай", исследовать не стали.
Галерею сделали из стандартного пакета react-image-gallery, тут все прошло гладко.
<ImageGallery
ref={gallery}
additionalClass="img-gallery animate__animated animate__bounceInDown"
items={galleryItems}
infinite={false}
showPlayButton={false}
showFullscreenButton={false}
/>
А вот с генерацией из загружаемой картинки пришлось повозиться. Момент в том что создание QR кодов происходит на сервере, и туда нужно эту картинку передать. По хорошему для этого нужно делать multipart запрос и передавать картинку. Обычно это делают в миделваре для next api routes. Но, хакатон же. В итоге, мы жмем картинку на клиенте и отправляем ее на сервер как json.
А обработанный результат складываем в DO spaces, конечно тут можно было взять хранилище от Amazon. Но под рукой оказался DO, тем более что он просто проксирует Amazon.
В завершении дня мы нашли и починили баг созданием QR кода в котором есть киррилица. Код создавался, но считывалась какая-то ерунда. Оказалось нужно прописать использование многобайтовой кодировки. Одна строчка кода и столько радости, что русский язык заработал:
qrcode.stringToBytes = qrcode.stringToBytesFuncs['UTF-8']
Результат
От второго дня нашего крафтового хакатона мы тоже остались очень довольны. И даже пофантазировали над фичами которые можно еще сделать - цветные qr коды, как это делает visualead, коды из мозаик, аналитика для qr кодов и многое многое другое.
Даже была идея открыть код, но там сейчас все довольно страшненько написано. Сейчас это скорее спайк, без тестов, без документации и с некоторыми явными костылями. И путь от спайка к настоящему продукту долог.
Главный результат в том, что мы получили кучу положительных эмоций от этих двух дней разработки. Мы можем точно сказать что выбранный нами стек отлично подходит для прототипирования приложений и его можно использовать.
Будем рады, если и вам понравится наш эксперимент.
А еще я успел написать статью которую вы сейчас прочитали. Мона Лиза прилагается и улыбается вам сквозь время и пиксели. Спасибо!
PS после публикации выяснилось что есть целое направление искусства, которое занимается созданием красивых QR кодов. И называется оно quick response art. И уже есть реализации генераторов, которые делают коды круто и красиво. Например библиотека amazing-qr. Скорее всего есть еще решения и сервисы. Буду вести и пополнять список под статьей.
amazing-qr - Python QR Code Generator GPLv3