寻找具有不透明度的“等效”颜色

105

假设我有一个带有“绶带”的背景颜色,绶带的颜色是另一种纯色。现在,我想让绶带部分透明,以便让一些细节融入其中,但仍然保持绶带在背景上的“相同颜色”。

有没有一种方法(简单地)确定给定不透明度/alpha < 100%时,需要使用哪些RGB值才能使绶带与在背景下100%不透明时的颜色相同?

这里有一张图片。背景为rgb(72, 28, 97),绶带rgb(45, 34, 70)。我想要一个rgba(r, g, b, a),使其外观与这种纯色相同。

enter image description here


1
这里可以找到一个非常类似的问题:https://dev59.com/rmw15IYBdhLWcg3wWqV0,但关键区别在于那个问题指定了一个基础颜色为白色,而这个问题是任意的。 - BoltClock
7个回答

144

颜色混合只是每个通道的线性插值,对吧?因此,数学计算非常简单。如果您具有RGBA1覆盖RGB2,则有效的可视结果RGB3将为:

r3 = r2 + (r1-r2)*a1
g3 = g2 + (g1-g2)*a1
b3 = b2 + (b1-b2)*a1

……其中alpha通道的取值范围是0.0到1.0。

合理性检查:如果alpha为0,RGB3是否与RGB2相同?是的。如果alpha为1,RGB3是否与RGB1相同?是的。

如果你只锁定了背景颜色和最终颜色,那么会有大量的RGBA颜色(在浮点空间中是无限的)可以满足要求。因此,你必须选择条形的颜色或所需的不透明度水平,并找出另一个值。

基于Alpha选择颜色

如果你知道RGB3(最终期望的颜色),RGB2(背景颜色)和A1(你想要的不透明度水平),并且你只是想找到RGB1,那么我们可以重新排列方程:

r1 = (r3 - r2 + r2*a1)/a1
g1 = (g3 - g2 + g2*a1)/a1
b1 = (b3 - b2 + b2*a1)/a1

有一些颜色组合在理论上是可能的,但在标准RGBA范围内是不可能的。例如,如果背景是纯黑色,所期望的颜色是纯白色,期望的透明度为1%,那么您需要:

r1 = g1 = b1 = 255/0.01 = 25500

…一个超亮的白色,比任何可用的颜色都要亮100倍。

根据颜色选择 Alpha 值

如果您知道 RGB3(最终期望的颜色)、RGB2(背景色)和 RGB1(您想要改变不透明度的颜色),并且您只是在寻找 A1,则我们可以重排方程:

a1 = (r3-r2) / (r1-r2)
a1 = (g3-g2) / (g1-g2)
a1 = (b3-b2) / (b1-b2)

如果这些值不同,那么你无法完全匹配它们,但你可以平均透明度来尽量接近。例如,在世界上没有任何不透明度可以让你将绿色放在红色上来得到蓝色。


r1g1b1也不是未知的吗? - Eric
@Eric 我不确定。如果已知 alpha 级别而颜色未知,则我已编辑答案。 - Phrogz
提醒一下,我在Photoshop中使用了你的前三个公式来找到当我的唯一参考颜色是白色时,0.8 alpha的颜色会是什么...我不得不使用1 +(1-a)或1.2才能找到正确的颜色... - John William Domingo
1
@Phrogz 我正在使用您的“基于Alpha选择颜色”的方程,红色通道得到了一个负数(-31)。当我将该负数用作红色值时,得到的结果与如果我将0用作红色值相同。有没有办法将这个负数转换为可用的东西?对此发生的事情有任何线索吗? - Nocturno
也许我的计算有误,但当RGB3 =#163920,RGB2 =#FFFFFF和A1 = 0.92时,此公式会产生意外的结果; 计算出的RGB1 = rgb(2,40,13),但rgba(2,40,13,0.92)=#15381F,而不是#163920。 - thdoan

14

我使用Phrogz的答案创建了一个LESS mixin。你需要输入:

  1. 颜色应该如何看
  2. 有一定的透明度
  3. 在给定的背景上(默认为白色)

这是代码:

.bg_alpha_calc (@desired_colour, @desired_alpha, @background_colour: white) {
    @r3: red(@desired_colour);
    @g3: green(@desired_colour);
    @b3: blue(@desired_colour);

    @r2: red(@background_colour);
    @g2: green(@background_colour);
    @b2: blue(@background_colour);

    // r1 = (r3 - r2 + r2 * a1) / a1
    @r1: ( @r3 - @r2 + (@r2 * @desired_alpha) ) / @desired_alpha;
    @g1: ( @g3 - @g2 + (@g2 * @desired_alpha) ) / @desired_alpha;
    @b1: ( @b3 - @b2 + (@b2 * @desired_alpha) ) / @desired_alpha;

    background-color: @desired_colour;
    background-color: rgba(@r1, @g1, @b1, @desired_alpha);

}

使用方法如下:

@mycolour: #abc;
@another_colour: blue;
.box_overlay {
  // example:
  .bg_alpha_calc (@mycolour, 0.97, @another_colour);
  // or (for white bg) just:
  .bg_alpha_calc (@mycolour, 0.97);
}

很明显,这对于不可能的组合是不起作用的(如Phrogz所提到的),这意味着只支持轻度透明度。看看你能否使用它。


2
@mixin bg_alpha_calc ($desired_colour, $desired_alpha, $background_colour: white) { $r3: red($desired_colour); $g3: green($desired_colour); $b3: blue($desired_colour); $r2: red($background_colour); $g2: green($background_colour); $b2: blue($background_colour); // r1 = (r3 - r2 + r2 * a1) / a1 $r1: ( $r3 - $r2 + ($r2 * $desired_alpha) ) / $desired_alpha; $g1: ( $g3 - $g2 + ($g2 * $desired_alpha) ) / $desired_alpha; $b1: ( $b3 - $b2 + ($b2 * $desired_alpha) ) / $desired_alpha; background-color: $desired_colour; background-color: rgba($r1, $g1, $b1, $desired_alpha); } - metaColin
2
我采用并制作了一个 SCSS 版本的 mixin。 在此处查看演示和代码:https://codepen.io/Grienauer/pen/Grogdx - Grienauer
@Grienauer 我也是这么做的,没意识到你已经分享了!我的唯一区别是将背景颜色默认为白色。 - tehlivi

9

感谢< strong>Phrogz < /strong>和< strong>ephemer< /strong>提供的好答案,这里有一个SASS函数,可以< strong>自动幻化< /strong>计算出最佳等效的RGBA颜色。

您可以使用所需的颜色和现有的背景来调用它,并且它将计算出在每个RGB组件±1/256内(由于四舍五入误差)给出所需结果的最佳(即最透明)等效RGBA颜色:

@function alphaize($desired-color, $background-color) {

    $r1: red($background-color);
    $g1: green($background-color);
    $b1: blue($background-color);

    $r2: red($desired-color);
    $g2: green($desired-color);
    $b2: blue($desired-color);

    $alpha: 0;
    $r: -1;
    $g: -1;
    $b: -1;

    @while $alpha < 1 and ($r < 0 or $g < 0 or $b < 0
                           or $r > 255 or $g > 255 or $b > 255) {
        $alpha: $alpha + 1/256;
        $inv: 1 / $alpha;
        $r: $r2 * $inv + $r1 * (1 - $inv);
        $g: $g2 * $inv + $g1 * (1 - $inv);
        $b: $b2 * $inv + $b1 * (1 - $inv);
    }

    @return rgba($r, $g, $b, $alpha);
}

我刚刚对各种组合(所有的Bootswatch主题)进行了测试,它完美地工作,无论是暗色背景上的浅色字体还是浅色背景上的深色字体。

附注:如果您需要在结果颜色中获得比±1/256更好的精度,则需要了解浏览器在混合rgba颜色时应用的四舍五入算法(我不知道是否标准化),并向现有的@while添加适当的条件,以便它保持增加$alpha直到达到所需的精度。


3
FYI,这个函数在 Stylus 中称为 transparentify():http://stylus-lang.com/docs/bifs.html#transparentifytop-bottom-alpha。在我手动将其移植到 Stylus 后发现了这一点。 - weotch

6

来自@Phrogz的回答:

r3 = r2 + (r1-r2)*a1
g3 = g2 + (g1-g2)*a1
b3 = b2 + (b1-b2)*a1

所以:
r3 - r2 = (r1-r2)*a1
g3 - g2 = (g1-g2)*a1
b3 - b2 = (b1-b2)*a1

所以:
r1 = (r3 - r2) / a1 + r2
g1 = (g3 - g2) / a1 + g2
b1 = (b3 - b2) / a1 + b2

请注意,您可以选择任何值的a1,并找到所需的r1g1b1的相应值。例如,选择alpha值为1告诉您需要RGB1 = RGB3,但选择alpha值为0则无解(显然)。

2
到目前为止,我找到的最实用的解决方案是:使用取色器工具测量结果颜色,然后将测量到的颜色用于叠加。这种方法可以完美匹配颜色。
我尝试使用不同的Mixin,但由于四舍五入误差,我仍然能够用肉眼看到差异。

2
OP可能想要一种动态确定的方法,但这并没有明确说明,您的答案确实为静态颜色/透明度选择提供了一个好的技巧。 - Erik Knowles

0

为了扩展@Tobia的答案并允许更像transparentify一样指定不透明度:

@function rgbaMorph($desired-color, $background-color: rgb(255,255,255), $desired-alpha: 0) {

    $r1: red($desired-color);
    $g1: green($desired-color);
    $b1: blue($desired-color);

    $r2: red($background-color);
    $g2: green($background-color);
    $b2: blue($background-color);

    $r: -1;
    $g: -1;
    $b: -1;

    @if ($desired-alpha != 0) {
        $r: ( $r1 - $r2 + ($r2 * $desired-alpha) ) / $desired-alpha;
        $g: ( $g1 - $g2 + ($g2 * $desired-alpha) ) / $desired-alpha;
        $b: ( $b1 - $b2 + ($b2 * $desired-alpha) ) / $desired-alpha;
    }

    @if (($desired-alpha == 0) or ($r < 0 or $g < 0 or $b < 0
                           or $r > 255 or $g > 255 or $b > 255)) {
        //if alpha not attainable, this will find lowest alpha that is

        $alpha: $desired-alpha;
        @while $alpha < 1 and ($r < 0 or $g < 0 or $b < 0
                           or $r > 255 or $g > 255 or $b > 255) {
            $alpha: $alpha + 1/256;
            $inv: 1 / $alpha;
            $r: $r1 * $inv + $r2 * (1 - $inv);
            $g: $g1 * $inv + $g2 * (1 - $inv);
            $b: $b1 * $inv + $b2 * (1 - $inv);
        }
        @debug "Color not attainable at opacity using alpha: " $alpha " instead of: " $desired-alpha;

        $desired-alpha: $alpha;
    }

    @return rgba($r, $g, $b, $desired-alpha);
}

0

我写了一个工具,可以从JSS中使用它来帮助我使颜色透明。它使用了material-ui的实用方法,可以在这里找到。
这是我的实用类代码(使用TypeScript编写)。请注意,有些颜色无法保持其颜色不变而同时使其透明。
抱歉我没有提供codepen或可运行的代码。我希望有人能发现这个工具并从中获得一些用处!

import {
  decomposeColor,
  recomposeColor,
  ColorObject,
} from '@material-ui/core/styles/colorManipulator';

/**
 * Take a non transparent color and create a transparent color that looks identical visually.
 * Using formula from this SO post https://dev59.com/92ct5IYBdhLWcg3wQ7My
 * Assuming a white background
 * @param origColor The color you want to match.
 * @param value Transparency value between 0 and 1. Cannot be too low because it is mathematically not possible (usually <0.2 but depends on the color)
 */
export const makeTransparent = (origColor: string, value: number): string => {
  const origColorObj: ColorObject = decomposeColor(origColor);

  if (value >= 1 || value <= 0)
    throw new Error('makeTransparent: invalid value provided');

  if (origColorObj.values[3] != null && origColorObj.values[3] !== 1)
    throw new Error('makeTransparent: origColor cannot be transparent');

  const newColorObj: ColorObject = {
    type: 'rgba',
    values: [0, 0, 0, value],
  };

  for (let i = 0; i < 3; i++) {
    const rgbNum: number = (255 * value + origColorObj.values[i] - 255) / value;

    if (rgbNum < 0 || rgbNum > 255)
      throw new Error('makeTransparent: Transparency value too low');

    newColorObj.values[i] = rgbNum;
  }

  return recomposeColor(newColorObj);
};


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接