CodingDesignTutorialJavaScript

Дизайнер и код. Как я писал генератор превью попапа

Должен ли дизайнер уметь программировать? На этот вопрос вываливается куча статей в поисковиках, где у авторов приводятся похожие аргументы «за» и «против». Иногда в комментариях появляются разработчики, которые критикуют дизайнеров, лезущих не в своё дело.

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

Немного обо мне и коде

Я графический дизайнер и иллюстратор, который знаком с программированием. Я немного разбираюсь в HTML, CSS, JavaScript, знаком с PHP и работой с Xcode.

Знания не раз помогали в коммерческих проектах, связанных с веб-дизайном. В свободное время создаю скрипты для Иллюстратора и Фотошопа, написал расширения для Хрома Open in Figma, Open in Notion. Я не обладаю какими-то сверх глубокими познаниями в коде, но могу написать что-то простое самостоятельно, не тратя время знакомых разработчиков.

Предыстория

В конце 2019 мы с продуктовой и маркетинговой командами Модульбанка применили систему аналитики Микспанель в новом для себя ключе.

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

На первый взгляд попап выглядит вполне сносно

CSS и кастомизация

В ходе первых тестов обнаружился первый минус. Превью в редакторе Микспанели далёк от реальности. Во время отправки тестового варианта мы обнаружили сильные отличия:

Ожидание-реальность: попап Микспанели из маленького окна внезапно превратился в огромный баннер с шириной 800 пикселей

С этим связан второй минус. Оказалось, что в Микспанели нельзя настраивать свои CSS стили. Это довольно грустно для современного сервиса, а нам необходимо было стилизовать попап под интерфейс нашего продукта.

Джон тоже не понимает, где кастомизация CSS?

Embedded content: Confused Travolta GIF - Find & Share on GIPHY

Как решили

Я подготовил редизайн уведомлений на базе имеющихся продуктовых компонентов в Фигме, а верстальщик сделал единственно возможное в этой ситуации — проанализировал все CSS стили окна Микспанели и переписал каждый.

Когда попап из Микспанели попадает в личный кабинет пользователя, стили на лету меняются. Костыль, но куда деваться:

Поп-ап после редизайна

Теперь начинается самое интересное. После запуска теста появилась потребность проверять попап перед отправкой реальным пользователям. Запускать на тестовом мире каждое новое всплывающее окно оказалось затратным, а проверять надо было. Ведь мне, как иллюстратору, нужно примерить скетч, оценить готовую иллюстрацию с текстом, а копирайтерам нужно понимать, сколько символов заголовка и описания помещается в окно. Я могу собрать мокап в Фотошопе или Фигме, но как быть остальным у кого нет под рукой софта?

Так пришла идея реализовать на базе кода от верстальщиков обычную html-страничку с формой ввода текста для попапа и изображения. В данном случае обращаться к коллегам по такой мелочи нелогично: у всех задачи расписаны наперёд. Просить помочь в нерабочее время — можно, но неприлично. А так как знания программирования у меня имеются, то эта идея крепко засела в моей голове.

Генератор v1. Начало

На первом этапе в html с попапом я решил добавить простенькую форму из пары полей <textarea>, чтобы можно было вводить заголовок и описание в несколько строк и обычным <input> для текста кнопки. Здесь и далее я буду приводить только куски кода непосредственно генератора.

<div class="txt-form">
 <textarea id="myTitle" maxlength="30" rows="2" cols="45" placeholder="Заголовок"></textarea>
 <textarea id="myDesc" maxlength="250" rows="5" cols="45" placeholder="Описание"></textarea>
 <input type="text" id="myBtn" placeholder="Кнопка">
 <button onclick="submitTxt()">Просмотреть</button>
 <button onclick="resetPage()">Сбросить</button>
</div>

Первая версия страницы генератора

Кнопка «Просмотреть» для подставки текста в попап. Зачем для предпросмотра было делать отдельную кнопку, добавляя на пути пользователя лишний клик? На тот момент мне эта кнопка казалась логичной, но позже я её выпилил.

После клика на «Просмотреть» функция submitTxt() брала значение из полей ввода по ID и подставляла также по ID в соответствующие блоки попапа на странице:

function submitTxt() {
 var title = $("#myTitle").val();
 var descr = $("#myDesc").val();
 var btn = $("#myBtn").val();
 $("#mixpanel-notification-title").html(nl2br(title));
 $("#mixpanel-notification-body").html(nl2br(descr));
 $("#mixpanel-notification-button-link").text(btn);
}

Проблема в том, что хоть <textarea>и поддерживает перенос строк, но при подставке в попап эти переносы исчезали и текст шёл в одну строку.

Покопавшись, я узнал о существовании функции nl2br(), которая преобразует перенос строки в html-код. Поэтому наш текст вставляется в попап после промежуточной обработки этой функцией:

function nl2br (str, replaceMode) {
  var breakTag = "<br>";
  var replaceStr = (replaceMode) ? "$1"+ breakTag : "$1"+ breakTag +"$2";
  return (str + "").replace(/([^>\r\n]?)(\r\n|\n\r|\r|\n)/g, replaceStr);
}

Кнопка «Сбросить» вызывает перезагрузку страницы.

function resetPage() {
  window.location.reload(false);
}

Работа первой версии генератора

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

Embedded content: Tired Cary Elwes GIF by The Crush - Find & Share on GIPHY

Генератор v2. Эволюция

Прошло пару месяцев и в феврале 2020 я набрался сил для улучшений функционала и рефакторинга кода. В планах было:

  • сразу видеть результат ввода текста;
  • подгружать иллюстрации через форму;
  • скачивать попап в png, чтобы пересылать внутренним заказчикам на согласование.
<div class="txt-form">
  <textarea id="myTitle" maxlength="100" rows="2" cols="45" placeholder="Заголовок"></textarea>
  <textarea id="myDesc" maxlength="250" rows="4" cols="45" placeholder="Описание"></textarea>
  <input id="myBtn" type="text" maxlength="25" placeholder="Кнопка">
  <input id="picture" type="file" name="file1">
  <button id="reset">Сбросить</button>
  <button id="download">Скачать</button>
</div>

Вторая версия генератора

Ввод
Отказаться от кнопки «Просмотреть» мне помог здравый смысл и события input, которое реагирует на каждое изменение текста в поле ввода, и change, которое сработает при потере фокуса с поля:

$(document).ready(function() {
  $("#myTitle, #myDesc, #myBtn").on("input", delayChange);
  $("#myTitle, #myDesc, #myBtn").on("change", doChange);
});

Чтобы после каждой набранной буквы весь текст не отправлялся в попап, я добавил функцию delayChange(). Она ждёт одну секунду, если больше не было нажатий, и отправляет обработанный текст в попап. Если же вставлять в поле скопированный текст и переходить сразу на следующее, то сработает событие change. Здесь задержка без надобности и достаточно функции doChange().

function doChange(e) {
  obj = e.target;
  var inpID = $(obj).attr("id");
  $("."+inpID).html(nl2br(obj.value));
}

var timeout = null;

function delayChange(e) {
  clearTimeout(timeout);
  timeout = setTimeout(function() {
    doChange(e);
  }, 1000);
}

Добавление иллюстраций
Чтобы добавить иллюстрации, я вставил стандартный инпут <input type="file">, но чтобы стилизовать внешний вид под себя — подключил библиотеку FileInput.

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

После выбора файла, также как и с вводом текста, надо подставить его в попап. По знакомому нам событию change вызывается функция previewFile().

$(document).ready(function() {
  $("#picture").on("change", previewFile);
});

Она считывает файл методом readAsDataURL как кодированный url, чтобы в итоге изображение оказалось в попапе в обычном теге <img>.

function previewFile() {
  var preview = document.querySelector("#mixpanel-notification-img");
  var file = document.querySelector("input[type=file]").files[0];
  var reader = new FileReader();
  reader.onloadend = function () {
    preview.src = reader.result;
  }
  if (file) { reader.readAsDataURL(file); }
}

Скачивание
Что представляет собой скачивание превью? Нужно текущее состояние блока попапа на html-странице превратить в растровое изображение и после скачать. Для этого решил использовать открытую библиотеку html2canvas. Она в общих чертах берёт содержимое указанного нами блока и отрисовывает, как графику, в элементе <canvas>.

$(document).ready(function() {
  $("#download").on("click", screenshot);
});

function screenshot() {
  var canvasPromise  = html2canvas(document.getElementById("mixpanel-notification-mainbox"), {
    useCORS: true
  });
  canvasPromise.then(function(canvas) {
    saveAs(canvas.toDataURL("image/png"), "mixpanel-web.png");
  });
}

Это только часть дела. Полученное в канвасе изображение нам ещё нужно скачать. Сразу после отрисовки вызывается функция saveAs(), которая создаёт временную ссылку для скачивания канваса и эмулирует клик по ней, подчищая всё за собой:

function saveAs(uri, filename) {
  var link = document.createElement("a");
  if (typeof link.download === 'string') {
    link.href = uri;
    link.download = filename;
    document.body.appendChild(link);
    link.click();
    document.body.removeChild(link);
  } else {
      window.open(uri);
    }
}

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

Работа второй версии генератора

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

Например, я делал страничку локально и c html2canvas не работало скачивание, пока в консоли Хрома не обнаружил ошибку. При запуске из папки не выполнялся метод toDataURL: Хром считал, что в канвас попадают данные с другого домена и нарушается Same Origin Policy. Стоило загрузить генератор на хостинг и всё сразу заработало.

Когда забыл про инструменты отладки в Хроме

Embedded content: Oh Brother Reaction GIF by reactionseditor - Find & Share on GIPHY

Заключение

Знать хотя бы немного код — отличный мастхев для дизайнеров. Вы сможете лучше понимать разработчиков и верстальщиков. Вы сможете самостоятельно делать простые прототипы и автоматизировать собственную работу. Да и лишний навык — это +1 балл в резюме и лишний 0 к возможной зарплате.

Я рекомендую хотя бы немного изучить языки программирования. Насколько глубоко? Каждый определяет сам, насколько это интересно и от наличия свободного времени.

Если не понравится — ок, у дизайнеров столько направлений для развития, что можно найти любое по душе. Не стоит заставлять себя изучать то, что не нравится, потому что это написали в вакансии или «все вокруг говорят, что надо». Вы так или иначе найдёте применение своим сильным сторонам.

Когда несколько строк кода немного улучшают жизнь , находишь в этом особое очарование. Если нравится это направление, то находишь время для практики, идёшь к поставленным целям и запускаешь свои проекты. Я по-прежнему не обладаю сильными знаниями в языках программирования, но мне нравится работать с кодом. И это главное.

Прим. автора: спасибо Коле Жидкову за терпеливый перебор CSS стилей попапа, Радмиру Кашаеву за ликбезы по кодингу и Тане Иншаковой за редактуру.

Где меня найти: Telegram / Facebook / Behance / Dribbble