在 javascript 中计算对比色

问题描述 投票:0回答:7

在我们当前正在开发的页面之一上,用户可以更改显示文本的背景。

我们希望自动更改前景色以保持文本的合理的对比度。

我们还希望颜色范围是任意的。例如,如果背景以 255 个增量从白色变为黑色,我们不希望看到 255 种色调的前景色。在这种情况下,可能是 2 到 4 个,只是为了保持合理的对比度。

有任何 UI/设计/色彩专家/画家可以制定公式吗?

javascript color-scheme
7个回答
37
投票

根据亮度做出黑白决定对我来说效果很好。亮度是 R、G 和 B 值的加权和,根据人类对相对亮度的感知进行调整,这在视频应用中显然很常见。亮度的官方定义随着时间的推移而发生变化,具有不同的权重;请参阅此处:http://en.wikipedia.org/wiki/Luma_(video)。我使用 Rec 得到了最好的结果。 709版本,如下面的代码。黑白阈值大约为 165 似乎不错。

function contrastingColor(color)
{
    return (luma(color) >= 165) ? '000' : 'fff';
}
function luma(color) // color can be a hx string or an array of RGB values 0-255
{
    var rgb = (typeof color === 'string') ? hexToRGBArray(color) : color;
    return (0.2126 * rgb[0]) + (0.7152 * rgb[1]) + (0.0722 * rgb[2]); // SMPTE C, Rec. 709 weightings
}
function hexToRGBArray(color)
{
    if (color.length === 3)
        color = color.charAt(0) + color.charAt(0) + color.charAt(1) + color.charAt(1) + color.charAt(2) + color.charAt(2);
    else if (color.length !== 6)
        throw('Invalid hex color: ' + color);
    var rgb = [];
    for (var i = 0; i <= 2; i++)
        rgb[i] = parseInt(color.substr(i * 2, 2), 16);
    return rgb;
}

15
投票

就纯粹的可读性而言,您希望在任何背景上使用黑白文本。所以将RGB转换为HSV,只需检查V是否为< 0.5. If so, white, if not, black.

先尝试一下,看看你是否觉得它有吸引力。

如果不这样做,那么当背景太亮或太暗时,您可能希望白色和黑色不要太鲜明。要降低此色调,请保持相同的色调和饱和度,并使用这些亮度值:

background V  foreground V
0.0-0.25      0.75
0.25-0.5      1.0
0.5-0.75      0.0
0.75-1.0      0.25

在中等颜色上,您仍然会看到黑色或白色文本,这将具有良好的可读性。在深色或浅色上,您将看到相同颜色的文本,但亮度至少相差 3/4,因此仍然可读。我希望它看起来不错:)


7
投票

尝试这个示例,它只是根据 r、g 和 b 值的平均值将其设置为黑色或白色。也适合测试!如果您发现效果不佳的情况,请替换更好的算法。

<html> 
<head> 
<script> 
function rgbstringToTriplet(rgbstring)
{
    var commadelim = rgbstring.substring(4,rgbstring.length-1);
var strings = commadelim.split(",");
var numeric = [];
for(var i=0; i<3; i++) { numeric[i] = parseInt(strings[i]); }
return numeric;
}

function adjustColour(someelement)
{
   var rgbstring = someelement.style.backgroundColor;
   var triplet = rgbstringToTriplet(rgbstring);
   var newtriplet = [];
   // black or white:
   var total = 0; for (var i=0; i<triplet.length; i++) { total += triplet[i]; } 
   if(total > (3*256/2)) {
     newtriplet = [0,0,0];
   } else {
 newtriplet = [255,255,255];
   }
   var newstring = "rgb("+newtriplet.join(",")+")";
   someelement.style.color = newstring;
   return true;
}

function randomiseBackground(someelement)
{
 var vals = [];
 for(var i=0; i<3; i++) { vals[i]=Math.floor(Math.random()*256+1); }
 someelement.style.backgroundColor="rgb("+vals.join(",")+")";
 return true;
}
</script> 
</head> 
<body onload="adjustColour(document.getElementById('mypara'));"> 
<p id="mypara" style="background-color:#2287ea">Some text</p> 
<form> 
    <input type=button onclick="var elem=document.getElementById('mypara'); randomiseBackground(elem); adjustColour(elem);" value="randomise background" /> 
</form> 
</body> 
</html>

1
投票

您可能想要改变色调,但不想改变饱和度或明度。

因此,将 RGB 值转换为 HSV,向 H 添加 180 度(当然是模 360 度),然后转换回 RGB。

转换论坛在这里


1
投票

如果你使用的是d3.js,你可以用

做一些判断

d3.hsl(d3.color("black")).l > 0.5 ? "#000" : "fff"
(简单但可能并不完美)

getLuma(r, g, b, fmt) > 165 ? "#000" : "#fff"
参见示例2

示例1

<script src="https://d3js.org/d3.v7.min.js"></script>
<script>
  const bgColorList = ["black", "purple", "#cc1515", "green", "blue", "red", "yellow", "white"]
  window.onload = () => {
    d3.select("body").append("section")
      .selectAll("div")
      .data(bgColorList)
      .enter().append("div")
      .text((bgColor, idx, arr) => {
        const div = arr[idx]
        const {h, l} = d3.hsl(d3.color(bgColor))
        const foreColor = l > 0.5 ? "#000" : "#fff"
        div.outerHTML = `<div style="width:100px;height:50px;
background-color: ${bgColor};
color:${foreColor}
">Hello world</div>`
      })
  }
</script>

但是当

l
等于0.5时(例如:黄色、蓝色),就会有争议。

示例2

<script src="https://d3js.org/d3.v7.min.js"></script>

<table>
  <tr>
    <th>601</th>
    <th>709</th>
    <th>240</th>
  </tr>
  <tr id="output"></tr>
</table>

<script>
  /**
   * https://en.wikipedia.org/wiki/Luma_%28video%29
   * @param {"601"|"709"|"240"|"145"} format
   */
  function getLuma(r, g, b, format) {
    switch (format) {
      case "601":
        return 0.2999 * r + 0.587 * g + 0.114 * b
      case "709":
        return 0.2126 * r + 0.7152 * g + 0.0722 * b
      case "145":
      case "240":
        return 0.212 * r + 0.701 * g + 0.087 * b
    }
  }

  const bgColorList = [...Array(100).keys()].map(t => d3.scaleSequential(d3.interpolateTurbo)(t / 100))
    .concat(["black", "purple", "#cc1515", "green", "blue", "red", "white", "yellow"])
  window.onload = () => {
    const tdArr = []
    d3.select("#output")
      .selectAll("td")
      .data(["601", "709", "240"])
      .enter().append("td")
      .text((fmt, _idx, _arr) => {
        const td = _arr[_idx]
        tdArr.push([td, fmt])
      })

    tdArr.forEach(([td, fmt]) => {
      d3.select(td).append("section")
        .selectAll("div")
        .data(bgColorList)
        .enter().append("div")
        .text((bgColor, idx, arr) => {
          const div = arr[idx]
          const {r, g, b} = d3.color(bgColor)
          const foreColor = getLuma(r, g, b, fmt) > 165 ? "#000" : "#fff"
          // const coefficient = [0.2126, 0.7152, 0.0722]
          // const foreColor = [r, g, b].reduce((sum, color, i) => sum + color * coefficient[i], 0) > 165 ? "#000" : "#fff"
          div.outerHTML = `<div style="width:100px;height:26px;
background-color: ${bgColor};
color:${foreColor}
">Hello</div>`
        })
    })
  }
</script>

这与enigment的答案相同


0
投票

由于 javascript 处理十进制值的方式,实现起来有点复杂。 为此,我发现让背景改变任何颜色但文本在黑色和白色之间切换要好得多。 如果你使用对比色,很多组合会很难忍受。另请注意,有些人是色盲,因此从可访问性的角度来看,这不是一个好主意。


0
投票

这是我遇到的一个非常老的问题,但我不喜欢任何答案,所以我提供我的:

要获得合适的文本颜色,您可以检查背景颜色的亮度并返回文本的黑色或白色。

请参阅下面的演示代码中的函数 GetLumColor(),这是问题答案的关键。

<!DOCTYPE html><head></head>

<body onload="Test();">

<script type="text/javascript">

//--------------------------------------------------------------------------------

function RandomNum(Num){return Math.floor(Math.random()*Num);}

//--------------------------------------------------------------------------------

// NB: accepts rgb() or rgba() color values but the alpha-channel is not taken into consideration

function GetLumColor(Rgb){

Rgb=Rgb.replace(/ /g,'').replace('rgba(','').replace('rgb(','').replace(')','').split(',');

let R=Number(Rgb[0]), G=Number(Rgb[1]), B=Number(Rgb[2]), Color='black';

let Lum=((R+G+B)*100)/765; // luminance as a percentage

if(Lum<50){Color='white';} // adjust this number to suit

return Color;}

//--------------------------------------------------------------------------------

// Tests the function above

function Test(){

var S='', Bg='', Fg='';

for(let i=0; i<40; i++){

 Bg='rgb('+RandomNum(256)+','+RandomNum(256)+','+RandomNum(256)+')';

 Fg=GetLumColor(Bg);

 S+='<div style="background:'+Bg+'; color:'+Fg+';"> Sample 123 </div>';

}

document.write(S);

}

//--------------------------------------------------------------------------------

</script></body></html>

基本用法...

some_ID.style.color = GetLumColor(some_ID.style.background);
© www.soinside.com 2019 - 2024. All rights reserved.