如何根据背景颜色决定字体颜色为白色还是黑色?
Posted
技术标签:
【中文标题】如何根据背景颜色决定字体颜色为白色还是黑色?【英文标题】:How to decide font color in white or black depending on background color? 【发布时间】:2011-04-25 23:11:26 【问题描述】:我想展示一些类似这个例子的图片
填充颜色由数据库中的一个字段决定,颜色为十六进制(例如:ClassX -> 颜色:#66FFFF)。 现在,我想用所选颜色在填充上方显示数据(如上图所示),但我需要知道颜色是深还是浅,所以我知道这些词应该是白色还是黑色。 有办法吗? tks
【问题讨论】:
相关(但也是骗子):***.com/questions/3942878/…***.com/questions/3116260/… 【参考方案1】:我从来没有做过这样的事情,但是编写一个函数来检查每种颜色的值与 Hex 7F (FF / 2) 的中值颜色的对比如何。如果三种颜色中有两种大于 7F,那么您正在使用较暗的颜色。
【讨论】:
【参考方案2】:以我的answer to a similar question 为基础。
您需要将十六进制代码分成 3 部分以获得单独的红色、绿色和蓝色强度。代码的每 2 位数字代表一个十六进制(base-16)表示法的值。我不会在这里详细介绍转换的细节,它们很容易查找。
获得各个颜色的强度后,您可以确定颜色的整体强度并选择相应的文本。
if (red*0.299 + green*0.587 + blue*0.114) > 186 use #000000 else use #ffffff
186 的阈值是基于理论的,但可以根据口味进行调整。基于低于 150 阈值的 cmets 可能对您更有效。
编辑: 上面的内容很简单,工作得相当好,并且似乎在 *** 上得到了很好的接受。但是,以下 cmets 之一表明它在某些情况下可能导致不遵守 W3C 指南。因此,我推导出一个修改后的表格,它总是根据指南选择最高的对比度。如果您 不需要 需要遵守 W3C 规则,那么我会坚持使用上面更简单的公式。
W3C Recommendations 中给出的对比度公式是 (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
。这个公式也是given in the guidelines,看起来像是从sRGB到线性RGB的转换,然后是ITU-R recommendation BT.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
不应更改 0.179 的阈值,因为它与 W3C 指南相关联。如果您发现结果不符合您的喜好,请尝试上述更简单的公式。
【讨论】:
Tks 马克。尝试了一些更改:仅通过第一个数字计算红色绿色和蓝色(不太精确,但重量更大的数字)而不是 186 使用 9。对我来说效果更好一点,特别是绿色。 这个公式错了很多。举个例子,它给黄色的#D6B508
一个值171,因此是白色的对比。然而,对比度应该是黑色的(在此确认:webaim.org/resources/contrastchecker)
Here's another demo 比较此答案中给出的两个公式。使用颜色选择器(在最近的 Firefox 或 Chrome 中),您可以检查任何颜色的对比度。
玩了一会儿@chetstone 的演示后,我认为Simple 公式最适合我,除了我不同意阈值建议。根据纯绿色的结果,我尝试将阈值设置为 149,在我看来效果更好。 I made a really dumb fiddle to demonstrate this;您可以尝试更改顶部的阈值以查看原始建议。
对我来说 150 而不是 186 在背景颜色和文本颜色之间形成了很好的对比【参考方案3】:
这只是一个示例,它会在单击元素时更改 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
【讨论】:
【参考方案4】:这个(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';
【讨论】:
这在大多数情况下都有效,但有些情况像 i.imgur.com/3pOUDe5.jpg 看起来很奇怪,背景颜色实际上是 rgb(6, 247, 241); 我很欣赏这是在您尚未将颜色转换为 rgb 值的情况下执行对比度计算的一种相对便宜的方法(不是那太难,只是额外的数学步骤)跨度> 【参考方案5】:这是我在 Java for 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);
【讨论】:
它真的“完美”吗?试试纯绿色背景,#00FF00。 确实,这没有针对所有颜色进行测试....但是谁会使用纯绿色背景来制作任何不是为了惹恼用户而设计的东西呢? @mwieczorek 依赖用户生成内容或随机选择颜色的人会这样做。【参考方案6】:我使用这个 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
【讨论】:
现在用纯绿色背景试试这个:#00FF00。 谢谢你!翻译成swift并在我的ios应用中使用!【参考方案7】:我不相信这段代码,因为它不是我的,但我把它留在这里供其他人在未来快速找到:
根据 Mark Ransoms 的回答,这里是简单版本的代码 sn-p:
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;
这是高级版的代码 sn-p:
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
。
【讨论】:
我使用了简单的函数,并对其进行了更多的剥离:删除了最后两个参数并重命名为isDark(bgColor)
我的用法就是 'color': isDark(color)?'white':'black'
对我来说就像一个魅力。非常感谢!虽然只是转换了语法和函数,但在 php 中做到了。
请注意,Mark 计算的 L>0.179 假设我们在白色和黑色中选择文本颜色。其他颜色选择需要重新计算此阈值,因此将任意颜色作为lightColor
和darkColor
传递是没有意义的。
我不断收到NaN
的高级示例。【参考方案8】:
根据@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';
【讨论】:
【参考方案9】:@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));
【讨论】:
【参考方案10】:LESS 有一个很好的 contrast()
函数,对我来说效果很好,请参阅 http://lesscss.org/functions/#color-operations-contrast
“选择两种颜色中的哪一种与另一种提供最大的对比。 这对于确保颜色在背景下可读性非常有用,这对于可访问性合规性也很有用。此功能的工作方式与 Compass for 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
【讨论】:
【参考方案11】:除了算术解决方案之外,还可以使用 AI 神经网络。优点是您可以根据自己的喜好和需要对其进行定制(即,明亮饱和的红色上的灰白色文本看起来不错,并且与黑色一样可读)。
这里有一个简洁的 Javascript 演示来说明这个概念。您也可以直接在演示中生成自己的 JS 公式。
https://harthur.github.io/brain/
下面是一些图表,帮助我了解around the problem。 在第一个图表中,亮度是一个常数 128,而色调和饱和度变化。在第二张图表中,饱和度是一个常数 255,而色调和明度是变化的。
【讨论】:
这是直观显示选项的绝佳方式 我在 Medium 上重新发布了我的文章,因为我的旧博客已经不存在了。 medium.com/@think_ui/… 不幸的是,由于建议的编辑队列太长,我无法编辑此答案中的链接。 嗨@RogerAttrill 我在帖子中更新了指向你的中篇文章的链接...【参考方案12】:Mark's detailed answer 效果很好。这是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])
得到白色或黑色。
【讨论】:
【参考方案13】:从十六进制到黑色或白色:
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";
【讨论】:
【参考方案14】:基于 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;
【讨论】:
【参考方案15】:这是作为 UIColor 扩展的 Mark Ransom 答案的快速版本
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
【讨论】:
【参考方案16】:基于来自链接Make foregroundcolor black or white depending on background 和此线程的不同输入,我为 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);
【讨论】:
【参考方案17】:用所有 24 位颜色进行测试怎么样?
请注意,假设阈值为 128,YIQ 方法将返回 1.9:1 的最小对比度,这不会通过 AA 和 AAA WCAG2.0 测试。
对于 W3C 方法,它将返回 4.58:1 的最小对比度,大文本通过 AA 和 AAA 测试,小文本通过 AA 测试,它不会通过所有颜色的小文本 AAA 测试。
【讨论】:
【参考方案18】:这是我自己的方法,我一直在使用,到目前为止还没有遇到问题?
const hexCode = value.charAt(0) === '#'
? value.substr(1, 6)
: value;
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);
contrastRatio >= 0.5
? 'black'
: 'white';
【讨论】:
【参考方案19】:如果您像我一样正在寻找考虑 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";
【讨论】:
登录只是为了给你一个赞成票,找不到任何考虑到这一点的人 @robertjuh 我也很少在 *** 上发帖,感谢您的努力:)【参考方案20】:这是我根据 Mark 的惊人回答编写的 Java Swing 代码:
public static Color getColorBasedOnBackground(Color background, Color darkColor, Color lightColor)
// Calculate foreground color based on background (based on https://***.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;
【讨论】:
【参考方案21】:这是 Mark Ransom 答案的 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"
【讨论】:
【参考方案22】:我正在使用tinyColor library,它也可以完成这项工作。
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(102,255,255)
这样的rgb颜色
【讨论】:
【参考方案23】:我根据@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"
【讨论】:
【参考方案24】:在使用androidx.compose.ui.graphics.Color
时,已接受的答案不知何故在 Android 上不起作用。然后我发现Android Jetpack Compose 中实际上内置了luminance()
函数,它返回[0,1] 之间的亮度值。文档说“基于 WCAG 2.0 中定义的相对亮度公式,W3C 建议 2008 年 12 月 11 日。”。
这里是official source code。
示例用法:
fun Color.generateOnColor()
: Color
return if (luminance() > 0.5f)
Color.Black.copy(alpha = .8f)
else
Color.White
【讨论】:
以上是关于如何根据背景颜色决定字体颜色为白色还是黑色?的主要内容,如果未能解决你的问题,请参考以下文章