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

Posted

技术标签:

【中文标题】寻找具有不透明度的“等效”颜色【英文标题】:Finding "equivalent" color with opacity 【发布时间】:2012-08-27 01:13:23 【问题描述】:

假设我有一个背景颜色,上面有一条“丝带”,上面是另一种纯色。现在,我希望色带部分透明以让一些细节融合在一起,但仍保持色带在背景上的“相同颜色”。

有没有办法(轻松)确定,对于给定的不透明度/alpha

这是一张照片。背景是rgb(72, 28, 97),丝带rgb(45, 34, 70)。我想要一个rgba(r, g, b, a) 用于丝带,使它看起来与这种纯色相同。

【问题讨论】:

可以在此处找到一个非常相似的问题:***.com/questions/6672374/convert-rgb-rgba,但主要区别在于该问题指定了白色的基色,而这个问题是任意的。 【参考方案1】:

颜色混合只是每个通道的线性插值,对吗?所以数学很简单。如果 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 范围内是不可能的。例如,如果背景是纯黑色,所需的感知颜色是纯白色,所需的 alpha 为 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)

如果这些给出不同的值,那么你不能让它完全匹配,但你可以平均 alpha 以尽可能接近。例如,世界上没有不透明的东西可以让你把绿色放在红色上得到蓝色。

【讨论】:

r1g1b1 不也是未知数吗? @Eric 我不确定。我已经编辑了答案,以防它是已知的 alpha 级别和不知道的颜色。 请注意,我在 Photoshop 中使用了您的前三个公式来找出当我唯一的其他参考颜色是白色时,0.8 alpha 时的颜色...我必须使用 1 + (1 - a ) 或 1.2 以找到正确的颜色... @Phrogz 我正在使用您的“基于 Alpha 选择颜色”方程,我得到红色通道的负数 (-31)。当我使用负数作为红色值时,我得到的结果与使用 0 作为红色值相同。有没有办法将这种负面转化为可用的东西?关于这里发生了什么的任何线索? 也许我的计算是错误的,但是当 RGB3 = #163920、RGB2 = #FFFFFF 和 A1 = 0.92 时,这个公式会产生意想不到的结果;计算得到的 RGB1 = rgb(2, 40, 13),但是 rgba(2, 40, 13, 0.92) = #15381F,而不是 #163920。【参考方案2】:

我使用 Phrogz 的回答做了一个 LESS 混合。你输入:

    颜色的外观 具有一定的阿尔法 在给定的背景上(默认为白色)

代码如下:

.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 所述),这意味着仅支持适度的透明度。看看你如何使用它。

【讨论】:

@mixin bg_alpha_calc ($desired_colour, $desired_alpha, $background_colour: white) $r3: red($desired_colour); $g3:绿色($desired_colour); $b3: 蓝色($desired_colour); $r2: 红色($background_colour); $g2:绿色($background_colour); $b2: 蓝色($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;背景颜色:$desired_colour;背景颜色:rgba($r1, $g1, $b1, $desired_alpha); 我采用并制作了一个 SCSS 版本的 mixin。在这里找到演示和代码:codepen.io/Grienauer/pen/Grogdx @Grienauer 我也做了同样的事情,没想到你已经分享了!我唯一的区别是我将背景颜色默认为白色【参考方案3】:

感谢 Phrogzephemer 的出色回答,这是一个 自动 计算最佳等效 RGBA 颜色的 SASS 函数。

您使用所需颜色和现有背景调用它,它会计算最佳(即最透明)等效 RGBA 颜色,在每个 RGB 分量的 ±1/256 范围内给出所需结果(由于舍入误差):

@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 主题)对它进行了测试,它对暗对光和亮对暗结果都很有效。

PS:如果您需要的结果颜色精度优于 ±1/256,则需要知道在混合 rgba 颜色时浏览器应用哪种舍入算法(我不知道这是否标准化)和为现有的@while添加一个合适的条件,使其不断增加$alpha,直到达到所需的精度。

【讨论】:

仅供参考,此功能在 Stylus 中以 transparentify(): stylus-lang.com/docs/bifs.html#transparentifytop-bottom-alpha ... 我在手动将其移植到 Stylus 后发现...【参考方案4】:

来自@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 的对应值。例如,选择 1 的 alpha 告诉您需要 RGB1 = RGB3,但选择 0 的 alpha 没有解决方案(显然)。

【讨论】:

【参考方案5】:

到目前为止我发现的最实用的解决方案:只需使用颜色选择器工具测量生成的颜色,然后使用测量的颜色进行叠加。这种方法提供了完美的颜色匹配。

我尝试过使用不同的 mixin,但由于舍入误差,我仍然能够用肉眼看到差异

【讨论】:

OP 可能想要一种动态做出决定的方法,但没有具体说明,您的回答确实为静态颜色/alpha 选择提供了一个很好的技巧。【参考方案6】:

扩展@Tobia 的答案并允许指定不透明度更像透明化:

@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);

【讨论】:

【参考方案7】:

我写了一个工具,我可以从 JSS 中使用它来帮助我使颜色透明。它使用来自 material-ui 的实用方法,可以在 here 找到。 这是我的实用程序类的代码(在 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://***.com/questions/12228548/finding-equivalent-color-with-opacity
 * 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);
;

【讨论】:

以上是关于寻找具有不透明度的“等效”颜色的主要内容,如果未能解决你的问题,请参考以下文章

CSS RGBA - 具有不透明度的颜色定义

灰度到黑色不透明度的转换

具有相同颜色(不透明度)和不同字体大小的两个文本块出现不同

Swift UIView 背景颜色不透明度

具有透明度的模型

使用透明背景、ttk 框架背景颜色配置 tkinter/ttk 小部件?