Encontrando cor “equivalente” com opacidade


93

Digamos que eu tenha uma cor de fundo com uma "fita" em outra cor sólida. Agora, quero que a fita seja parcialmente transparente para permitir que alguns detalhes se misturem, mas ainda manter a "mesma cor" sobre o fundo.

Existe uma maneira de (facilmente) determinar, para uma determinada opacidade / alfa <100% da cor da fita, quais valores RGB ele deve ter para ser idêntico à sua cor com 100% de opacidade sobre o fundo?

Aqui está uma foto. O plano de fundo é rgb(72, 28, 97)fita rgb(45, 34, 70). Quero um rgba(r, g, b, a)para a fita para que pareça idêntica a esta cor sólida.

insira a descrição da imagem aqui


1
Uma pergunta muito semelhante pode ser encontrada aqui: stackoverflow.com/questions/6672374/convert-rgb-rgba, mas a principal diferença é que essa pergunta especifica uma cor básica de branco, enquanto esta é arbitrária.
— BoltClock

Respostas:


132

A mistura de cores é apenas uma interpolação linear por canal, certo? Portanto, a matemática é muito simples. Se você tiver RGBA1 sobre RGB2, o resultado visual efetivo RGB3 será:

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

… Onde o canal alfa é de 0,0 a 1,0.

Verificação de sanidade: se o alfa for 0, RGB3 é o mesmo que RGB2? Sim. Se o alfa for 1, RGB3 é o mesmo que RGB1? Sim.

Se você bloqueou apenas a cor de fundo e a cor final, há um grande número de cores RGBA (infinitas, em espaço de ponto flutuante) que podem atender aos requisitos. Portanto, você precisa escolher a cor da barra ou o nível de opacidade desejado e descobrir o valor do outro.

Escolhendo a cor com base no alfa

Se você conhece RGB3 (a cor final desejada), RGB2 (a cor de fundo) e A1 (quanta opacidade você deseja), e está apenas procurando RGB1, então podemos reorganizar as equações da seguinte maneira:

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

Existem algumas combinações de cores que são teoricamente possíveis, mas impossíveis dada a faixa RGBA padrão. Por exemplo, se o fundo for preto puro, a cor percebida desejada é branco puro e o alfa desejado é 1%, então você precisa:

r1 = g1 = b1 = 255/0.01 = 25500

… Um branco superclaro 100 × mais brilhante do que qualquer disponível.

Escolhendo o alfa com base nas cores

Se você conhece RGB3 (a cor final desejada), RGB2 (a cor de fundo) e RGB1 (a cor que você tem e deseja variar a opacidade), e está apenas procurando A1, então podemos reorganizar o equações assim:

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

Se eles fornecerem valores diferentes, você não poderá fazer uma correspondência exata, mas pode calcular a média dos alfas para chegar o mais próximo possível. Por exemplo, não há opacidade no mundo que permita colocar o verde sobre o vermelho para ficar azul.


Não é r1, g1e b1também desconhecido?
— Eric,

@Eric não tenho certeza. Editei a resposta caso seja o nível alfa que é conhecido e as cores que não são.
— Phrogz 01 de

Atenção, usei suas três primeiras fórmulas no Photoshop para descobrir que cor seria em 0,8 alfa quando minha única outra cor de referência era o branco ... Tive que usar 1 + (1 - a) ou 1,2 para encontrar o cor certa ...
— John William Domingo

1
@Phrogz Estou usando suas equações 'Escolha a cor com base em alfa' e recebo um número negativo (-31) para o canal vermelho. Quando eu uso o negativo como o valor vermelho, obtenho o mesmo resultado como se eu tivesse usado 0 como o valor vermelho. Existe uma maneira de converter esse negativo em algo útil? Alguma pista sobre o que está acontecendo aqui?
— Nocturno

Talvez meus cálculos estejam errados, mas esta fórmula produz resultados inesperados quando RGB3 = # 163920, RGB2 = #FFFFFF e A1 = 0,92; o RGB1 calculado = rgb (2, 40, 13), mas rgba (2, 40, 13, 0,92) = # 15381F, não # 163920.
— thdoan

12

fiz MENOS mixin usando a resposta de Phrogz. você insere:

  1. como a cor deve ficar
  2. com um certo alfa
  3. em um determinado plano de fundo (o padrão é branco)

Aqui está o código:

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

}

Uso assim:

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

Obviamente, não funciona para combinações impossíveis (como mencionado por Phrogz), o que significa que apenas níveis suaves de transparência são suportados. Veja como você vai com isso.


1
@mixin bg_alpha_calc ($ desejado_colour, $ desejado_alpha, $ background_colour: branco) {$ r3: vermelho ($ desejado_colour); $ g3: verde ($ desejada_colour); $ b3: azul ($ desejada_colour); $ r2: vermelho ($ background_colour); $ g2: verde ($ background_colour); $ b2: azul ($ background_colour); // r1 = (r3 - r2 + r2 * a1) / a1 $ r1: ($ r3 - $ r2 + ($ r2 * $ desejado_alfa)) / $ desejado_alfa; $ g1: ($ g3 - $ g2 + ($ g2 * $ desejado_alfa)) / $ desejado_alfa; $ b1: ($ b3 - $ b2 + ($ b2 * $ desejado_alpha)) / $ desejado_alpha; cor de fundo: $ desejada_colour; cor de fundo: rgba ($ r1, $ g1, $ b1, $ desejado_alpha); }
— metaColin

2
Adotei e fiz um mixin de versão SCSS. Encontre a demonstração e o código aqui: codepen.io/Grienauer/pen/Grogdx
— Grienauer

@Grienauer Eu fiz exatamente o mesmo, não percebi que você já compartilhou! Minha única diferença é que eu padronizei a cor de fundo para branco
— tehlivi

7

Graças à Phrogz 's e Ephemer ' s grandes respostas, aqui é uma função SASS que automagicamente calcula a melhor cor RGBA equivalente.

Você o chama com a cor desejada e o fundo existente, e ele calcula a melhor (ou seja, mais transparente) cor RGBA equivalente que dá o resultado desejado dentro de ± 1/256 de cada componente RGB (devido a erros de arredondamento):

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

Acabei de testá-lo contra uma série de combinações (todos os temas Bootswatch) e funciona muito bem, tanto para resultados escuro sobre claro quanto claro sobre escuro.

PS: Se você precisar de precisão melhor do que ± 1/256 na cor resultante, você precisará saber que tipo de algoritmo de arredondamento os navegadores aplicam ao misturar cores rgba (não sei se isso é padronizado ou não) e adicionar um adequado condição ao existente @while, de forma que vai aumentando $alphaaté atingir a precisão desejada.


2
Para sua informação, esta função existe na Stylus como transparentify(): stylus-lang.com/docs/bifs.html#transparentifytop-bottom-alpha ... que descobri após portar manualmente para a Stylus ...
— weotch

6

Da resposta de @Phrogz:

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

Então:

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

Então:

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

Nota você pode escolher qualquer valor a1, e isso vai encontrar os valores correspondentes r1, g1e b1necessários. Por exemplo, escolher um alfa de 1 indica que você precisa de RGB1 = RGB3, mas escolher um alfa de 0 não dá solução (obviamente).


1

A solução mais prática que encontrei até agora: basta medir a cor resultante com uma ferramenta de seleção de cores e usar a cor medida para a sobreposição. Este método fornece uma combinação perfeita de cores.

Eu tentei usar mixins diferentes, mas devido a um erro de arredondamento, ainda consegui ver a diferença a olho nu


1
O OP provavelmente queria uma maneira de fazer a determinação dinamicamente, mas isso não foi especificamente declarado, e sua resposta fornece um bom hack para seleções de cor / alfa estáticas.
— Erik Knowles,

0

Para expandir a resposta de @Tobia e permitir a especificação da opacidade mais como 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);
}
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.