Как проверить, не «слишком ли черный» шестнадцатеричный цвет?


111

Я пытаюсь оценить темноту цвета, выбранного палитрой цветов, чтобы увидеть, "слишком ли он черный", и если да, установите его на белый. Я подумал, что могу использовать первые символы шестнадцатеричного значения, чтобы осуществить это. Он работает, но меняет и некоторые вполне законные «светлые» цвета.

У меня есть код для этого:

        if (lightcolor.substring(0,3) == "#00"|| lightcolor.substring(0,3) == "#010"){
            lightcolor="#FFFFFF";
            color=lightcolor;
        }

Должен быть более эффективный способ с помощью шестнадцатеричной математики узнать, что цвет вышел за пределы определенного уровня темноты? Например, если lightcolor + "какое-то шестнадцатеричное значение" <= "какое-то шестнадцатеричное значение", тогда установите его на белый.

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


1
Вы пробовали взять палитру цветов и проверить значения? Я заметил, что когда R, G и B все ниже ~ 70, становится темно. Возможно, это неправильный способ, но он один.
Рик Кейперс,

1
Поскольку вы уже используете tinyColor, преобразуйте цвет в HSL и посмотрите на компонент L. 1 = белый, 0 = черный
Андреас

4
@Andreas HSL легкость не учитывает человеческое восприятие. Значение L 0,5 будет иметь различную воспринимаемую яркость для разных оттенков.
Alnitak

1
@Alnitak Вы правы, но описание ТО не совсем точное. Таким образом, любое значение ниже 3/8 могло быть достаточно темным для его целей.
Андреас

1
@Andreas, это зависит - если вы посмотрите на значения яркости ITU в моем ответе, вы увидите, что синий цвет воспринимается только на 1/10 яркости зеленого.
Alnitak

Ответы:


226

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

Предполагая шестизначный цвет:

var c = c.substring(1);      // strip #
var rgb = parseInt(c, 16);   // convert rrggbb to decimal
var r = (rgb >> 16) & 0xff;  // extract red
var g = (rgb >>  8) & 0xff;  // extract green
var b = (rgb >>  0) & 0xff;  // extract blue

var luma = 0.2126 * r + 0.7152 * g + 0.0722 * b; // per ITU-R BT.709

if (luma < 40) {
    // pick a different colour
}

РЕДАКТИРОВАТЬ

С мая 2014 года tinycolorтеперь есть getBrightness()функция, хотя и с использованием весовых коэффициентов CCIR601 вместо вышеупомянутых ITU-R.

РЕДАКТИРОВАТЬ

Результирующий диапазон значений яркости составляет 0..255, где 0 - самый темный, а 255 - самый светлый. Значения больше 128 считаются незначительными tinycolor. (бессовестно скопировано из комментариев @ pau.moreno и @Alnitak)


12
давно не видел хороших манипуляций с битами в javascript. крутая вещь. en.wikipedia.org/wiki/Rec._709#Luma_coefficients
jbabey

2
Хороший код, но после тестирования предлагаю var luma = (r + g + b) / 3; if (luma <128) {// будет полезнее. }
Терри Лин

2
@TerryLin Почему? Приведенные коэффициенты являются стандартными значениями ITU, которые учитывают тот факт, что зеленый цвет воспринимается более ярко, чем красный (а затем синий).
Alnitak

1
Результирующий lumaдиапазон значений: 0..255где 0самый темный и 255самый светлый (сумма трех коэффициентов равна одному).
pau.moreno

1
@gabssnake только с мая 2014 года, а isDark()порог жестко запрограммирован на 128
Alnitak

21

Библиотека TinyColor (вы уже упомянули о ней) предоставляет несколько функций для проверки и управления цветами, среди них:


15

Я нашел эту PHP-функцию WooCommerce Wordpress ( wc_hex_is_light ) и преобразовал ее в JavaScript. Работает отлично!

function wc_hex_is_light(color) {
    const hex = color.replace('#', '');
    const c_r = parseInt(hex.substr(0, 2), 16);
    const c_g = parseInt(hex.substr(2, 2), 16);
    const c_b = parseInt(hex.substr(4, 2), 16);
    const brightness = ((c_r * 299) + (c_g * 587) + (c_b * 114)) / 1000;
    return brightness > 155;
}

1
Супер круто, спасибо! Я тестировал его с несколькими цветами, обнаружение было правильным со всеми из них :)
Дэвид Дал Буско

colorIsDarkOrLight (цвет) {var hex = color.replace ("#", ""); var c_r, c_g, c_b, яркость = ""; если (hex.length == 3) {c_r = parseInt (hex.substr (0, 2), 16); c_g = parseInt (hex.substr (1, 2), 16); c_b = parseInt (hex.substr (2, 2), 16); яркость = (c_r * 299 + c_g * 587 + c_b * 114) / 1000; } еще {c_r = parseInt (hex.substr (0, 2), 16); c_g = parseInt (hex.substr (2, 2), 16); c_b = parseInt (hex.substr (4, 2), 16); } вернуть яркость> 155; },
Педро Энрике

Используйте с шестнадцатеричными символами 3 и 6
Педро Энрике

6

Вы можете вычислить яркость :

Таким образом, яркость является показателем того, насколько яркой будет поверхность.

Так что здорово выбрать, будет ли текст белым или черным.

var getRGB = function(b){
    var a;
    if(b&&b.constructor==Array&&b.length==3)return b;
    if(a=/rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(b))return[parseInt(a[1]),parseInt(a[2]),parseInt(a[3])];
    if(a=/rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(b))return[parseFloat(a[1])*2.55,parseFloat(a[2])*2.55,parseFloat(a[3])*2.55];
    if(a=/#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(b))return[parseInt(a[1],16),parseInt(a[2],16),parseInt(a[3],
16)];
    if(a=/#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(b))return[parseInt(a[1]+a[1],16),parseInt(a[2]+a[2],16),parseInt(a[3]+a[3],16)];
    return (typeof (colors) != "undefined")?colors[jQuery.trim(b).toLowerCase()]:null
};

var luminance_get = function(color) {
    var rgb = getRGB(color);
    if (!rgb) return null;
        return 0.2126 * rgb[0] + 0.7152 * rgb[1] + 0.0722 * rgb[2];
}

Приведенный выше метод позволяет передавать цвет в разных форматах, но алгоритм в основном только в luminance_get.

Когда я его использовал, я устанавливал черный цвет, если яркость была больше 180, в противном случае - белый.


5

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

Чтобы быть точным, вы ищете «яркость», «значение» или «относительную яркость», как предлагали другие. Вы можете рассчитать это по-разному (например, быть человеком!) Http://en.wikipedia.org/wiki/HSL_and_HSV#Lightness

  1. Возьмите максимум R, G и B.
  2. Возьмите среднее значение максимума и минимума из R, G и B.
  3. Возьмите среднее значение всех трех.
  4. Используйте некоторые средневзвешенные значения, как предлагали здесь другие.

AFAIK только расчет яркости, описанный на странице Википедии, является моделью, основанной на восприятии.
Alnitak

2
Приятно отметить различие между физической световой энергией и воспринимаемой яркостью, но я думаю, что у вас все довольно перепутано. Раздел статьи в Википедии, на которую вы ссылаетесь, имеет четвертый пункт, в котором говорится: «Более релевантной для восприятия альтернативой является использование яркости Y 'в качестве измерения яркости» (выделено мной), а затем приводится формула, представленная в книге Альнитака. и ответы Робина. Другими словами, метод, который вы упустили и не рекомендовали, лучше всего соответствует человеческому восприятию.
John Y

@JohnY да, это то, что я пытался сказать - он упустил единственный, который действительно соответствует остальной части его ответа.
Alnitak

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

4

Это работает с шестнадцатеричным кодом, например #fefefe

function isTooDark(hexcolor){
    var r = parseInt(hexcolor.substr(1,2),16);
    var g = parseInt(hexcolor.substr(3,2),16);
    var b = parseInt(hexcolor.substr(4,2),16);
    var yiq = ((r*299)+(g*587)+(b*114))/1000;
    // Return new color if to dark, else return the original
    return (yiq < 40) ? '#2980b9' : hexcolor;
}

Вы можете изменить его на возврат trueили falseна изменение

return (yiq < 40) ? '#2980b9' : hexcolor;

к

return (yiq < 40);

@Vivek, потому что вам нужно использовать полные шестнадцатеричные значения (# 000000 и #ffffff)
TheCrazyProfessor

2

Возможное решение - преобразовать цвет из RGB в HSB . HSB обозначает оттенок, насыщенность и яркость (также известный как HSV, где V означает значение). Тогда вам нужно проверить только один параметр: яркость.


1

Я понимаю, что этому разговору несколько лет, но он все еще актуален. Я хотел добавить, что у моей команды была такая же проблема в Java (SWT), и я обнаружил, что это немного точнее:

private Color getFontColor(RGB bgColor) {
    Color COLOR_BLACK = new Color(Display.getDefault(), 0, 0, 0);
    Color COLOR_WHITE = new Color(Display.getDefault(), 255, 255, 255);

    double luminance = Math.sqrt(0.241 
       * Math.pow(bgColor.red, 2) + 0.691 * Math.pow(bgColor.green, 2) +  0.068 
       * Math.pow(bgColor.blue, 2));
    if (luminance >= 130) {
        return COLOR_BLACK;
    } else {
        return COLOR_WHITE;
    }
}
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.