给定一个系统(例如一个网站),允许用户自定义某些部分的背景色,但不允许自定义字体颜色(以保持选项的数量最小化),是否有一种方法可以通过编程来确定“浅色”或“深色”字体颜色是必要的?

我相信有一些算法,但我对颜色、光度等了解不够,无法自己找出答案。


当前回答

iOS Swift 3.0 (UIColor扩展):

func isLight() -> Bool
{
    if let components = self.cgColor.components, let firstComponentValue = components[0], let secondComponentValue = components[1], let thirdComponentValue = components[2] {
        let firstComponent = (firstComponentValue * 299)
        let secondComponent = (secondComponentValue * 587)
        let thirdComponent = (thirdComponentValue * 114)
        let brightness = (firstComponent + secondComponent + thirdComponent) / 1000

        if brightness < 0.5
        {
            return false
        }else{
            return true
        }
    }  

    print("Unable to grab components and determine brightness")
    return nil
}

其他回答

基于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()。

丑陋的Python,如果你不想写它:)

'''
Input a string without hash sign of RGB hex digits to compute
complementary contrasting color such as for fonts
'''
def contrasting_text_color(hex_str):
    (r, g, b) = (hex_str[:2], hex_str[2:4], hex_str[4:])
    return '000' if 1 - (int(r, 16) * 0.299 + int(g, 16) * 0.587 + int(b, 16) * 0.114) / 255 < 0.5 else 'fff'

我本想对@MichaelChirico的回答发表评论,但我没有足够的声誉。这里有一个在R中返回颜色的例子:

get_text_colour <- function(
    background_colour,
    light_text_colour = 'white',
    dark_text_colour = 'black',
    threshold = 0.5
) {

    background_luminance <- c( 
        c( .299, .587, .114 ) %*% col2rgb( background_colour ) / 255
    )

    return(
        ifelse(
            background_luminance < threshold,
            light_text_colour,
            dark_text_colour
        )
    )
}
> get_text_colour( background_colour = 'blue' )
[1] "white"

> get_text_colour( background_colour = c( 'blue', 'yellow', 'pink' ) )
[1] "white" "black" "black"

> get_text_colour( background_colour = c('black', 'white', '#236FAB', 'darkred', '#01F11F') )
[1] "white" "black" "white" "white" "black"

谢谢@Gacek。下面是一个Android版本:

@ColorInt
public static int getContrastColor(@ColorInt int color) {
    // Counting the perceptive luminance - human eye favors green color...
    double a = 1 - (0.299 * Color.red(color) + 0.587 * Color.green(color) + 0.114 * Color.blue(color)) / 255;

    int d;
    if (a < 0.5) {
        d = 0; // bright colors - black font
    } else {
        d = 255; // dark colors - white font
    }

    return Color.rgb(d, d, d);
}

还有一个改进的(更短的)版本:

@ColorInt
public static int getContrastColor(@ColorInt int color) {
    // Counting the perceptive luminance - human eye favors green color...
    double a = 1 - (0.299 * Color.red(color) + 0.587 * Color.green(color) + 0.114 * Color.blue(color)) / 255;
    return a < 0.5 ? Color.BLACK : Color.WHITE;
}

基于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中产生零低对比度错误。