Chuyển đổi RGB sang RGBA qua màu trắng


196

Tôi có một màu hex, ví dụ #F4F8FB(hoặc rgb(244, 248, 251)) mà tôi muốn chuyển đổi thành màu rgba càng trong suốt càng tốt (khi được hiển thị trên màu trắng). Có lý? Tôi đang tìm kiếm một thuật toán, hoặc ít nhất là ý tưởng về một thuật toán để làm thế nào để làm điều đó.

Ví dụ:

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

Ý tưởng?


Giải pháp FYI dựa trên câu trả lời của 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)' 
    }

Giao nhau! Bạn vẫn muốn nó được hiển thị ngay, không hoàn toàn minh bạch?
Ông trùm

6
Câu hỏi thú vị! Có một câu hỏi ngược lại chính xác tại stackoverflow.com/questions/2049230/convert-rgba-color-to-rgb , nó có thể giúp
apscience

@Mrchief - Có, tôi muốn màu trông giống nhau khi hiển thị trên màu trắng, nhưng càng trong suốt càng tốt.
Mark Kahn

1
Câu hỏi tuyệt vời, tôi đã luôn tự hỏi làm thế nào để làm điều này, nhưng thường tránh xa nó;) +1
Dominic K

1
@ e100 - Câu hỏi cụ thể đã kích hoạt điều này là stackoverflow.com/questions/6658350/ . Về cơ bản, tôi muốn có thể phủ bóng CSS lên trên các phần tử bằng màu nền mà không chặn nhấp qua bằng cách đặt phần tử bóng lên trên tất cả mọi thứ. Tôi cũng đã muốn làm điều này trong một thời gian để tôi có thể làm những việc ngẫu nhiên như: jsfiddle.net/qwySW/2 . Thêm vào đó tôi chỉ thấy đó là một câu hỏi hấp dẫn :)
Mark Kahn

Câu trả lời:


185

Lấy thành phần màu thấp nhất và chuyển đổi thành giá trị alpha. Sau đó chia tỷ lệ các thành phần màu bằng cách trừ giá trị thấp nhất và chia cho giá trị alpha.

Thí dụ:

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

Vì vậy, rgb(152, 177, 202)hiển thị như rgba(0, 62, 123, .404).

Tôi đã xác minh trong Photoshop rằng màu sắc thực sự phù hợp hoàn hảo.


1
Cảm ơn bạn! Tôi đã làm một cái gì đó tương tự (ví dụ get .404) nhưng không thể hoàn thành các con số.
Mark Kahn

3
FYI, đã đăng giải pháp cuối cùng dựa trên câu trả lời của bạn trong câu hỏi
Mark Kahn

2
@templatetypedef: Chuyển đổi thành phần thấp nhất thành alpha đang mở rộng từ phạm vi 0..255sang 0..1và đảo ngược. Sử dụng 1.0 - 152 / 255cũng sẽ làm việc. Chuyển đổi các thành phần màu sắc chỉ đơn giản là mở rộng quy mô từ n..255đến 0..255nơi nlà thành phần thấp nhất.
Guffa

1
@Christoph: Nguyên tắc sẽ giống nhau, nhưng phức tạp hơn. Thay vì chỉ sử dụng thành phần thấp nhất để tính toán alpha, bạn sẽ tính toán alpha thấp nhất có thể cho từng thành phần (nghĩa là sử dụng giá trị alpha nào để có màu đúng khi sử dụng 0 hoặc 255 làm giá trị thành phần), sau đó sử dụng giá trị cao nhất của các giá trị alpha đó. Từ đó bạn có thể tính toán giá trị màu nào sẽ sử dụng cho từng thành phần với giá trị alpha đó để có được màu phù hợp. Lưu ý rằng một số kết hợp màu (ví dụ: màu trắng trên nền đen) sẽ cho 1.0 là giá trị alpha thấp nhất có thể sử dụng.
Guffa

2
Tôi đã viết một câu đố nhỏ thực hiện giải pháp này. Đây là liên kết. jsfiddle.net/wb5fwLoc/1 . Có lẽ một trong số bạn có thể sử dụng nó. Nó chỉ là một kịch bản nhanh, không có lỗi .. nó đủ tốt để chơi xung quanh.
chsymann

23

Đặt r, g và b là các giá trị đầu vào và r ', g', b 'và a' là các giá trị đầu ra, tất cả được chia tỷ lệ (hiện tại, vì nó làm cho toán học đẹp hơn) trong khoảng từ 1 đến 0. Sau đó, bởi công thức để phủ màu:

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

Các điều khoản 1 - a 'đại diện cho đóng góp nền và các điều khoản khác đại diện cho tiền cảnh. Làm một số đại số:

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

Theo trực giác, có vẻ như giá trị màu tối thiểu sẽ là yếu tố hạn chế trong vấn đề, vì vậy hãy liên kết điều này với m:

m = min(r, g, b)

Đặt giá trị đầu ra tương ứng, m ', bằng 0, vì chúng tôi muốn tối đa hóa độ trong suốt:

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

Vì vậy, trong javascript (dịch từ 1 đến 255 trên đường đi):

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];
}

Lưu ý rằng tôi giả sử rằng 'là độ mờ ở đây. Việc thay đổi nó thành minh bạch là chuyện nhỏ - chỉ cần xóa "1 -" khỏi công thức cho '. Một điều thú vị cần lưu ý là điều này dường như không tạo ra kết quả chính xác - nó nói rằng độ mờ là 0,498 cho ví dụ bạn đã đưa ra ở trên (128, 128, 255). Tuy nhiên, điều này là vô cùng gần gũi.


Nếu bạn phân phối 255 nhân trong phương trình của bạn, bạn sẽ nhận được kết quả đúng -[255 * (r/255 - 1) / a + 1 * 255, ...] == [(255*r/255 - 255 * 1) / a + 255, ...] == [(r - 255) / a + 255, ...]
gereeter

6

Tôi muốn chuyển sang RGB <-> HSL. Tức là độ sáng == lượng trắng == lượng trong suốt.

Ví dụ của bạn rgb( 128, 128, 255 ), chúng ta cần thay đổi giá trị RGB thành 0số lượng tối đa trước tiên, nghĩa là rgb( 0, 0, 128 )- đó sẽ là màu của chúng tôi với càng ít màu trắng càng tốt. Và sau đó, bằng cách sử dụng công thức cho độ chói, chúng tôi tính toán lượng màu trắng cần thêm vào màu tối để có được màu gốc - đó sẽ là alpha của chúng tôi:

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;

Hy vọng rằng có ý nghĩa. :)


6

Đối với những người sử dụng SASS / SCSS, tôi đã viết một hàm SCSS nhỏ để bạn có thể dễ dàng sử dụng thuật toán được mô tả bởi @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

Tôi chỉ mô tả một ý tưởng cho thuật toán, không có giải pháp đầy đủ:

Về cơ bản, bạn có ba số x, y, zvà bạn đang tìm kiếm ba con số mới x', y', z'và một số nhân atrong khoảng [0,1] sao cho:

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

Điều này được viết bằng các đơn vị trong đó các kênh cũng lấy các giá trị trong phạm vi [0,1]. Trong các giá trị rời rạc 8 bit, nó sẽ giống như thế này:

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

Hơn nữa, bạn muốn giá trị lớn nhất có thể a. Bạn có thể giải quyết:

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

V.v. Trong các giá trị thực, điều này có vô số giải pháp, chỉ cần cắm vào bất kỳ số thực nào a, nhưng vấn đề là tìm một số mà lỗi phân tách là tối thiểu.


0

Điều này nên làm điều đó:

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

Tôi thực sự chỉ nhận thấy rằng tôi đã có một lỗi trong alpha calc (đã được sửa bây giờ). Vì vậy, điều đó có thể có một cái gì đó để làm với nó quá.
thật

Vẫn còn một vấn đề được giới thiệu bởi bạn y(y-x). Các 255 / (y-x)sai buộc số lượng lớn nhất tới 255, vì vậy bạn luôn luôn muốn kết thúc với một single 0, một đĩa đơn 255và sau đó một số khác: 0, 22, 255, 255, 0, 55, vv ...
Đánh dấu Kahn

Tôi hiểu rồi, vì vậy nó sẽ không cho phép màu tối hơn ... Tôi chỉ nên làm nó /avà sau đó nó phù hợp với câu trả lời đúng.
thật

-1

Câu trả lời hàng đầu không làm việc cho tôi với các thành phần màu thấp. Ví dụ, nó không tính toán alpha chính xác nếu màu là # 80000. Về mặt kỹ thuật, nó sẽ biến nó thành # ff0000 với alpha 0.5. Để giải quyết vấn đề này, bạn cần sử dụng chuyển đổi RGB -> HSL -> RGBA. Đây là mã giả để có được các giá trị chính xác:

//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" sẽ chứa giá trị của màu được điều chỉnh mới và sử dụng biến "alpha" để kiểm soát độ trong suốt.


#800000rgba( 255, 0, 0, .5 )không nơi nào có cùng màu. Đầu tiên sẽ có màu đỏ sẫm, cá hồi thứ hai. Trừ khi hiển thị trên màu đen, thì tôi nghĩ chúng sẽ giống nhau.
Mark Kahn
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.