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

vicvicvic
fonte
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.

Phrogz
fonte
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.

efêmero
fonte
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.

Tobia
fonte
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).

Eric
fonte
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

executeinstaller
fonte
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);
}
Phazei
fonte