Encontrar un color "equivalente" con opacidad

92

Digamos que tengo un color de fondo con una "cinta" que lo recorre en otro color sólido. Ahora, quiero que la cinta sea parcialmente transparente para permitir que algunos detalles se mezclen, pero aún así mantener la cinta del "mismo color" sobre el fondo.

¿Hay alguna manera de determinar (fácilmente), para una opacidad dada / alfa <100% del color de la cinta, qué valores RGB debería tener para ser idéntico a su color con un 100% de opacidad sobre el fondo?

Aquí tienes una foto. El fondo es rgb(72, 28, 97), cinta rgb(45, 34, 70). Quiero una rgba(r, g, b, a)para la cinta para que parezca idéntica a este color sólido.

ingrese la descripción de la imagen aquí

vicvicvic
fuente
1
Se puede encontrar una pregunta muy similar aquí: stackoverflow.com/questions/6672374/convert-rgb-rgba pero la diferencia clave es que esa pregunta especifica un color base de blanco, mientras que esta es arbitraria.
BoltClock

Respuestas:

129

La combinación de colores es solo una interpolación lineal por canal, ¿verdad? Entonces las matemáticas son bastante simples. Si tiene RGBA1 sobre RGB2, el resultado visual efectivo RGB3 será:

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

… Donde el canal alfa es de 0.0 a 1.0.

Comprobación de cordura: si el alfa es 0, ¿RGB3 es lo mismo que RGB2? Si. Si el alfa es 1, ¿RGB3 es lo mismo que RGB1? Si.

Si bloqueó solo el color de fondo y el color final, hay una gran cantidad de colores RGBA (infinitos, en el espacio de punto flotante) que podrían satisfacer los requisitos. Por lo tanto, debe elegir el color de la barra o el nivel de opacidad que desee y averiguar el valor de la otra.

Elegir el color basado en Alpha

Si conoce RGB3 (el color final deseado), RGB2 (el color de fondo) y A1 (cuánta opacidad desea), y solo está buscando RGB1, entonces podemos reorganizar las ecuaciones de la siguiente manera:

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

Hay algunas combinaciones de colores que son teóricamente posibles, pero imposibles dado el rango estándar de RGBA. Por ejemplo, si el fondo es negro puro, el color percibido deseado es blanco puro y el alfa deseado es 1%, entonces necesitaría:

r1 = g1 = b1 = 255/0.01 = 25500

… Un blanco superbrillante 100 veces más brillante que cualquier otro disponible.

Elegir el Alfa en función de los colores

Si conoce RGB3 (el color final deseado), RGB2 (el color de fondo) y RGB1 (el color que tiene del que desea variar la opacidad), y solo está buscando A1, entonces podemos reorganizar el ecuaciones así:

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

Si estos dan valores diferentes, entonces no puede hacer que coincida exactamente, pero puede promediar los valores alfa para acercarse lo más posible. Por ejemplo, no hay opacidad en el mundo que le permita poner verde sobre rojo para obtener azul.

Phrogz
fuente
No son r1, g1y b1también se desconoce?
Eric
@Eric no estoy seguro. He editado la respuesta en caso de que sea el nivel alfa el que se conoce y los colores que no.
Phrogz
Aviso, usé sus primeras tres fórmulas en Photoshop para encontrar qué color sería en 0.8 alfa cuando mi único otro color de referencia era el blanco ... Tuve que usar 1 + (1 - a) o 1.2 para encontrar el color correcto ...
John William Domingo
1
@Phrogz Estoy usando sus ecuaciones 'Elija el color basado en alfa', y obtengo un número negativo (-31) para el canal rojo. Cuando uso el negativo como valor rojo, obtengo el mismo resultado que si hubiera usado 0 como valor rojo. ¿Hay alguna forma de convertir este negativo en algo utilizable? ¿Alguna pista de lo que está pasando aquí?
Nocturno
Tal vez mis cálculos estén mal, pero esta fórmula produce un resultado inesperado cuando RGB3 = # 163920, RGB2 = #FFFFFF y A1 = 0.92; el RGB1 calculado = rgb (2, 40, 13), pero rgba (2, 40, 13, 0.92) = # 15381F, no # 163920.
jueves
12

Hice un mixin MENOS usando la respuesta de Phrogz. usted ingresa:

  1. cómo debe verse el color
  2. con un cierto alfa
  3. sobre un fondo dado (por defecto es blanco)

Aquí está el 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 así:

@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, no funciona para combinaciones imposibles (como lo menciona Phrogz), eso significa que solo se admiten niveles leves de transparencia. Vea cómo le va.

efímero
fuente
1
@mixin bg_alpha_calc ($ color_deseado, $ color_deseado, $ color_de_fondo: blanco) {$ r3: rojo ($ color_deseado); $ g3: verde ($ color_deseado); $ b3: azul ($ color_deseado); $ r2: rojo ($ color_de_fondo); $ g2: verde ($ color_de_fondo); $ b2: azul ($ color_de_fondo); // r1 = (r3 - r2 + r2 * a1) / a1 $ r1: ($ r3 - $ r2 + ($ r2 * $ deseada_alfa)) / $ deseada_alfa; $ g1: ($ g3 - $ g2 + ($ g2 * $ deseado_alpha)) / $ deseado_alpha; $ b1: ($ b3 - $ b2 + ($ b2 * $ deseada_alfa)) / $ deseada_alfa; color de fondo: $ color_deseado; color de fondo: rgba ($ r1, $ g1, $ b1, $ deseado_alpha); }
metaColin
2
Adopté e hice una mezcla de versión SCSS. Encuentre la demostración y el código aquí: codepen.io/Grienauer/pen/Grogdx
Grienauer
@Grienauer hice exactamente lo mismo, ¡no me di cuenta de que ya lo compartiste! Mi única diferencia es que establecí el color de fondo por defecto en blanco
tehlivi
7

Gracias a Phrogz 's y Ephemer ' s grandes respuestas, aquí es una función que SASS automagicamente calcula el mejor color RGBA equivalente.

Lo llama con el color deseado y el fondo existente, y calculará el mejor color RGBA equivalente (es decir, el más transparente) que dé el resultado deseado dentro de ± 1/256 de cada componente RGB (debido a errores de redondeo):

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

Lo probé con una serie de combinaciones (todos los temas de Bootswatch) y funciona de maravilla, tanto para resultados de oscuridad sobre luz como de luz sobre oscuridad.

PD: Si necesita una precisión superior a ± 1/256 en el color resultante, necesitará saber qué tipo de algoritmo de redondeo aplican los navegadores al mezclar colores rgba (no sé si eso está estandarizado o no) y agregar un condición a la existente @while, de manera que siga aumentando $alphahasta lograr la precisión deseada.

Tobia
fuente
2
FYI, esta función existe en Stylus como transparentify(): stylus-lang.com/docs/bifs.html#transparentifytop-bottom-alpha ... que descubrí después de portar esto manualmente a Stylus ...
weotch
6

De la respuesta de @Phrogz:

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

Entonces:

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

Entonces:

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

Nota usted puede elegir cualquier valor de a1, y esto va a encontrar los valores correspondientes de r1, g1y b1requeridos. Por ejemplo, elegir un alfa de 1 te dice que necesitas RGB1 = RGB3, pero elegir un alfa de 0 no da ninguna solución (obviamente).

Eric
fuente
1

La solución más práctica que encontré hasta ahora: simplemente mida el color resultante con una herramienta de selección de color y luego use el color medido para la superposición. Este método proporciona una combinación de colores perfecta.

Intenté usar diferentes mezclas, pero debido a un error de redondeo, aún pude ver la diferencia a simple vista.

ejecutar instalador
fuente
1
El OP probablemente quería una forma de tomar la determinación de forma dinámica, pero eso no se indicó específicamente, y su respuesta da un buen truco para las selecciones estáticas de color / alfa.
Erik Knowles
0

Para expandir la respuesta de @ Tobia y permitir especificar la opacidad más como transparente:

@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
fuente