如何根据背景颜色决定白色或黑色的字体颜色?

时间:2010-10-15 13:55:54

标签: colors contrast

我想展示一些像这个例子alt text

的图像

填充颜色由数据库中的字段决定,颜色为十六进制(例如:ClassX - >颜色:#66FFFF)。 现在,我想用所选颜色显示填充上方的数据(如上图所示),但我需要知道颜色是暗还是浅,所以我知道这些单词应该是白色还是黑色。 有办法吗? TKS

24 个答案:

答案 0 :(得分:262)

以我的answer to a similar question为基础。

您需要将十六进制代码分成3个部分以获得单独的红色,绿色和蓝色强度。代码的每两个数字代表十六进制(base-16)表示法的值。我不会在这里详细介绍转换,它们很容易查找。

一旦获得了各种颜色的强度,就可以确定颜色的整体强度并选择相应的文字。

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

186的阈值是基于理论的,但可以调整到味道。根据以下评论,阈值150可能对您更有效。

<小时/> 编辑:上述内容很简单,效果也相当不错,而且似乎在StackOverflow上得到了很好的认可。但是,下面的一条评论表明,在某些情况下,它可能导致不遵守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指南相关联。如果您发现结果不符合您的喜好,请尝试上面更简单的公式。

答案 1 :(得分:19)

我不赞成这个代码,因为它不是我的代码,但是我将它留在这里供其他人快速找到:

根据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

答案 2 :(得分:15)

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

答案 3 :(得分:7)

以下是我在Java for Android中的解决方案:

jQuery(".selling-box-slide").click(function() {
    jQuery('.selling-box-slide').toggleClass('selling-box-clicked');
});

答案 4 :(得分:6)

除了算术解决方案之外,还可以使用AI神经网络。优点是你可以根据自己的口味和需求量身定制(即明亮的饱和红色上的灰白色文字看起来很好,就像黑色一样可读。)

这是一个简洁的Javascript演示,说明了这个概念。您还可以在演示中生成自己的JS公式。

https://harthur.github.io/brain/

下面是一些帮助我理解around the problem的图表。 在第一个图表中,亮度是常数128,而色调和饱和度变化。在第二个图表中,饱和度是常数255,而色调和亮度会变化。

In the first chart, lightness is a constant 128, while hue and saturation vary:

Saturation is a constant 255, while hue and lightness vary:

答案 5 :(得分:2)

这只是一个在单击元素时会更改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

答案 6 :(得分:2)

我使用此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

答案 7 :(得分:2)

基于@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';
}

答案 8 :(得分:2)

这是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"

答案 9 :(得分:1)

我使用的 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 颜色,如 rgb(102,255,255)

答案 10 :(得分:1)

如果像我一样,您正在寻找考虑到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";
}

答案 11 :(得分:1)

基于来自链接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);
    }
}

答案 12 :(得分:1)

这是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
} }

答案 13 :(得分:1)

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])以获得白色或黑色。

答案 14 :(得分:1)

我从来没有做过这样的事情,但是如何编写一个函数来检查每种颜色的值与Hex 7F(FF / 2)的中间颜色。如果三种颜色中的两种颜色大于7F,那么您使用的是较暗的颜色。

答案 15 :(得分:0)

这是我基于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;
}

答案 16 :(得分:0)

我根据@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"

答案 17 :(得分:0)

我制作了Python模块,以根据RGB或背景色的十六进制值https://github.com/azaitsev/foreground

选择前景色

答案 18 :(得分:0)

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

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 :(得分:0)

使用所有24位颜色进行测试怎么样?

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

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

答案 20 :(得分:0)

基于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;
}

答案 21 :(得分:0)

从十六进制到黑色或白色:

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

答案 22 :(得分:0)

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
}

答案 23 :(得分:0)

@SoBiT,我看着你的答案,看起来不错,但是它有一个小错误。你的函数hexToG和hextoB需要一个小的编辑。 substr中的最后一个数字是字符串的长度,因此在这种情况下,它应该是&#34; 2&#34;而不是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));
}