Средний цвет в JavaScript

    Fruit average color


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


    Фон


    Рассчитываем средний цвет фотографии и устанавливаем цвет подложки. Пример


    Background


    Градиент


    Средний цвет высчитывается у верхней или нижней части картинки и используется в подложке для текста. Между картинкой и подложкой установлен плавный градиент. Стиль Яндекс.Дзена. Пример


    Gradient


    Градиент в стиле Minecraft — средний цвет высчитывается частями (горизональными полосками). Пример


    Minecraft gradient


    Рамка


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


    Border


    Тень


    Вычисленный средний цвет используется в задании цвета падающей тени. Пример


    Box shadow


    В CSS у элемента можно задать несколько теней. Для каждой из сторон фотографии вычислим средний цвет и установим отдельную тень. Пример


    Box shadow, 4 sides


    Видео


    Предыдущий пример применим в динамике для видео. Пример


    Video, box shadow, 4 sides


    Разделим стороны экрана на большее количество частей (30), в которых вычислим средний цвет для отбрасываемой тени, совсем как у Philips Ambilight. Пример


    Video, Ambilight


    Текстовая фотография


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


    Text photo


    Использование


    Средний цвет в примерах вычисляется с помощью небольшого пакета «fast-average-color». При подсчёте цвета учитывается прозрачность. По умолчанию используется квадратичный алгоритм, т.к. при простом усреднении цвет становится более тёмным.


    npm i -D fast-average-color


    Примеры


    Для получения среднего цвета из загруженных картинок, видео и canvas’a используется метод .getColor():


    <html>
    <body>
        ...
        <div class="image-container">
            <img src="image.png" />
            <div>
                Lorem ipsum dolor sit amet, consectetur adipiscing elit,
                sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
            </div>
        </div>
        <script src="https://unpkg.com/fast-average-color/dist/index.min.js"></script>
        <script>
            window.addEventListener('load', function() {
                var
                    fac = new FastAverageColor(),
                    container = document.querySelector('.image-container'),
                    color = fac.getColor(container.querySelector('img'));
    
                container.style.backgroundColor = color.rgba;
                container.style.color = color.isDark ? '#fff' : '#000';
    
                console.log(color);
                // {
                //     error: null,
                //     rgb: 'rgb(255, 0, 0)',
                //     rgba: 'rgba(255, 0, 0, 1)',
                //     hex: '#ff0000',
                //     hexa: '#ff0000ff',
                //     value: [255, 0, 0, 255],
                //     isDark: true,
                //     isLight: false
                // }
            }, false);
        </script>
    </body>
    </html>

    А для картинок, которые находятся в процессе загрузки — .getColorAsync():


    <html>
    <body>
        ...
        <div class="image-container">
            <img src="image.png" />
            <div>
                Lorem ipsum dolor sit amet, consectetur adipiscing elit,
                sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
            </div>
        </div>
        <script src="https://unpkg.com/fast-average-color/dist/index.min.js"></script>
        <script>
            var
                fac = new FastAverageColor(),
                container = document.querySelector('.image-container');
    
            fac.getColorAsync(container.querySelector('img'), function(color) {
                container.style.backgroundColor = color.rgba;
                container.style.color = color.isDark ? '#fff' : '#000';
    
                console.log(color);
                // {
                //     error: null,
                //     rgb: 'rgb(255, 0, 0)',
                //     rgba: 'rgba(255, 0, 0, 1)',
                //     hex: '#ff0000',
                //     hexa: '#ff0000ff',
                //     value: [255, 0, 0, 255],
                //     isDark: true,
                //     isLight: false
                // }
            });
        </script>
    </body>
    </html>

    Для картинок и видео с разных доменов стоит не забывать про CORS.


    Ссылки:


    Share post
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More
    Ads

    Comments 17

      0
      Пример с рамкой выглядит клево, только если у сторон близкие цвета, иначе получается 4 вариант с костром, который смотрится не очень
        0
        Насколько я понимаю, «квадратичный» алгоритм — это переход в «почти линейное» пространство RGB с грубым учетом гаммы. Гамма принимается равной 2, что конечно же дает очень приблизительный результат, но гораздо лучше, чем если не учитывать её вовсе.
          0

          Странно что в примерах сравниваются настройки simple и sqrt с усреднением цветов в lab. Lab для этого не подходит, лучше бы показали сравнение настоящим линейным усреднением.

            0
            Почему lab не подходит?
              0

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


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


              Так, например, сравним гамму: у линейного света — 1, у SRGB ≈ 2.2, у Lab — 3. И в первом примере, где смешиваются черный и белый, это видно: у Lab получается даже более тёмный цвет, чем у SRGB. Далее, при смешивании синего и жёлтого у Lab получается розовый.

          0
          На первый взгляд скрипт круче color-thief получился. Здорово, кстати, у вас выглядит amdient light.
            +2
            А как такой алгоритм обрабатывает очень пестрые фотографии, типа такой?
            image
              0
              У меня вышло #a2806b
              +3

              Есть разница между "средним цветом" и "цветом, встречающимся наиболее часто" — так же, как и между медианой, средним и модой: https://www.purplemath.com/modules/meanmode.htm


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


              data = [ 13, 13, 13, 13, 14, 14, 16, 18, 21 ]
              
              mean(data) = 15
              mode(data) = 13

              Поэтому у вас на примерах небо темнее полоски на фоне, а на картинке с листиками фоном вообще болотный цвет.


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

                0
                Прочитал заголовок «Градиент». Посмотрел на картинку, не понял. Прочитал абзац «Средний цвет высчитывается у верхней или нижней части картинки и используется в подложке для текста. Между картинкой и подложкой установлен плавный градиент». Присмотрелся к картинкам и понял что эту подложку я считал частью фотографии. Как мне кажется, это наиболее органичное применение «среднего цвета» из всех приведённых примеров
                  0
                  Варианты с градиентами и рамочками уходят в практику, а вот обсуждение, какой цвет для фотографии средний — это же потрясающе интересный вопрос.

                  Вот несколько интересных идей:

                  1. Минимальное расстояние (сумма расстояний) по цвету до каждого пиксела картинки. Высчитываем цветовое расстояние (например, как квадратный корень из суммы квадратов H, S, L) для пиксела, суммируем его, минимизируем, получаем «самый близкий ко всем». Для разноцветья — серый, для фотографий с общим цветом (например, синеватых) — усреднённо синеватый.

                  2. Минимизируем расстояние в пятимерном пространстве, где три кординаты — HSL, а ещё две — расстояние на картинке. Таким образом, чем дальше пиксел от рамочки, тем меньше он влияет на её цвет.

                  Можно придумать кучу вариантов. Уход в градиентики и прочие «нарисовать рамочку» уже не так интересно.
                    0
                    А для чего вы умножение цвет на alpha? (выглядит так, будто вы и сходите из предположегия, что любая картинка на черном фоне (мб стоит передавать предполагаемый цвет фона и смешивать?)
                    0
                    По логике sqrt алгоритм — это гамма-коррекция 2. Почему бы не использозать страндартную 2.2?
                      0

                      Наверное, побоялись что будет медленно. Но сравнения скорости нету. И можно ведь заранее посчитать все 256 значений, а потом просто доставать результат по индексу массива.
                      Кстати, ести точно, то стандартная не просто 2.2, а такая формула:
                      image

                      0
                      Можно считать количество каждого встречающегося значения 0-255 в массиве и по этой статистике уже пересчитывать разные варианты «усреднения», не сканируюя всю картинку заново, раз «усреднение» всё равно поканальное. Хотя это скорее оптимизация для больших размеров картинки.
                      Поскольку это производные от изображения данные, их можно рассчитать заранее (Каким-нибудь ImageMagick, например) и передавать уже готовые рядом с картинкой. Онлайн-рассчёт это хорошо, но пользователи устройств с питанием от аккумулятора, возможно, будут недовольны раходом батареи :(

                      Only users with full accounts can post comments. Log in, please.