Converti RGB in RGBA su bianco


196

Ho un colore esadecimale, ad es. #F4F8FB(O rgb(244, 248, 251)) che desidero convertire nel modo più trasparente possibile colore rgba il trasparente (se visualizzato su bianco). Ha senso? Sto cercando un algoritmo, o almeno un'idea di un algoritmo per come farlo.

Per esempio:

rgb( 128, 128, 255 ) --> rgba(   0,   0, 255,  .5 )
rgb( 152, 177, 202 ) --> rgba(  50, 100, 150,  .5 ) // can be better(lower alpha)

Idee?


Soluzione FYI basata sulla risposta di Guffa:

function RGBtoRGBA(r, g, b){
    if((g == null) && (typeof r === 'string')){
        var hex = r.replace(/^\s*#|\s*$/g, '');
        if(hex.length === 3){
            hex = hex.replace(/(.)/g, '$1$1');
        }
        r = parseInt(hex.substr(0, 2), 16);
        g = parseInt(hex.substr(2, 2), 16);
        b = parseInt(hex.substr(4, 2), 16);
    }

    var min, a = (255 - (min = Math.min(r, g, b))) / 255;

    return {
        r    : r = 0|(r - min) / a,
        g    : g = 0|(g - min) / a,
        b    : b = 0|(b - min) / a,
        a    : a = (0|1000*a)/1000,
        rgba : 'rgba(' + r + ', ' + g + ', ' + b + ', ' + a + ')'
    };
}

RGBtoRGBA(204, 153, 102) == RGBtoRGBA('#CC9966') == RGBtoRGBA('C96') == 
    {
       r    : 170,
       g    : 85 ,
       b    : 0  ,
       a    : 0.6,
       rgba : 'rgba(170, 85, 0, 0.6)' 
    }

Interseting! Vuoi ancora che sia visibile giusto, non completamente trasparente?
Mrchief,

6
Domanda interessante! C'è una domanda esattamente opposta su stackoverflow.com/questions/2049230/convert-rgba-color-to-rgb , potrebbe aiutare
apscience

@Mrchief: Sì, voglio che il colore appaia uguale quando viene visualizzato su bianco, ma che sia il più trasparente possibile.
Mark Kahn,

1
Grande domanda, mi sono sempre chiesto come farlo, ma di solito evitato;) +1
Dominic K

1
@ e100 - La domanda specifica che lo ha innescato è stata stackoverflow.com/questions/6658350/… . Fondamentalmente volevo essere in grado di sovrapporre le ombre CSS sopra gli elementi con un colore di sfondo senza bloccare il click-through mettendo un elemento ombra sopra tutto. Ho anche voluto farlo per un po 'in modo da poter fare cose casuali come: jsfiddle.net/qwySW/2 . Inoltre ho appena trovato una domanda affascinante :)
Mark Kahn,

Risposte:


185

Prendi il componente di colore più basso e convertilo in un valore alfa. Quindi ridimensionare i componenti del colore sottraendo il più basso e dividendolo per il valore alfa.

Esempio:

152 converts to an alpha value of (255 - 152) / 255 ~ 0.404

152 scales using (152 - 152) / 0.404 = 0
177 scales using (177 - 152) / 0.404 ~ 62
202 scales using (202 - 152) / 0.404 ~ 123

Quindi, rgb(152, 177, 202)visualizza come rgba(0, 62, 123, .404).

Ho verificato in Photoshop che i colori effettivamente corrispondono perfettamente.


1
Grazie! Stavo facendo qualcosa di simile (ad esempio get .404) ma non riuscivo a far funzionare i numeri.
Mark Kahn,

3
Cordiali saluti, pubblicato la soluzione finale basata sulla tua risposta in questione
Mark Kahn,

2
@templatetypedef: la conversione del componente più basso in alfa è il ridimensionamento dell'intervallo 0..255in 0..1e l'inversione. Anche l'uso 1.0 - 152 / 255avrebbe funzionato. La conversione delle componenti di colore è semplicemente scalando da n..255a 0..255dove nè il componente più basso.
Guffa,

1
@Christoph: il principio sarebbe lo stesso, ma più complicato. Invece di usare solo il componente più basso per calcolare l'alfa, dovresti calcolare l'alfa più bassa possibile per ciascun componente (cioè quale valore alfa da usare per ottenere il colore giusto quando usi 0 o 255 come valore del componente), quindi usa il valore più alto di quei valori alfa. Da ciò puoi calcolare quale valore di colore usare per ciascun componente con quel valore alfa per ottenere il colore giusto. Si noti che alcune combinazioni di colori (ad esempio bianco su sfondo nero) daranno 1,0 come valore alfa più basso possibile da utilizzare.
Guffa,

2
Ho scritto un piccolo violino che implementa questa soluzione. Ecco il link. jsfiddle.net/wb5fwLoc/1 . Forse uno di voi può usarlo. È solo uno script veloce, non privo di bug .. dovrebbe essere abbastanza buono per giocare.
Chsymann,

23

Sia r, g, e b i valori di input e r ', g', b 'e a' siano i valori di output, tutti ridimensionati (per ora, in quanto rende la matematica più carina) tra 1 e 0. Quindi, con la formula per sovrapporre i colori:

r = a' * r' + 1 - a'
g = a' * g' + 1 - a'
b = a' * b' + 1 - a'

I termini 1 - a rappresentano il contributo di fondo e gli altri termini rappresentano il primo piano. Fai un po 'di algebra:

r = a' * (r' - 1) + 1
r - 1 = a' * (r' - 1)
(r - 1) / (r' - 1) = a'
(r' - 1) / (r - 1) = 1 / a'
r' - 1 = (r - 1) / a'
r' = (r - 1) / a' + 1

Intuitivamente, sembra che il valore minimo del colore sia il fattore limitante nel problema, quindi associalo a m:

m = min(r, g, b)

Impostare il valore di output corrispondente, m ', su zero, per massimizzare la trasparenza:

0 = (m - 1) / a' + 1
-1 = (m - 1) / a'
-a' = m - 1
a' = 1 - m

Quindi, in javascript (traducendo da 1 a 255 lungo la strada):

function rgba(r, g, b) {
    var a = 1 - Math.min(r, Math.min(g, b)) / 255;
    return [255 + (r - 255) / a, 255 + (g - 255) / a, 255 + (b - 255) / a, a];
}

Si noti che sto assumendo che un 'è opacità qui. È banale cambiarlo in trasparenza - basta rimuovere "1 -" dalla formula per un '. Un'altra cosa da notare è che questo non sembra produrre risultati esatti - ha detto che l'opacità era 0,498 per l'esempio che hai dato sopra (128, 128, 255). Tuttavia, questo è estremamente vicino.


Se distribuisci i 255 moltiplicati nella tua equazione otterrai il risultato giusto -[255 * (r/255 - 1) / a + 1 * 255, ...] == [(255*r/255 - 255 * 1) / a + 255, ...] == [(r - 255) / a + 255, ...]
Gereeter,

6

Guarderei alla conversione RGB <-> HSL. Vale a dire luminosità == quantità di bianco == quantità di trasparenza.

Per il tuo esempio rgb( 128, 128, 255 ), dobbiamo spostare i valori RGB in 0primo luogo per la massima quantità, ovvero a rgb( 0, 0, 128 )- sarebbe il nostro colore con il minor numero di bianco possibile. Dopodiché, utilizzando la formula per la luminanza, calcoliamo la quantità di bianco che dobbiamo aggiungere al nostro colore scuro per ottenere il colore originale - quello sarebbe il nostro alfa:

L = (MAX(R,G,B) + MIN(R,G,B))/2
L1 = (255 + 128) / 2 = 191.5
L2 = (128 + 0) /2 = 64
A = (191,5 - 64) / 255 = 0,5;

Spero che abbia un senso. :)


6

Per quelli di voi che usano SASS / SCSS, ho scritto una piccola funzione SCSS in modo da poter usare facilmente l'algoritmo descritto da @Guffa

@function transparentize-on-white($color)
{
    $red: red($color);
    $green: green($color);
    $blue: blue($color);
    $lowestColorComponent: min($red, $green, $blue);
    $alpha: (255 - $lowestColorComponent) / 255;

    @return rgba(
        ($red - $lowestColorComponent) / $alpha,
        ($green - $lowestColorComponent) / $alpha,
        ($blue - $lowestColorComponent) / $alpha,
        $alpha
    );
}

3

Sto solo descrivendo un'idea per l'algoritmo, nessuna soluzione completa:

In sostanza, si dispone di tre numeri x, y, ze siete alla ricerca di tre nuovi numeri x', y', z'e un moltiplicatore anel range [0,1] tale che:

x = a + (1 - a) x'
y = a + (1 - a) y'
z = a + (1 - a) z'

Questo è scritto in unità in cui i canali assumono anche valori nell'intervallo [0,1]. Con valori discreti a 8 bit, sarebbe qualcosa del genere:

x = 255 a + (1 - a) x'
y = 255 a + (1 - a) y'
z = 255 a + (1 - a) z'

Inoltre, vuoi il massimo valore possibile a. Puoi risolvere:

a  = (x - x')/(255 - x')          x' = (x - 255 a)/(1 - a)

Ecc. In valori reali questo ha infinite soluzioni, basta inserire un numero reale a, ma il problema è trovare un numero per il quale l'errore di discretizzazione è minimo.


0

Questo dovrebbe farlo:

let x = min(r,g,b)
a = 1 - x/255                    # Correction 1
r,g,b = ( (r,g,b) - x ) / a      # Correction 2

In realtà ho appena notato che avevo un errore nel calcolo alfa (risolto ora). Quindi potrebbe aver avuto qualcosa a che fare anche con questo.
verità

C'è ancora un problema introdotto dal tuo ye (y-x). La 255 / (y-x)costringe in modo errato il numero più grande di 255, in modo che ci si finisce sempre con un singolo 0, un singolo 255e poi un altro numero: 0, 22, 255, 255, 0, 55, ecc ...
Mark Kahn

Vedo, quindi non consentirà colori più scuri ... Dovrei solo farlo /ae quindi corrisponde alla risposta corretta.
verità

-1

La risposta migliore non ha funzionato per me con componenti a basso colore. Ad esempio, non calcola l'alfa corretta se il colore è # 80000. Tecnicamente dovrebbe trasformarlo in # ff0000 con alfa 0,5. Per risolvere questo problema, è necessario utilizzare la conversione RGB -> HSL -> RGBA. Questo è pseudo codice per ottenere i valori corretti:

//Convert RGB to HSL
hsl = new HSL(rgb)

//Use lightness as alpha
alpha = hsl.Lightness

//For #80000 lightness is 0.5, so we have to take this into account.
//Lightness more than 0.5 converts to alpha 1 and below is a linear ratio
if (alpha > 0.5)
{
    alpha = 1;
}
else
{
    alpha = alpha / 0.5;
    //We need to drop the lightness of the color to 0.5 to get the actual color
    //that needs to display. Alpha blending will take care of that.
    hsl.Lightness = 0.5;
}

newRgb = hsl.convertToRgb()

"newRgb" conterrà il valore del nuovo colore modificato e utilizzerà la variabile "alpha" per controllare la trasparenza.


#800000e rgba( 255, 0, 0, .5 )non sono vicini allo stesso colore. Il primo sarebbe rosso scuro, il secondo salmone. A meno che non vengano visualizzati sul nero, penso che sarebbero gli stessi.
Mark Kahn,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.