Очень простой алгоритм подбора цвета, который работает
Различные сервисы Яндекса работают с цветом для решения интерфейсных задач: выделения информационных блоков, объектных ответов; для управления вниманием и создания визуальной иерархии.
В зависимости от задач подбор цвета может требовать сложных вычислений. Но бывает, что получить необходимый результат гораздо проще. Об этом история.
У нас в Yandex Launcher есть промо-карточки приложений: рейтинг, описание и кнопка «Установить». Это контекстные рекомендации— открываются поверх списка приложений или в папке на рабочем столе.
Первоначальная реализация
Цвет для фона карточки подбирался автоматически на основе иконки, кнопка — полупрозрачная белая. Алгоритм пытался определить основной цвет иконки, разбирая пиксели по hue. Такой подход не всегда давал красивый результат, обладал недостатками:
- неправильное определение цвета,
- «грязные» цвета из-за усреднения,
- тусклые кнопки, скучные карточки.
Чего на самом деле хотелось
Карточка должна была стать настоящим продолжением иконки. Цвета — сочными и яркими. Хотелось создать ощущение, что карточку бережно делали вручную, а не подсунули что-то небрежно сгенерированное автоматически.
Красивее хочется сделать всегда, но ресурсы не безграничны. Выделять команду на написание чудо-библиотеки по определению цветов не планировалось. Так что, задача:
Минимальными силами улучшить алгоритм определения цветов, придумать как покрасить карточку красиво, не изобретая при этом космический корабль.
В субботу я сдул пыль с редактора кода, вооружился HTML5 и Canvas и стал придумывать. В понедельник пришёл к команде с предложением.
Новый алгоритм определения цветов
Шаг 1.
Берём иконку. Выкидываем белые, чёрные и прозрачные пиксели.
Шаг 2.
Уменьшаем полученное изображение до размера 2 × 2 пикселя (с отключенным антиалиасингом). В результате получим четыре цвета иконки. В случае однородной исходной картинки они могут повторяться, ничего страшного.
У нас отключен антиалиасинг, чтобы цвета не смешивались, не становились «грязными».
На деле получается так:
квадрат делится на четыре части; мы берём средний пиксель из верхнего ряда каждой четверти.
В реализации всё просто: нам даже не нужен реальный даунсепмл изображения и вообще работа с графикой. Пиксели с нужной позицией берём из одномерного массива, получившегося после первого шага.
Шаг 3.
Почти всё готово. Осталось совсем чуть-чуть: достаем полученные цвета, переводим в HSL, сортируем по светлоте (L). Красим карточку.
Светлая схема:
- фон — самый светлый цвет,
- кнопка — ближайший к светлому,
- текст — самый тёмный.
Тёмная схема (если 2 и более цветов тёмные):
- фон — самый тёмный цвет,
- кнопка — ближайший к тёмному,
- текст — самый светлый.
Применяя цвета, проверяем контрастность: разница Lightness между фоном и кнопкой ≥ 20; между фоном и текстом ≥ 60. Если не соответствует, корректируем.
И ещё немного карточек для примера:
Результат
У нас получились красочные карточки, из настоящих цветов иконки, без «грязных» примесей. За счет использования нескольких цветов карточка выглядит гораздо живее. Особенно приятно, что при однородном фоне иконки, карточка становится её прямым продолжением: граница между ними совсем не заметна.
И самое главное:
Через 2 дня после предложения нового алгоритма первая реализация уже была доступна в dev-сборке. Опробовали внутри команды, настроили пороги для фильтра на первом шаге, предусмотрели особые случаи:
- иконка из одного цвета — делаем фон чуть темнее, чтобы не сливалась,
- иконка с фоном—смотрим пиксели по краям, если все одинаковы, ставим такой же фон карточки.
Доработанный алгоритм вошёл в ближайший релиз.
Отдельное спасибо Диме Овчарову, руководителю группы разработки Yandex Launcher, — за интерес, желание и терпение.