根据目标对比度缩放颜色

Scaling a color based on a target contrast ratio

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

例如,如果提供的背景为 #000,提供的前景为 #444(对比度为 2.15),此函数会将前景调亮为 #757575 和 return 那种颜色。

除了我需要反转对比度计算的部分,我已经完成了所有工作。我最初的想法是用它偏离目标的百分比来接近它,然后简单地 lighten/darken(取决于哪种颜色最初更暗)乘以 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.
}

请注意注释行 "There is more to it than this..." – 这是我需要反转对比度公式的地方,但我希望能使用更简单的公式,因为我已经知道目标对比度是多少。

这几天我一直在想这个问题,但我很困惑。我更愿意通过遍历 1% lightened/darkened 颜色并单独测试每种颜色的对比度来避免猜测和检查方法——这可行,但我确信有更优化的解决方案。

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

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

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

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

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

I need to reverse my contrast formula

是什么意思?

这有点复杂,因为您要求解 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%,所以基本概念仍然适用。)

为了好玩,我查看了 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。如果你愿意,我也可以 post 那门数学课。