【问题标题】:Scaling a color based on a target contrast ratio根据目标对比度缩放颜色
【发布时间】:2019-01-17 18:11:11
【问题描述】:

我正在尝试创建一个接收前景色和背景色并计算对比度的 Sass 函数。从那里(以及我坚持的部分)是,如果它满足目标对比度,它将简单地返回前景色,但如果它不满足目标对比度,它将使前景色变亮或变暗。

例如,如果提供的背景是#000,而提供的前景是#444(对比度为2.15),则此函数会将前景变亮为#757575并返回那个颜色。

除了需要反转对比度计算的部分之外,我已经完成了所有工作。我最初的想法是用它远离目标的百分比来接近它,然后简单地通过 100 减去百分比差异来变亮/变暗(取决于最初更暗的颜色)。事后看来,这种方法有点幼稚,恐怕会涉及一些更高级的数学。

这是我目前创建的 (and here is a simplified fiddle):

@function wcag-color($bg, $fg, $size: 16px, $level: "aa"){
    @if ( $level == "aa" ){
        $wcag_contrast_ratio: 4.5; //For text smaller than 18px
        @if ( $size >= 19  ){
            $wcag_contrast_ratio: 3; //For text larger than 19px
        }
    }

    @if ( $level == "aaa" ){
        $wcag_contrast_ratio: 7; //For text smaller than 18px
        @if ( $size >= 19  ){
            $wcag_contrast_ratio: 4.5; //For text larger than 19px
        }
    }

    $actual_contrast_ratio: contrast($bg, $fg); //This function returns the contrast between the two colors.

    @if ( $actual_contrast_ratio > $wcag_contrast_ratio ){
        @return $fg; //Foreground color is acceptable
    }

    //Scale the lightness of the foreground to meet requested WCAG contrast ratio
    $difference: 100 - $actual_contrast_ratio / $wcag_contrast_ratio * 100; //There is more to it than this...

    //Edit: here are a few new lines to ponder. This assumes BG is darker than FG (would need to add a condition to compare luminance of each).
    $acceptable_luminance: luminance($bg)*$wcag_contrast_ratio; //What the luminance of the FG must be to comply
    $difference: ($acceptable_luminance - luminance($fg)); //How far away the FG luminance actually is (not sure if this helps anything...)

    @return scale-color($fg, $lightness: $difference); //Unfortunately luminance is not the same as lightness.
}

请注意注释行“它比这...目标对比度是。

这几天我一直在思考这个问题,我很难过。我宁愿通过循环 1% 变亮/变暗的颜色并单独测试每种颜色的对比度来避免猜测和检查方法——这会奏效,但我确信有一个更优化的解决方案。

这是我的初始函数(对比度和亮度)的参考,非常有帮助:https://medium.com/dev-channel/using-sass-to-automatically-pick-text-colors-4ba7645d2796

注意:我没有使用 Compass 或任何其他 Sass 库。

编辑:这是一个简化的小提琴供参考:https://www.sassmeister.com/gist/445836123feb42885a0cf7f4709261ff

【问题讨论】:

    标签: css colors sass wcag


    【解决方案1】:

    因此,给定 #000000 和 #444444,您可以计算对比度(在本例中为 2.15)。数学很简单,虽然有点毛茸茸。 (参见“relative luminance”的定义。)

    现在你想倒退吗?如果你有 #000000 并且想要 4.5 的比率,从 #444444 开始,颜色应该是什么?是这样吗

    我需要颠倒我的对比公式

    是什么意思?

    这有点复杂,因为您要求解 3 个变量,即红色、绿色和蓝色分量,而且亮度公式并未平等对待红色、绿色和蓝色。它使用 21.25% 的红色、71.5% 的绿色和 7.25% 的蓝色。

    另外,亮度公式不是简单的线性公式,因此您不能只取亮度不足的百分比,然后将颜色值提高相同的百分比。

    例如,在您的情况下,比率是 2.15,但您需要它是 4.5。 2.15 比期望值 4.5 低 108%。

    但是,如果您查看原始 RGB 值 #444444 并计算出它需要为 #757575(以便获得 4.5 的比率),那么如果您将这些 RGB 值视为简单数字(并转换为十进制) ,则 #444444 (4473924) 比 #757575 (7697781) 少 72%。

    因此,您的比率少了 108%,但 RGB 值少了 72%。因此你不能做一个简单的线性方程。

    (由于#757575 给你的比率是 4.56,而不是精确的 4.5,所以这些数字并不完全准确。如果你使用 #747474,你会得到 4.49 的比率,这对于 WCAG 来说太小了一点合规性,但比 4.56 更接近 4.5。但是,#444444 比 #747474 少 71%,所以它仍然不同于 2.15 比 4.5 少 108%,所以基本概念仍然适用。) p>

    只是为了好玩,我查看了 0x11111 到 0x666666 的值,以 0x111111 递增,并计算了对比度。图表上没有足够的点,所以我在 0x111111 和 0x222222 之间添加了一个颜色,然后在 0x222222 和 0x333333 之间添加了一个颜色,等等。

    RGB     contrast  % from 4.5  % from 0x747474
    111111    1.11      305.41%       582.35%
    191919    1.19      278.15%       364.00%
    222222    1.32      240.91%       241.18%
    2a2a2a    1.46      208.22%       176.19%
    333333    1.66      171.08%       127.45%
    3b3b3b    1.87      140.64%        96.61%
    444444    2.16      108.33%        70.59%
    4c4c4c    2.45       83.67%        52.63%
    555555    2.82       59.57%        36.47%
    5d5d5d    3.19       41.07%        24.73%
    666666    3.66       22.95%        13.73%
    6e6e6e    4.12        9.22%         5.45%
    
    

    如您所见,在第 3 个数据点处相交的线然后彼此会聚。我确定其中有一个公式,因此您可以获取对比度百分比,对其执行一些(可能是对数)函数并获得更改颜色所需的百分比。

    这将是一个有趣的数学问题,我目前没有时间玩。

    2019 年 1 月 18 日更新

    我让它向后工作,但它不能处理边缘情况,例如当使深色变暗但您已经达到最大值(或浅色较浅但您达到最大值)时。但也许你可以玩它。

    测试用例

    • #ee0add 浅色(洋红色)
    • #445566为深色(深灰色)
    • 对比度2.09

    在计算颜色的“relative luminance”时,它有一个条件语句。

    if X <= 0.03928 then 
      X = X/12.92 
    else 
      X = ((X+0.055)/1.055) ^ 2.4
    

    在该条件下使用 X 之前,它被 除以 255 以标准化 0 和 1 之间的值。因此,如果您采用条件值 0.03928,然后 乘以乘以 255,你得到 10.0164。由于 RGB 值必须是整数,这意味着 10 (0x0A) 或更少的 RGB 分量将通过“if”,而任何 11 (0x0B) 或更大的分量将通过“else”。因此,在我的测试用例值中,我希望颜色部分之一为 10 (0x0A) (#EE0ADD)。

    #ee0add 的相对亮度为 0.23614683378171950172526363525113 (0.236)

    #445566 的相对亮度为 0.0868525191131797135799815832377 (0.0868)

    contrast ratio”是 (0.236 + .05) / (0.0868 + .05) = 2.09

    (您可以在https://webaim.org/resources/contrastchecker/?fcolor=EE0ADD&bcolor=445566 上验证此比率)

    如果我们想要 4.5 的比率,并且我们希望 #ee0add 不变,那么我们必须调整 #445566。这意味着您需要解决:

    4.5 = (0.236 + .05) / (XX + .05)
    

    所以第二个亮度值(XX)需要为0.01358818528482655593894747450025(0.0136)

    The original second luminance value was 0.0868525191131797135799815832377 (0.0868), so to get 0.01358818528482655593894747450025 (0.0136), we need to multiply the original by 0.15645125119651910313960717062698 (0.0136 / 0.0868 = 0.156) (or 15.6% of the original value)

    如果我们将 15.6% 应用于 R、G 和 B 的相对亮度值中的每一个,然后逆向执行上面的条件语句,就可以得到 RGB 值。

    #445566 的原始亮度

    r = 0x44 = 68 
    g = 0x55 = 85 
    b = 0x66 = 102
    
    r1 = 68  / 255 = 0.26666666666666666666666666666667
    g1 = 85  / 255 = 0.33333333333333333333333333333333
    b1 = 102 / 255 = 0.4
    
    r2 = ((.267 + .055) / 1.055)^^2.4 = 0.05780543019106721120703816752337
    g2 = ((.333 + .055) / 1.055)^^2.4 = 0.09084171118340767766490119106965
    b2 = ((.400 + .055) / 1.055)^^2.4 = 0.13286832155381791428570549818868
    
    l = 0.2126 * r2 + 0.7152 * g2 + 0.0722 * b2
      = 0.0868525191131797135799815832377
    

    向后工作,取 r2、g2 和 b2 值的 15.6%

    r2 = 0.05780543019106721120703816752337 * 15.6% = 0.00904373187934550551617004082875
    g2 = 0.09084171118340767766490119106965 * 15.6% = 0.01421229937547695322310904970549
    b2 = 0.13286832155381791428570549818868 * 15.6% = 0.02078741515147623990363062978804
    

    现在撤消 ^^2.4 和其他东西的混乱

    • 要撤消X^^2.4,你必须做相反的操作,X^^(1/2.4)X^^(0.4167)
    • 然后乘以 1.055
    • 然后减去 0.055
    • 然后乘以 255
    pow( 0.00904373187934550551617004082875, 1/2.4) = 0.14075965680504652191078668676178
    pow( 0.01421229937547695322310904970549, 1/2.4) = 0.16993264267137740728089791717873
    pow( 0.02078741515147623990363062978804, 1/2.4) = 0.19910562853770829265100914759565
    

    乘以 1.055

    0.14075965680504652191078668676178 * 1.055 = 0.14850143792932408061587995453368
    0.16993264267137740728089791717873 * 1.055 = 0.17927893801830316468134730262356
    0.19910562853770829265100914759565 * 1.055 = 0.21005643810728224874681465071341
    

    减去 0.055

    0.14850143792932408061587995453368 - 0.055 = 0.09350143792932408061587995453368
    0.17927893801830316468134730262356 - 0.055 = 0.12427893801830316468134730262356
    0.21005643810728224874681465071341 - 0.055 = 0.15505643810728224874681465071341
    

    乘以 255

    0.09350143792932408061587995453368 * 255 = 23.842866671977640557049388406088 = 24 = 0x18
    0.12427893801830316468134730262356 * 255 = 31.691129194667306993743562169008 = 32 = 0x20
    0.15505643810728224874681465071341 * 255 = 39.53939171735697343043773593192  = 40 = 0x28
    

    所以较深的颜色是#182028。可能存在一些舍入错误,但如果您检查原始前景色 #ee0add 和新颜色 #182028,您会得到 4.48 的对比度。略低于 4.5,但正如我所说,可能存在一些舍入错误。

    https://webaim.org/resources/contrastchecker/?fcolor=EE0ADD&bcolor=182028

    我尝试用#ee0add 做同样的事情,保持#445566 不变,但是当倒退到最后一步乘以 255 时,我得到大于 255 的数字,这不是有效的 RGB 分量(它们最多只能达到 0xFF)。如果我将数字停在 255,然后将其差值加到最小的颜色值上,我得到了一个不错的颜色,但比率为 5.04,超过了 4.5。如果你愿意,我也可以发布这个数学。

    【讨论】:

    • 非常感谢您提供的信息/视觉效果。是的,我昨晚在尝试修改公式时遇到了一些与您描述的相同的事情。我知道我可以通过将 L2 乘以目标对比度来隔离 L1,然后我遇到了你提到的问题,然后我需要解决红色、绿色和蓝色(以不相等的方式)。但是,由于我确实知道色相和饱和度(以及原始亮度)的 RGB,我认为这可能会有所帮助。谢谢你的图表——我实际上是在一张纸上写下数值,但这张图表的帮助更大。
    • 查看webaim.org/resources/contrastchecker 上的颜色对比度检查器。他们有一个调整颜色的变亮/变暗滑块。它不会自动将其撞到具有足够对比度的颜色,但您可以查看他们的代码以了解他们如何将颜色调整为更亮或更暗。看看webaim.org/resources/contrastchecker/contrast.js。它正在将 rgb 转换为 hsl,这听起来像你在做什么。必须有一种方法可以倒退以生成足够的颜色。
    • 哇——非常感谢您的更新!非常感谢您在这方面的数学帮助!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-08-24
    • 2013-06-25
    • 2011-10-19
    • 2012-04-23
    • 2011-10-09
    • 1970-01-01
    相关资源
    最近更新 更多