如何决定字体颜色在白色或黑色取决于背景颜色?

我想展示一些图片,像这个例子alt文本

填充颜色由数据库中颜色为十六进制的字段决定(例如:ClassX -> color: #66FFFF)。 现在,我想显示上面的数据与所选的颜色填充(如上图),但我需要知道如果颜色是暗或光,所以我知道如果文字应该在白色或黑色。 有办法吗?谢谢< / p >
115207 次浏览

我从来没有做过这样的事情,但是写一个函数来检查每个颜色的值与十六进制7F (FF / 2)的中位数颜色。如果三种颜色中有两种大于7F,那么你正在使用较深的颜色。

建立在我的回答一个类似的问题

您需要将十六进制代码分解为3个部分,以获得单独的红色、绿色和蓝色强度。代码的每两个数字代表一个十六进制(以16为基数)表示法的值。这里我就不详细讲了,它们很容易查到。

一旦有了各个颜色的强度,就可以确定颜色的整体强度并选择相应的文本。

if (red*0.299 + green*0.587 + blue*0.114) > 186 use #000000 else use #ffffff

186的阈值是基于理论的,但可以根据口味进行调整。根据评论,低于150的阈值可能更适合您。


编辑:以上是简单的,工作得相当好,似乎有良好的接受在这里的StackOverflow。然而,下面的一条评论表明,在某些情况下,它可能导致不符合W3C指南。在此,我推导了一个修改的形式,总是选择最高对比度的基础上的指导方针。如果你需要符合W3C规则,那么我将坚持使用上面更简单的公式。有关此问题的有趣研究,请参阅对比度数学和相关视觉问题

W3C推荐标准(WCAG 2.0)中给出的对比度公式是(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的转换,然后是亮度的ITU-R建议BT.709

for each c in r,g,b:
c = c / 255.0
if c <= 0.04045 then c = c/12.92 else c = ((c+0.055)/1.055) ^ 2.4
L = 0.2126 * r + 0.7152 * g + 0.0722 * b

阈值0.179不应该改变,因为它与W3C指南相关联。如果你发现结果不是你喜欢的,试试上面更简单的公式。

这只是一个示例,它将在单击元素时更改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);
}

https://gist.github.com/dcondrey/183971f17808e9277572

这个(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';
}

以下是我用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);

我使用这个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

我不认为这段代码是我的功劳,因为它不是我的,但我把它留在这里,让其他人将来很快就能找到:

根据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');

另外,感谢Alxchetstone

根据@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';
}

@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));
}

LESS有一个很好的contrast()函数,它对我来说工作得很好,参见http://lesscss.org/functions/#color-operations-contrast

"在两种颜色中选择一种与另一种颜色形成最大的对比。 这对于确保颜色在背景下是可读的很有用,这对于可访问性遵从也很有用。此函数的工作方式与Compass for SASS中的对比函数相同。根据WCAG 2.0,颜色是用它们的伽马校正亮度值来比较的,而不是它们的亮度。" < / em > < / p >

例子:

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
}

除了算术解决方案,还可以使用人工智能神经网络。这样做的好处是,你可以根据自己的口味和需求来定制它。灰白色的文字在明亮的饱和红色上看起来很好,和黑色一样可读)。

下面是一个简洁的Javascript演示,演示了这个概念。你也可以在演示中生成自己的JS公式。

< a href = " https://harthur.github。io /大脑/ noreferrer“rel = > https://harthur.github.io/brain/ < / >

下面是一些帮助我理清思路的图表。 在第一个图表中,亮度是一个常数128,而色调和饱和度变化。在第二张图中,饱和度是一个常数255,而色调和亮度变化

在第一个图表中,亮度是一个常数128,而色调和饱和度变化:

饱和度是一个常数255,而色调和亮度变化:

Mark的详细回答工作得很好。下面是一个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])来获取白色或黑色。

从hex到black或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";
}

基于Mark回答的iOS Objective-c版本代码:

- (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;
}

这是Mark Ransom的答案的一个快速版本,作为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
} }

基于来自链接使前景色黑色或白色取决于背景和此线程的不同输入,我为Color创建了一个扩展类,为您提供所需的对比色。

代码如下:

 public static class ColorExtension
{
public static int PerceivedBrightness(this Color c)
{
return (int)Math.Sqrt(
c.R * c.R * .299 +
c.G * c.G * .587 +
c.B * c.B * .114);
}
public static Color ContrastColor(this Color iColor, Color darkColor,Color lightColor)
{
//  Counting the perceptive luminance (aka luma) - human eye favors green color...
double luma = (iColor.PerceivedBrightness() / 255);


// Return black for bright colors, white for dark colors
return luma > 0.5 ? darkColor : lightColor;
}
public static Color ContrastColor(this Color iColor) => iColor.ContrastColor(Color.Black);
public static Color ContrastColor(this Color iColor, Color darkColor) => iColor.ContrastColor(darkColor, Color.White);
// Converts a given Color to gray
public static Color ToGray(this Color input)
{
int g = (int)(input.R * .299) + (int)(input.G * .587) + (int)(input.B * .114);
return Color.FromArgb(input.A, g, g, g);
}
}

用所有24位的颜色测试怎么样?

请注意,YIQ方法将返回1.9:1的最小对比度,这不会通过AA和AAA WCAG2.0测试,假设阈值为128。

对于W3C方法,它将返回最小对比度4.58:1,通过AA和AAA测试的大文本,AA测试的小文本,它不会通过AAA测试的小文本的每一种颜色。

这是我自己的方法,我一直在使用,到目前为止还没有问题😄

function hexToRgb(hex) {
const hexCode = hex.charAt(0) === '#'
? hex.substr(1, 6)
: hex;


const hexR = parseInt(hexCode.substr(0, 2), 16);
const hexG = parseInt(hexCode.substr(2, 2), 16);
const hexB = parseInt(hexCode.substr(4, 2), 16);
// Gets the average value of the colors
const contrastRatio = (hexR + hexG + hexB) / (255 * 3);


return contrastRatio >= 0.5
? 'black'
: 'white';
}

如果你像我一样正在寻找一个考虑alpha的RGBA版本,这里有一个非常好的工作,具有高对比度。

function getContrastColor(R, G, B, A) {
const brightness = R * 0.299 + G * 0.587 + B * 0.114 + (1 - A) * 255;


return brightness > 186 ? "#000000" : "#FFFFFF";
}

下面是我基于Mark的惊人回答编写的Java Swing代码:

public static Color getColorBasedOnBackground(Color background, Color darkColor, Color lightColor) {
// Calculate foreground color based on background (based on https://stackoverflow.com/a/3943023/)
Color color;
double[] cL = new double[3];
double[] colorRGB = new double[] {background.getRed(), background.getGreen(), background.getBlue()};


for (int i = 0; i < colorRGB.length; i++)
cL[i] = (colorRGB[i] / 255.0 <= 0.03928) ? colorRGB[i] / 255.0 / 12.92 :
Math.pow(((colorRGB[i] / 255.0 + 0.055) / 1.055), 2.4);


double L = 0.2126 * cL[0] + 0.7152 * cL[1] + 0.0722 * cL[2];
color = (L > Math.sqrt(1.05 * 0.05) - 0.05) ? darkColor : lightColor;


return color;
}

这是马克·兰森的答案的R版本,只使用R基。

hex_bw <- function(hex_code) {


myrgb <- as.integer(col2rgb(hex_code))


rgb_conv <- lapply(myrgb, function(x) {
i <- x / 255
if (i <= 0.03928) {
i <- i / 12.92
} else {
i <- ((i + 0.055) / 1.055) ^ 2.4
}
return(i)
})


rgb_calc <- (0.2126*rgb_conv[[1]]) + (0.7152*rgb_conv[[2]]) + (0.0722*rgb_conv[[3]])


if (rgb_calc > 0.179) return("#000000") else return("#ffffff")


}


> hex_bw("#8FBC8F")
[1] "#000000"
> hex_bw("#7fa5e3")
[1] "#000000"
> hex_bw("#0054de")
[1] "#ffffff"
> hex_bw("#2064d4")
[1] "#ffffff"
> hex_bw("#5387db")
[1] "#000000"

我使用的tinyColor图书馆也可以做这项工作。

import { TinyColor } from '@ctrl/tinycolor'


// ...


getColorContrast(color = '#66FFFF'): string {
if(new TinyColor(color).getLuminance() > 0.179) { // 0.179 -> Mark Ransom answer
return '#000'
} else {
return '#fff'
}
}

此方法也将接受rgb颜色,如rgb(102,255,255)

我在@SudoPlz提出的高级函数的基础上做了一个函数,它也考虑了浅色和深色:

function getTextColor (bgColor, lightColor = '#FFFFFF', darkColor = '#000000') {


const getLuminance = function (hexColor) {
var color = (hexColor.charAt(0) === '#') ? hexColor.substring(1, 7) : hexColor
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 => col <= 0.03928 ? col / 12.92 : ((col + 0.055) / 1.055) ** 2.4)


return (0.2126 * c[0]) + (0.7152 * c[1]) + (0.0722 * c[2]);
}


var L = getLuminance(bgColor)
var L1 = getLuminance(lightColor)
var L2 = getLuminance(darkColor)


return (L > Math.sqrt((L1 + 0.05) * (L2 + 0.05)) - 0.05) ? darkColor : lightColor;
}

因此,如果深色文本不是黑色而是栗色,灰色背景上的推荐文本颜色会变成白色:

getTextColor('#808080')
"#000000"
getTextColor('#808080', '#FFFFFF', '#800000')
"#FFFFFF"

当使用androidx.compose.ui.graphics.Color时,接受的答案不知何故从未在Android上工作。然后我发现在Android Jetpack Compose中实际上内置了luminance()函数,该函数返回[0,1]之间的亮度值。文档上写着“__abc2”。

这里是官方源代码

使用示例:

fun Color.generateOnColor()
: Color {
return if (luminance() > 0.5f) {
Color.Black.copy(alpha = .8f)
} else {
Color.White
}
}

以防有人关心Mark Ransom的答案的SCSS版本:

@use 'sass:color' as *;
@use 'sass:math' as *;


@function col_r($color) {
@if $color <= 0.03928 {
@return $color / 12.92;
} @else {
@return pow((($color + 0.055) / 1.055), (2.4));
}
}


@function pickTextColorBasedOnBgColorAdvanced(
$bgColor,
$lightColor,
$darkColor
) {
$r: red($bgColor);
$g: green($bgColor);
$b: blue($bgColor);
$ui_r: $r / 255;
$ui_g: $g / 255;
$ui_b: $b / 255;


$ui_r_c: col_r($ui_r);
$ui_g_c: col_r($ui_g);
$ui_b_c: col_r($ui_b);


$L: (0.2126 * $ui_r_c) + (0.7152 * $ui_g_c) + (0.0722 * $ui_b_c);
@if ($L > 0.179) {
@return $darkColor;
} @else {
@return $lightColor;
}
}