给定一个系统(例如一个网站),允许用户自定义某些部分的背景色,但不允许自定义字体颜色(以保持选项的数量最小化),是否有一种方法可以通过编程来确定“浅色”或“深色”字体颜色是必要的?
我相信有一些算法,但我对颜色、光度等了解不够,无法自己找出答案。
给定一个系统(例如一个网站),允许用户自定义某些部分的背景色,但不允许自定义字体颜色(以保持选项的数量最小化),是否有一种方法可以通过编程来确定“浅色”或“深色”字体颜色是必要的?
我相信有一些算法,但我对颜色、光度等了解不够,无法自己找出答案。
当前回答
基于Gacek的回答,在用WAVE浏览器扩展分析了@WebSeed的例子后,我提出了以下版本,它根据对比度(在W3C的Web内容可访问性指南(WCAG) 2.1中定义)而不是亮度来选择黑色或白色文本。
这是代码(javascript):
// As defined in WCAG 2.1
var relativeLuminance = function (R8bit, G8bit, B8bit) {
var RsRGB = R8bit / 255.0;
var GsRGB = G8bit / 255.0;
var BsRGB = B8bit / 255.0;
var R = (RsRGB <= 0.03928) ? RsRGB / 12.92 : Math.pow((RsRGB + 0.055) / 1.055, 2.4);
var G = (GsRGB <= 0.03928) ? GsRGB / 12.92 : Math.pow((GsRGB + 0.055) / 1.055, 2.4);
var B = (BsRGB <= 0.03928) ? BsRGB / 12.92 : Math.pow((BsRGB + 0.055) / 1.055, 2.4);
return 0.2126 * R + 0.7152 * G + 0.0722 * B;
};
var blackContrast = function(r, g, b) {
var L = relativeLuminance(r, g, b);
return (L + 0.05) / 0.05;
};
var whiteContrast = function(r, g, b) {
var L = relativeLuminance(r, g, b);
return 1.05 / (L + 0.05);
};
// If both options satisfy AAA criterion (at least 7:1 contrast), use preference
// else, use higher contrast (white breaks tie)
var chooseFGcolor = function(r, g, b, prefer = 'white') {
var Cb = blackContrast(r, g, b);
var Cw = whiteContrast(r, g, b);
if(Cb >= 7.0 && Cw >= 7.0) return prefer;
else return (Cb > Cw) ? 'black' : 'white';
};
在我的@WebSeed的代码依赖的分支中可以找到一个工作示例,它在WAVE中产生零低对比度错误。
其他回答
作为Kotlin / Android扩展:
fun Int.getContrastColor(): Int {
// Counting the perceptive luminance - human eye favors green color...
val a = 1 - (0.299 * Color.red(this) + 0.587 * Color.green(this) + 0.114 * Color.blue(this)) / 255
return if (a < 0.5) Color.BLACK else Color.WHITE
}
如果你是为了视觉效果而操纵色彩空间,通常使用HSL(色相、饱和度和明度)比RGB更容易。在RGB中移动颜色以获得自然的令人愉悦的效果在概念上是相当困难的,而转换为HSL,在那里进行操作,然后再转换回来在概念上更直观,并且总是会给出更好的外观结果。
维基百科对HSL和密切相关的HSV有很好的介绍。网络上有免费的代码可以进行转换(例如这里是一个javascript实现)
你使用什么精确的转换是一个品味问题,但我个人认为颠倒色调和明度组件肯定会产生一个良好的高对比度的颜色作为第一个近似,但你可以很容易地追求更微妙的效果。
基于Gacek的回答,在用WAVE浏览器扩展分析了@WebSeed的例子后,我提出了以下版本,它根据对比度(在W3C的Web内容可访问性指南(WCAG) 2.1中定义)而不是亮度来选择黑色或白色文本。
这是代码(javascript):
// As defined in WCAG 2.1
var relativeLuminance = function (R8bit, G8bit, B8bit) {
var RsRGB = R8bit / 255.0;
var GsRGB = G8bit / 255.0;
var BsRGB = B8bit / 255.0;
var R = (RsRGB <= 0.03928) ? RsRGB / 12.92 : Math.pow((RsRGB + 0.055) / 1.055, 2.4);
var G = (GsRGB <= 0.03928) ? GsRGB / 12.92 : Math.pow((GsRGB + 0.055) / 1.055, 2.4);
var B = (BsRGB <= 0.03928) ? BsRGB / 12.92 : Math.pow((BsRGB + 0.055) / 1.055, 2.4);
return 0.2126 * R + 0.7152 * G + 0.0722 * B;
};
var blackContrast = function(r, g, b) {
var L = relativeLuminance(r, g, b);
return (L + 0.05) / 0.05;
};
var whiteContrast = function(r, g, b) {
var L = relativeLuminance(r, g, b);
return 1.05 / (L + 0.05);
};
// If both options satisfy AAA criterion (at least 7:1 contrast), use preference
// else, use higher contrast (white breaks tie)
var chooseFGcolor = function(r, g, b, prefer = 'white') {
var Cb = blackContrast(r, g, b);
var Cw = whiteContrast(r, g, b);
if(Cb >= 7.0 && Cw >= 7.0) return prefer;
else return (Cb > Cw) ? 'black' : 'white';
};
在我的@WebSeed的代码依赖的分支中可以找到一个工作示例,它在WAVE中产生零低对比度错误。
您可以在任何色相背景上有任何色相文本,并确保它是易读的。我一直都这么做。在Javascript中有一个关于可读的彩色文本的公式- STW* 正如它在那个链接上所说的那样,这个公式是逆伽马调整计算的变化,尽管IMHO更易于管理。 该链接右侧的菜单及其相关页面使用随机生成的颜色作为文本和背景,始终清晰可辨。所以,是的,显然这是可以做到的,没有问题。
基于Gacek的答案,但直接返回颜色常数(其他修改见下文):
public Color ContrastColor(Color iColor)
{
// Calculate the perceptive luminance (aka luma) - human eye favors green color...
double luma = ((0.299 * iColor.R) + (0.587 * iColor.G) + (0.114 * iColor.B)) / 255;
// Return black for bright colors, white for dark colors
return luma > 0.5 ? Color.Black : Color.White;
}
注意:我去掉了亮度值的反转,使明亮的颜色有一个更高的值,这对我来说似乎更自然,也是“默认”的计算方法。 (编辑:这在原来的答案中也被采用了)
我使用了与Gacek相同的常数,因为它们非常适合我。
你也可以使用下面的签名来实现这个扩展方法:
public static Color ContrastColor(this Color iColor)
然后,您可以轻松地将其称为via foregroundColor = backgroundColor.ContrastColor()。