Mam kolor szesnastkowy, np. #F4F8FB
(Lub rgb(244, 248, 251)
), który chcę przekształcić w tak przezroczysty-jak to możliwe kolor rgba (gdy jest wyświetlany na białym). Ma sens? Szukam algorytmu, a przynajmniej pomysłu na algorytm, jak to zrobić.
Na przykład:
rgb( 128, 128, 255 ) --> rgba( 0, 0, 255, .5 )
rgb( 152, 177, 202 ) --> rgba( 50, 100, 150, .5 ) // can be better(lower alpha)
Pomysły?
Rozwiązanie FYI oparte na odpowiedzi 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)'
}
javascript
colors
css
rgba
Mark Kahn
źródło
źródło
Odpowiedzi:
Weź najniższy składnik koloru i zamień go na wartość alfa. Następnie przeskaluj komponenty koloru, odejmując najniższą i dzieląc przez wartość alfa.
Przykład:
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
Tak więc
rgb(152, 177, 202)
wyświetla się jakorgba(0, 62, 123, .404)
.W Photoshopie sprawdziłem, że kolory rzeczywiście pasują idealnie.
źródło
.404
), ale nie mogłem znaleźć liczb do obliczenia.0..255
do0..1
i odwracanie. Używanie1.0 - 152 / 255
też by działało. Konwersja składników koloru to po prostu skalowanie od miejsca,n..255
w0..255
którymn
znajduje się najniższy składnik.Niech r, g i b będą wartościami wejściowymi, a r ', g', b 'i a' będą wartościami wyjściowymi, wszystkie przeskalowane (na razie, ponieważ dzięki temu matematyka jest ładniejsza) między 1 a 0. Następnie, przez wzór nakładania kolorów:
Wyrazy 1 - a 'reprezentują wkład tła, a pozostałe terminy reprezentują pierwszy plan. Zrób trochę algebry:
Intuicyjnie wydaje się, że minimalna wartość koloru będzie czynnikiem ograniczającym problem, więc powiąż to z m:
Ustaw odpowiednią wartość wyjściową, m ', na zero, ponieważ chcemy zmaksymalizować przezroczystość:
Tak więc w javascript (tłumacząc od 1 do 255 po drodze):
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]; }
Zauważ, że zakładam, że 'jest tutaj nieprzezroczystością. Zmiana na przezroczystość jest trywialna - wystarczy usunąć „1 -” ze wzoru na a. Nic, na co należy zwrócić uwagę, to fakt, że nie wydaje się to dawać dokładnych wyników - stwierdzono, że krycie wynosi 0,498 dla przykładu, który podałeś powyżej (128, 128, 255). Jednak jest to bardzo blisko.
źródło
[255 * (r/255 - 1) / a + 1 * 255, ...] == [(255*r/255 - 255 * 1) / a + 255, ...] == [(r - 255) / a + 255, ...]
Spojrzałbym na konwersję RGB <-> HSL. Tj. Jasność == ilość bieli == stopień przezroczystości.
Na przykład
rgb( 128, 128, 255 )
musimy przesunąć wartości RGB do0
pierwszej o wartość maksymalną, tj. Dorgb( 0, 0, 128 )
- to byłby nasz kolor z jak najmniejszą ilością bieli. Następnie, używając wzoru na luminancję, obliczamy ilość bieli, którą musimy dodać do naszego ciemnego koloru, aby uzyskać oryginalny kolor - to byłaby nasza 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;
Mam nadzieję, że to ma sens. :)
źródło
Dla tych z Was, którzy używają SASS / SCSS, napisałem małą funkcję SCSS, dzięki czemu można łatwo korzystać z algorytmu opisanego przez @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 ); }
źródło
Właśnie opisuję pomysł na algorytm, brak pełnego rozwiązania:
Zasadniczo masz trzy numery
x
,y
,z
i szukasz trzech nowych numerówx'
,y'
,z'
i mnożnika
w przedziale [0,1] taka, że:Jest to zapisane w jednostkach, w których kanały również przyjmują wartości z zakresu [0,1]. W 8-bitowych wartościach dyskretnych wyglądałoby to mniej więcej tak:
Ponadto chcesz mieć jak największą wartość
a
. Możesz rozwiązać:Itd. W rzeczywistych wartościach ma to nieskończenie wiele rozwiązań, wystarczy podłączyć dowolną liczbę rzeczywistą
a
, ale problem polega na znalezieniu liczby, dla której błąd dyskretyzacji jest minimalny.źródło
To powinno wystarczyć:
let x = min(r,g,b) a = 1 - x/255 # Correction 1 r,g,b = ( (r,g,b) - x ) / a # Correction 2
źródło
y
i(y-x)
.255 / (y-x)
Nieprawidłowo wymusza największą liczbę do255
, tak że zawsze skończyć z jednym0
, pojedynczym255
, a następnie inny numer:0, 22, 255
,255, 0, 55
itd .../a
a wtedy pasuje do poprawnej odpowiedzi.Najlepsza odpowiedź nie zadziałała dla mnie z niskimi kolorami. Na przykład nie oblicza prawidłowego współczynnika alfa, jeśli kolor to # 80000. Technicznie powinien trafić do # ff0000 z alfa 0.5. Aby rozwiązać ten problem, musisz użyć konwersji RGB -> HSL -> RGBA. To jest pseudo kod do uzyskania poprawnych wartości:
//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” będzie zawierało wartość nowego dopasowanego koloru i użyje zmiennej „alpha” do sterowania przezroczystością.
źródło
#800000
irgba( 255, 0, 0, .5 )
nigdzie nie są w tym samym kolorze. Pierwsza byłaby ciemnoczerwona, druga łososiowa. Chyba że wyświetlają się na czarno, wtedy myślę, że będą takie same.