【问题标题】:How to programmatically calculate the contrast ratio between two colors?如何以编程方式计算两种颜色之间的对比度?
【发布时间】:2012-04-01 18:16:17
【问题描述】:

非常直接,以黄色和白色为例:

back_color = {r:255,g:255,b:255}; //white
text_color = {r:255,g:255,b:0}; //yellow

上帝的地球上的什么物理定律的普遍常数,使得黄色文本不能在白色背景上阅读,而蓝色文本可以?

为了我的可定制小部件,我尝试了所有可能的颜色模型,我找到了转换函数;两者都不能说绿色可以在白色上,黄色不能,仅基于数字比较。

我查看了 Adsense(它是由所有互联网的佛陀创建的)并猜测他们做了什么,他们进行了预设和颜色单元距离计算。我不能那样做。我的用户有权选择即使是最刺激视网膜、最不美观的组合,只要文本仍然可以阅读。

【问题讨论】:

  • 所以您的用户可以选择任意两种颜色,只要它们具有明显的对比效果?
  • 从技术角度来看,我觉得这很有趣,但更实际的是,如果您的用户有“权利”选择任何颜色,您为什么还要关心它是否可以阅读?难道不是由他们来做对吗?
  • @nnnnnn 我真的不在乎他们选择什么颜色,他们可以随意混合,但我关心的是 (c) 2012 Company Inc. 的可读性。
  • 我设置了一个 jsfiddle 来亲眼看看答案有多准确,而且他们确实能够很好地预测可读性:jsfiddle.net/UVUZC
  • 如果有人错过了 ShaoKahn 的回复,this was the link

标签: javascript validation colors


【解决方案1】:

根据维基百科,当转换为亮度的灰度表示时,“必须获得其红色、绿色和蓝色的值”并将它们按以下比例混合:R:30% G:59% B:11%

因此,白色的亮度为 100%,黄色的亮度为 89%。同时,绿色也只有 59%。 11% 几乎是 41% 差异的四倍!

即使是石灰 (#00ff00) 也不适合阅读大量文本。

恕我直言,良好对比色的亮度应至少相差 50%。并且这个亮度应该被转换成灰度来测量。

upd:最近在网上找到了一个comprehensive tool 按顺序使用来自w3 document 的公式 阈值可以取自#1.4 这是这个更高级的东西的实现。

function luminance(r, g, b) {
    var a = [r, g, b].map(function (v) {
        v /= 255;
        return v <= 0.03928
            ? v / 12.92
            : Math.pow( (v + 0.055) / 1.055, 2.4 );
    });
    return a[0] * 0.2126 + a[1] * 0.7152 + a[2] * 0.0722;
}
function contrast(rgb1, rgb2) {
    var lum1 = luminance(rgb1[0], rgb1[1], rgb1[2]);
    var lum2 = luminance(rgb2[0], rgb2[1], rgb2[2]);
    var brightest = Math.max(lum1, lum2);
    var darkest = Math.min(lum1, lum2);
    return (brightest + 0.05)
         / (darkest + 0.05);
}
contrast([255, 255, 255], [255, 255, 0]); // 1.074 for yellow
contrast([255, 255, 255], [0, 0, 255]); // 8.592 for blue
// minimal recommended contrast ratio is 4.5, or 3 for larger font-sizes

有关更多信息,请查看WCAG 2.0 documentation,了解如何计算此值。

【讨论】:

  • +1 没错。在现实生活中 - 转换为灰度以检查设计的可读性是必须做的(特别是对于徽标)。
  • 我会选择这个答案作为解决方案,因为它是最全面的,我不能选择两个解决方案;但我会选择 HondaSuzukiShaolinShaorma 的答案,因为它提供了现成的代码。
  • contrast([255, 255, 255], [0, 0, 255]) 返回 8.592,相同的数字反转 contrast([0, 0, 255], [255, 255, 255]) 返回 0.116,即 1/8.592。要获得 1 到 21 之间的对比度,如果输出 function contrast(rgb1, rgb2) { var result = (luminanace(rgb1[0], rgb1[1], rgb1[2]) + 0.05) / (luminanace(rgb2[0], rgb2[1], rgb2[2]) + 0.05); if (result < 1) result = 1/result; return result; }
  • 这里有个小错误:return (luminanace(rgb1[0], rgb1[1], rgb1[2]) + 0.05) / (luminanace(rgb2[0], rgb2[1], rgb2[2]) + 0.05); 。您必须将较大的值除以较小的值。最好使用这个:l1=luminanace(rgb1[0], rgb1[1], rgb1[2]) + 0.05; l2=luminanace(rgb2[0], rgb2[1], rgb2[2]) + 0.05; return (Math.max(l1,l2) / Math.min(l1,l2));
  • @zuluk,你完全正确。已编辑。 w3.org/TR/WCAG20-TECHS/G17.html#G17-procedure
【解决方案2】:

计算对比度的方法有很多种,但常用的方法是这个公式:

brightness = (299*R + 587*G + 114*B) / 1000

您对两种颜色都进行此操作,然后获取差异。这显然给白底蓝比白底黄提供了更大的对比度。

【讨论】:

  • 仅供参考,显然这是来自 w3c trendct.org/2016/01/22/… w3.org/TR/AERT#color-contrast
  • 仅供参考,上面显示的系数 (299*R + 587*G + 114*B) 与 NTSC 相关,对于 sRGB 的网页颜色已过时/不正确。正确的 sRGB 系数为 0.2126*R + 0.7152*G + 0.0722*B(请注意,对于 sRGB,必须首先对 R G B 分量进行线性化)。
【解决方案3】:

const getLuminanace = (values) => {
  const rgb = values.map((v) => {
    const val = v / 255;
    return val <= 0.03928 ? val / 12.92 : ((val + 0.055) / 1.055) ** 2.4;
  });
  return Number((0.2126 * rgb[0] + 0.7152 * rgb[1] + 0.0722 * rgb[2]).toFixed(3));
};

const getContrastRatio = (colorA, colorB) => {
  const lumA = getLuminanace(colorA);
  const lumB = getLuminanace(colorB);

  return (Math.max(lumA, lumB) + 0.05) / (Math.min(lumA, lumB) + 0.05);
};

// usage:
const back_color = [255,255,255]; //white
const text_color = [255,255,0]; //yellow

getContrastRatio(back_color, text_color); // 1.0736196319018405

【讨论】:

    【解决方案4】:

    基于 kirilloid 的回答:

    通过传入十六进制值来计算对比度和发光度的 Angular 服务:

    .service('ColorContrast', [function() {
    var self = this;
    
    /**
     * Return iluminance value (base for getting the contrast)
     */
    self.calculateIlluminance = function(hexColor) {
        return calculateIluminance(hexColor);
    };
    
    /**
     * Calculate contrast value to white
     */
    self.contrastToWhite = function(hexColor){
        var whiteIlluminance = 1;
        var illuminance = calculateIlluminance(hexColor);
        return whiteIlluminance / illuminance;
    };
    
    /**
    * Bool if there is enough contrast to white
    */
    self.isContrastOkToWhite = function(hexColor){
        return self.contrastToWhite(hexColor) > 4.5;
    };
    
    /**
     * Convert HEX color to RGB
     */
    var hex2Rgb = function(hex) {
        var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
        return result ? {
            r: parseInt(result[1], 16),
            g: parseInt(result[2], 16),
            b: parseInt(result[3], 16)
        } : null;
    };
    
    /**
     * Calculate iluminance
     */
    var calculateIlluminance = function(hexColor) {
        var rgbColor = hex2Rgb(hexColor);
        var r = rgbColor.r, g = rgbColor.g, b = rgbColor.b;
        var a = [r, g, b].map(function(v) {
            v /= 255;
            return (v <= 0.03928) ?
                v / 12.92 :
                Math.pow(((v + 0.055) / 1.055), 2.4);
        });
        return a[0] * 0.2126 + a[1] * 0.7152 + a[2] * 0.0722;
    };
    
    }]);
    

    【讨论】:

      【解决方案5】:

      最近我在这个页面上看到了答案,我使用代码为 Adob​​e Illustrator 制作了一个脚本来计算对比度。

      这里可以看到结果:http://screencast.com/t/utT481Ut

      上面脚本的一些速记符号让我感到困惑,并且在 Adob​​e 扩展脚本中不起作用。因此,我认为很高兴分享我对 kirilloid 共享的代码的改进/解释。

      function luminance(r, g, b) {
          var colorArray = [r, g, b];
          var colorFactor;
          var i;
          for (i = 0; i < colorArray.length; i++) {
              colorFactor = colorArray[i] / 255;
              if (colorFactor <= 0.03928) {
                  colorFactor = colorFactor / 12.92;
              } else {
                  colorFactor = Math.pow(((colorFactor + 0.055) / 1.055), 2.4);
              }
              colorArray[i] = colorFactor;
          }
          return (colorArray[0] * 0.2126 + colorArray[1] * 0.7152 + colorArray[2] * 0.0722) + 0.05;
      }
      

      当然你需要调用这个函数

      在 for 循环中,我从我的 illustrator 对象中获取所有颜色

      //just a snippet here to demonstrate the notation
      var selection = app.activeDocument.selection;
      for (i = 0; i < selection.length; i++) {
         red[i] = selection[i].fillColor.red;
         //I left out the rest,because it would become to long
      }
      
      //this can then be used to calculate the contrast ratio.
      var foreGround = luminance(red[0], green[0], blue[0]);
      var background = luminance(red[1], green[1], blue[1]);
      luminanceValue = foreGround / background;
      luminanceValue = round(luminanceValue, 2);
      
      //for rounding the numbers I use this function:
      function round(number, decimals) {
         return +(Math.round(number + "e+" + decimals) + "e-" + decimals);
      }
      

      更多对比度信息:http://webaim.org/resources/contrastchecker/

      【讨论】:

        【解决方案6】:
        module.exports = function colorcontrast (hex) {
            var color = {};
        
            color.contrast = function(rgb) {
                // check if we are receiving an element or element background-color
                if (rgb instanceof jQuery) {
                    // get element background-color
                    rgb = rgb.css('background-color');
                } else if (typeof rgb !== 'string') {
                    return;
                }
        
                // Strip everything except the integers eg. "rgb(" and ")" and " "
                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';
            };
        
            // Return public interface
            return color;
        
        };
        

        【讨论】:

        • 这看起来不错,但为什么你的colorcontrast 方法接受hex 参数,但没有使用该参数?
        猜你喜欢
        • 1970-01-01
        • 2023-03-14
        • 1970-01-01
        • 2013-08-02
        • 2010-09-29
        • 1970-01-01
        • 2014-12-15
        • 1970-01-01
        相关资源
        最近更新 更多