Как выбрать цвет шрифта на белый или черный в зависимости от цвета фона?
Я хочу показать некоторые изображения, как в этом примере
цвет заливки определяется полем в базе данных с цветом в шестнадцатеричном формате (например:ClassX -> Color: #66FFFF). Теперь я хочу показать данные над заливкой с выбранным цветом (как на изображении выше), но мне нужно знать, является ли цвет темным или светлым, поэтому я знаю, должны ли слова быть белыми или черными. Есть ли способ? ТКС
15 ответов:
опираясь на мой ответ на аналогичный вопрос.
нужно разбить код на 3 части, чтобы получить красного, зеленого и синего света. Каждые 2 цифры кода представляют собой значение в шестнадцатеричной (base-16) нотации. Я не буду вдаваться в подробности преобразования, они легко найти.
как только вы имеете интенсивности для индивидуальных цветов, вы можете определить общую интенсивность цвета и выбрать соответствующий текст.
if (red*0.299 + green*0.587 + blue*0.114) > 186 use #000000 else use #ffffff
Edit: вышеизложенное просто и работает достаточно хорошо, и, похоже, имеет хорошее принятие здесь, в StackOverflow. Однако один из приведенных ниже комментариев показывает, что это может привести к несоблюдению руководящих принципов W3C в некоторых обстоятельствах. При этом я получаю модифицированную форму, которая всегда выбирает самый высокий контраст на основе руководящих принципов.формула, приведенная для контраста в рекомендации W3C это
(L1 + 0.05) / (L2 + 0.05)
, гдеL1
- это яркость самого светлого цвета иL2
это яркость самого темного по шкале 0.0-1.0. Яркость черного равна 0,0, а Белого-1,0, поэтому подстановка этих значений позволяет определить тот, который имеет наибольший контраст. Если контраст для Черного больше, чем контраст для белого, используйте черный, в противном случае используйте белый. Учитывая яркость цвета, который вы тестируете какL
тест становится:if (L + 0.05) / (0.0 + 0.05) > (1.0 + 0.05) / (L + 0.05) use #000000 else use #ffffff
это упрощает вниз алгебраически к:
if L > sqrt(1.05 * 0.05) - 0.05
или примерно:
if L > 0.179 use #000000 else use #ffffff
осталось только вычислить
L
. Эта формула также приведено в руководстве и это похоже на преобразование из sRGB в линейный RGB с последующим рекомендация BT МСЭ-R.709 для яркости.for each c in r,g,b: c = c / 255.0 if c <= 0.03928 then c = c/12.92 else c = ((c+0.055)/1.055) ^ 2.4 L = 0.2126 * r + 0.7152 * g + 0.0722 * b
Как насчет этого (код JavaScript)?
/** * Get color (black/white) depending on bgColor so it would be clearly seen. * @param bgColor * @returns {string} */ getColorByBgColor(bgColor) { if (!bgColor) { return ''; } return (parseInt(bgColor.replace('#', ''), 16) > 0xffffff / 2) ? '#000' : '#fff'; }
Я не беру на себя ответственность за этот код, поскольку он не мой, но я оставляю его здесь для других, чтобы быстро найти в будущем:
на основе ответа Mark Ransoms, вот фрагмент кода для простой версии:
function pickTextColorBasedOnBgColorSimple(bgColor, lightColor, darkColor) { var color = (bgColor.charAt(0) === '#') ? bgColor.substring(1, 7) : bgColor; var r = parseInt(color.substring(0, 2), 16); // hexToR var g = parseInt(color.substring(2, 4), 16); // hexToG var b = parseInt(color.substring(4, 6), 16); // hexToB return (((r * 0.299) + (g * 0.587) + (b * 0.114)) > 186) ? darkColor : lightColor; }
и вот фрагмент кода для расширенной версии:
function pickTextColorBasedOnBgColorAdvanced(bgColor, lightColor, darkColor) { var color = (bgColor.charAt(0) === '#') ? bgColor.substring(1, 7) : bgColor; var r = parseInt(color.substring(0, 2), 16); // hexToR var g = parseInt(color.substring(2, 4), 16); // hexToG var b = parseInt(color.substring(4, 6), 16); // hexToB var uicolors = [r / 255, g / 255, b / 255]; var c = uicolors.map((col) => { if (col <= 0.03928) { return col / 12.92; } return Math.pow((col + 0.055) / 1.055, 2.4); }); var L = (0.2126 * c[0]) + (0.7152 * c[1]) + (0.0722 * c[2]); return (L > 0.179) ? darkColor : lightColor; }
использовать их просто звоните:
var color = '#EEACAE' // this can be any color pickTextColorBasedOnBgColorSimple(color, '#FFFFFF', '#000000');
тоже, спасибо
Alx
иchetstone
.
вот мое решение в Java для Android:
// Put this method in whichever class you deem appropriate // static or non-static, up to you. public static int getContrastColor(int colorIntValue) { int red = Color.red(colorIntValue); int green = Color.green(colorIntValue); int blue = Color.blue(colorIntValue); double lum = (((0.299 * red) + ((0.587 * green) + (0.114 * blue)))); return lum > 186 ? 0xFF000000 : 0xFFFFFFFF; } // Usage // If Color is represented as HEX code: String colorHex = "#484588"; int color = Color.parseColor(colorHex); // Or if color is Integer: int color = 0xFF484588; // Get White (0xFFFFFFFF) or Black (0xFF000000) int contrastColor = WhateverClass.getContrastColor(color);
В дополнение к арифметическим решениям также можно использовать нейронную сеть AI. Преимущество заключается в том, что вы можете адаптировать его к своему вкусу и потребностям (т. е. не совсем белый текст на ярких насыщенных красных тонах выглядит хорошо и так же читаем, как и черный).
вот аккуратная демонстрация Javascript, которая иллюстрирует эту концепцию. Вы также можете создать свою собственную формулу JS прямо в демо.
https://harthur.github.io/brain/
Ниже приведены некоторые диаграммы, которые помогли мне получить мой ум проблему. На первой диаграмме яркость является постоянной величиной 128, в то время как оттенок и насыщенность изменяются. На второй диаграмме насыщенность является постоянной величиной 255, в то время как оттенок и яркость изменяются.
Это просто пример, который изменит цвет флажка SVG при нажатии на элемент. Он будет установлен флажок цвет черный или белый на основе цвет фона выбранного элемента.
checkmarkColor: function(el) { var self = el; var contrast = function checkContrast(rgb) { // @TODO check for HEX value // Get RGB value between parenthesis, and remove any whitespace rgb = rgb.split(/\(([^)]+)\)/)[1].replace(/ /g, ''); // map RGB values to variables var r = parseInt(rgb.split(',')[0], 10), g = parseInt(rgb.split(',')[1], 10), b = parseInt(rgb.split(',')[2], 10), a; // if RGBA, map alpha to variable (not currently in use) if (rgb.split(',')[3] !== null) { a = parseInt(rgb.split(',')[3], 10); } // calculate contrast of color (standard grayscale algorithmic formula) var contrast = (Math.round(r * 299) + Math.round(g * 587) + Math.round(b * 114)) / 1000; return (contrast >= 128) ? 'black' : 'white'; }; $('#steps .step.color .color-item .icon-ui-checkmark-shadow svg').css({ 'fill': contrast($(self).css('background-color')) }); } onClickExtColor: function(evt) { var self = this; self.checkmarkColor(evt.currentTarget); }
Я использую эту функцию JavaScript для преобразования
rgb
/rgba
до'white'
или'black'
.function getTextColor(rgba) { rgba = rgba.match(/\d+/g); if ((rgba[0] * 0.299) + (rgba[1] * 0.587) + (rgba[2] * 0.114) > 186) { return 'black'; } else { return 'white'; } }
вы можете войти в любой из этих форматов и он будет выводить
'black'
или'white'
rgb(255,255,255)
rgba(255,255,255,0.1)
color:rgba(255,255,255,0.1)
255,255,255,0.1
основываясь на ответе @MarkRansom, я создал PHP-скрипт, который вы можете найти здесь:
function calcC($c) { if ($c <= 0.03928) { return $c / 12.92; } else { return pow(($c + 0.055) / 1.055, 2.4); } } function cutHex($h) { return ($h[0] == "#") ? substr($h, 1, 7) : $h; } function hexToR($h) { return hexdec(substr(cutHex($h), 0, 2)); } function hexToG($h) { return hexdec(substr(cutHex($h), 2, 2)); // Edited } function hexToB($h) { return hexdec(substr(cutHex($h), 4, 2)); // Edited } function computeTextColor($color) { $r = hexToR($color); $g = hexToG($color); $b = hexToB($color); $uicolors = [$r / 255, $g / 255, $b / 255]; $c = array_map("calcC", $uicolors); $l = 0.2126 * $c[0] + 0.7152 * $c[1] + 0.0722 * $c[2]; return ($l > 0.179) ? '#000000' : '#ffffff'; }
Я никогда не делал ничего подобного, но как насчет написания функции для проверки значений каждого из цветов против медианного цвета Hex 7F (FF / 2). Если два из трех цветов больше 7F, то вы работаете с более темным цветом.
подробный ответ Марка работает отлично. Вот реализация в javascript:
function lum(rgb) { var lrgb = []; rgb.forEach(function(c) { c = c / 255.0; if (c <= 0.03928) { c = c / 12.92; } else { c = Math.pow((c + 0.055) / 1.055, 2.4); } lrgb.push(c); }); var lum = 0.2126 * lrgb[0] + 0.7152 * lrgb[1] + 0.0722 * lrgb[2]; return (lum > 0.179) ? '#000000' : '#ffffff'; }
тогда можно вызвать эту функцию
lum([111, 22, 255])
чтобы получить белый или черный.
@SoBiT, я смотрел на ваш ответ, который выглядит хорошо, но есть небольшая ошибка в нем. Ваша функция hexToG и hextoB нуждаются в незначительном редактировании. Последнее число в substr-это длина строки, и поэтому в этом случае она должна быть "2", а не 4 или 6.
function hexToR($h) { return hexdec(substr(cutHex($h), 0, 2)); } function hexToG($h) { return hexdec(substr(cutHex($h), 2, 2)); } function hexToB($h) { return hexdec(substr(cutHex($h), 4, 2)); }
меньше имеет хороший
contrast()
функция, которая хорошо работала для меня, см. http://lesscss.org/functions/#color-operations-contrast"выбор из двух цветов дает наибольший контраст с другим. Это полезно для обеспечения того, чтобы цвет читался на фоне, что также полезно для соответствия доступности. Эта функция работает так же, как функция контраста в Compass для SASS. В соответствии с WCAG 2.0, цвета сравниваются используя их гамма-скорректированное значение яркости, а не их легкость."
пример:
p { a: contrast(#bbbbbb); b: contrast(#222222, #101010); c: contrast(#222222, #101010, #dddddd); d: contrast(hsl(90, 100%, 50%), #000000, #ffffff, 30%); e: contrast(hsl(90, 100%, 50%), #000000, #ffffff, 80%); }
выход:
p { a: #000000 // black b: #ffffff // white c: #dddddd d: #000000 // black e: #ffffff // white }
от шестнадцатеричного до черного или белого:
function hexToRgb(hex) { var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); return result ? [ parseInt(result[1], 16), parseInt(result[2], 16), parseInt(result[3], 16) ] : [0, 0, 0]; } function lum(hex) { var rgb = hexToRgb(hex) var lrgb = []; rgb.forEach(function(c) { c = c / 255.0; if (c <= 0.03928) { c = c / 12.92; } else { c = Math.pow((c + 0.055) / 1.055, 2.4); } lrgb.push(c); }); var lum = 0.2126 * lrgb[0] + 0.7152 * lrgb[1] + 0.0722 * lrgb[2]; return lum > 0.179 ? "#000000" : "#ffffff"; }
код версии Objective-c для iOS на основе ответа Марка:
- (UIColor *)contrastForegroundColor { CGFloat red = 0, green = 0, blue = 0, alpha = 0; [self getRed:&red green:&green blue:&blue alpha:&alpha]; NSArray<NSNumber *> *rgbArray = @[@(red), @(green), @(blue)]; NSMutableArray<NSNumber *> *parsedRGBArray = [NSMutableArray arrayWithCapacity:rgbArray.count]; for (NSNumber *item in rgbArray) { if (item.doubleValue <= 0.03928) { [parsedRGBArray addObject:@(item.doubleValue / 12.92)]; } else { double newValue = pow((item.doubleValue + 0.055) / 1.055, 2.4); [parsedRGBArray addObject:@(newValue)]; } } double luminance = 0.2126 * parsedRGBArray[0].doubleValue + 0.7152 * parsedRGBArray[1].doubleValue + 0.0722 * parsedRGBArray[2].doubleValue; return luminance > 0.179 ? UIColor.blackColor : UIColor.whiteColor; }
это быстрая версия ответа Марка Рэнсома как расширение UIColor
extension UIColor { // Get the rgba components in CGFloat var rgba: (red: CGFloat, green: CGFloat, blue: CGFloat, alpha: CGFloat) { var red: CGFloat = 0, green: CGFloat = 0, blue: CGFloat = 0, alpha: CGFloat = 0 getRed(&red, green: &green, blue: &blue, alpha: &alpha) return (red, green, blue, alpha) } /// Return the better contrasting color, white or black func contrastColor() -> UIColor { let rgbArray = [rgba.red, rgba.green, rgba.blue] let luminanceArray = rgbArray.map({ value -> (CGFloat) in if value < 0.03928 { return (value / 12.92) } else { return (pow( (value + 0.55) / 1.055, 2.4) ) } }) let luminance = 0.2126 * luminanceArray[0] + 0.7152 * luminanceArray[1] + 0.0722 * luminanceArray[2] return luminance > 0.179 ? UIColor.black : UIColor.white } }