Làm thế nào để kiểm tra xem màu hex có "quá đen" không?


111

Tôi đang cố gắng đánh giá độ tối của một màu được chọn bởi một bộ chọn màu để xem nó có "quá đen" hay không, và nếu có, hãy đặt nó thành màu trắng. Tôi nghĩ rằng tôi có thể sử dụng các ký tự đầu tiên của giá trị hex để thực hiện điều này. Nó đang hoạt động, nhưng nó cũng đang chuyển một số màu "sáng" hợp pháp.

Tôi có mã làm việc này:

        if (lightcolor.substring(0,3) == "#00"|| lightcolor.substring(0,3) == "#010"){
            lightcolor="#FFFFFF";
            color=lightcolor;
        }

Phải có một cách hiệu quả hơn với phép toán hex để biết rằng một màu đã vượt ra ngoài một mức độ tối nhất định? Giống như if lightcolor + "some hex value" <= "some hex value" rồi đặt nó thành màu trắng.

Tôi đã thêm tinyColor, có thể được sử dụng cho việc này, nhưng tôi không biết chắc.


1
Bạn đã thử lấy một bảng chọn màu và kiểm tra các giá trị chưa? Tôi nhận thấy rằng khi R, G và B đều dưới ~ 70 thì trời tối. Đây có thể không phải là cách thích hợp, nhưng đó là một cách.
Rick Kuipers

1
Khi bạn đã sử dụng tinyColor, hãy chuyển màu sang HSL và xem thành phần L. 1 = màu trắng, 0 = đen
Andreas

4
@Andreas HSL nhẹ không tính đến nhận thức của con người. Giá trị L là 0,5 sẽ có độ sáng cảm nhận khác nhau đối với các màu sắc khác nhau.
Alnitak

1
@Alnitak Bạn nói đúng nhưng mô tả của TO không chính xác như vậy. Vì vậy, bất kỳ giá trị nào dưới 3/8 có thể đã đủ tối cho mục đích của anh ta.
Andreas

1
@Andreas điều đó phụ thuộc - nếu bạn nhìn vào các giá trị độ sáng ITU trong câu trả lời của tôi, bạn sẽ thấy rằng màu xanh lam chỉ được coi là 1/10 sáng như màu xanh lá cây.
Alnitak

Câu trả lời:


226

Bạn phải trích xuất ba thành phần RGB riêng lẻ, sau đó sử dụng công thức tiêu chuẩn để chuyển đổi các giá trị RGB thu được thành độ sáng cảm nhận của chúng.

Giả sử một màu sáu ký tự:

var c = c.substring(1);      // strip #
var rgb = parseInt(c, 16);   // convert rrggbb to decimal
var r = (rgb >> 16) & 0xff;  // extract red
var g = (rgb >>  8) & 0xff;  // extract green
var b = (rgb >>  0) & 0xff;  // extract blue

var luma = 0.2126 * r + 0.7152 * g + 0.0722 * b; // per ITU-R BT.709

if (luma < 40) {
    // pick a different colour
}

BIÊN TẬP

Kể từ tháng 5 năm 2014 tinycolorhiện đã có một getBrightness()chức năng, mặc dù sử dụng hệ số trọng số CCIR601 thay vì hệ số trọng số ITU-R ở trên.

BIÊN TẬP

Phạm vi giá trị luma kết quả là 0..255, trong đó 0 là tối nhất và 255 là sáng nhất. Các giá trị lớn hơn 128 được coi là nhẹ tinycolor. (sao chép một cách đáng xấu hổ từ các bình luận của @ pau.moreno và @Alnitak)


11
đã lâu không thấy một số thao tác bit tốt trong javascript. những thứ hay ho. vi.wikipedia.org/wiki/Rec._709#Luma_coefficients
jbabey

2
Mã tốt, nhưng sau khi thử nghiệm, tôi đề xuất var luma = (r + g + b) / 3; if (luma <128) {// sẽ hữu ích hơn. }
Terry Lin

2
@TerryLin Tại sao? Các hệ số được đưa ra là các giá trị ITU tiêu chuẩn cho phép thực tế rằng màu xanh lá cây được cảm nhận sáng hơn màu đỏ (và sau đó là màu xanh lam).
Alnitak

1
lumaPhạm vi giá trị kết quả là 0..255, trong đó 0là tối nhất và 255sáng nhất (ba hệ số cộng lại thành một).
pau.moreno

1
@gabssnake chỉ kể từ tháng 5 năm 2014 và isDark()ngưỡng được mã hóa cứng là 128
Alnitak

21

Các TinyColor thư viện (bạn đã đề cập đến nó) cung cấp một số chức năng để kiểm tra và thao tác màu sắc, trong đó có:


15

Tôi đã tìm thấy hàm WooCommerce Wordpress PHP này ( wc_hex_is_light ) và tôi đã chuyển đổi sang JavaScript. Hoạt động tốt!

function wc_hex_is_light(color) {
    const hex = color.replace('#', '');
    const c_r = parseInt(hex.substr(0, 2), 16);
    const c_g = parseInt(hex.substr(2, 2), 16);
    const c_b = parseInt(hex.substr(4, 2), 16);
    const brightness = ((c_r * 299) + (c_g * 587) + (c_b * 114)) / 1000;
    return brightness > 155;
}

1
Tuyệt vời, thx! Tôi đã thử nghiệm nó với nhiều màu sắc, phát hiện là đúng với tất cả trong số họ :)
David Dal BUSCO

colorIsDarkOrLight (color) {var hex = color.replace ("#", ""); var c_r, c_g, c_b, Bright = ""; if (hex.length == 3) {c_r = parseInt (hex.substr (0, 2), 16); c_g = parseInt (hex.substr (1, 2), 16); c_b = parseInt (hex.substr (2, 2), 16); độ sáng = (c_r * 299 + c_g * 587 + c_b * 114) / 1000; } else {c_r = parseInt (hex.substr (0, 2), 16); c_g = parseInt (hex.substr (2, 2), 16); c_b = parseInt (hex.substr (4, 2), 16); } trả về độ sáng> 155; },
Pedro Henrique

Sử dụng với hex 3 đặc tính và 6
Pedro Henrique

6

Bạn có thể tính toán độ sáng :

Do đó, độ chói là một chỉ báo về mức độ sáng của bề mặt.

Vì vậy, thật tuyệt khi chọn văn bản nên có màu trắng hoặc đen.

var getRGB = function(b){
    var a;
    if(b&&b.constructor==Array&&b.length==3)return b;
    if(a=/rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(b))return[parseInt(a[1]),parseInt(a[2]),parseInt(a[3])];
    if(a=/rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(b))return[parseFloat(a[1])*2.55,parseFloat(a[2])*2.55,parseFloat(a[3])*2.55];
    if(a=/#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(b))return[parseInt(a[1],16),parseInt(a[2],16),parseInt(a[3],
16)];
    if(a=/#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(b))return[parseInt(a[1]+a[1],16),parseInt(a[2]+a[2],16),parseInt(a[3]+a[3],16)];
    return (typeof (colors) != "undefined")?colors[jQuery.trim(b).toLowerCase()]:null
};

var luminance_get = function(color) {
    var rgb = getRGB(color);
    if (!rgb) return null;
        return 0.2126 * rgb[0] + 0.7152 * rgb[1] + 0.0722 * rgb[2];
}

Phương pháp trên cho phép bạn chuyển màu ở các định dạng khác nhau, nhưng về cơ bản thuật toán chỉ ở trong luminance_get.

Khi tôi sử dụng nó, tôi đã đặt màu thành đen nếu độ sáng lớn hơn 180, nếu không thì màu trắng.


5

Có một sự khác biệt quan trọng ở đây giữa độ chói và độ sáng. Độ chói, vào cuối ngày, là thước đo lượng năng lượng truyền qua một khu vực nhất định và hoàn toàn bỏ qua cách hệ thống tri giác của chúng ta cảm nhận năng lượng đó. Mặt khác, độ sáng là thước đo cách chúng ta cảm nhận năng lượng đó và tính đến mối quan hệ giữa độ chói và hệ thống tri giác của chúng ta. (Như một điểm gây nhầm lẫn, có một thuật ngữ gọi là độ sáng tương đối, có vẻ như được sử dụng đồng nghĩa với thuật ngữ độ sáng. Nó khiến tôi trở nên tốt).

Nói một cách chính xác, bạn đang tìm kiếm "độ sáng" hoặc "giá trị" hoặc "độ sáng tương đối" như những người khác đã đề xuất. Bạn có thể tính toán điều này theo một số cách khác nhau (chẳng hạn như con người!) Http://en.wikipedia.org/wiki/HSL_and_HSV#Lightness

  1. Lấy giá trị lớn nhất của R, G và B.
  2. Lấy giá trị trung bình của giá trị lớn nhất và giá trị nhỏ nhất từ ​​R, G và B.
  3. Lấy giá trị trung bình của cả ba.
  4. Sử dụng một số trung bình có trọng số như những người khác đã đề xuất ở đây.

AFAIK chỉ tính toán luma được mô tả trên trang Wikipedia là mô hình dựa trên nhận thức.
Alnitak

2
Thật tuyệt khi chỉ ra sự khác biệt giữa năng lượng ánh sáng vật lý và độ sáng cảm nhận, nhưng tôi nghĩ rằng bạn đã có mọi thứ khá lẫn lộn. Phần của bài viết Wikipedia mà bạn đã liên kết có dấu gạch đầu dòng thứ tư, trong đó nói rằng "Một phương pháp thay thế phù hợp hơn về mặt nhận thức là sử dụng luma, Y ′, làm thứ nguyên độ đậm nhạt" (nhấn mạnh của tôi) và sau đó tiếp tục đưa ra công thức được trình bày trong Alnitak's và câu trả lời của Robin. Nói cách khác, phương pháp bạn đã bỏ qua và khuyến nghị là phương pháp phù hợp nhất với nhận thức của con người.
John Y

@JohnY vâng, đó là những gì tôi đang cố gắng nói - anh ấy đã bỏ qua câu trả lời duy nhất thực sự khớp với phần còn lại của câu trả lời.
Alnitak

Vâng, hóa ra tôi là người duy nhất bối rối ở đây. Tôi ổn với điều đó :) Tôi chỉ muốn nói rõ điểm chính là có sự khác biệt giữa năng lượng và nhận thức. Tôi sẽ cập nhật câu trả lời của tôi cho phù hợp.
David Nguyen

4

Điều này hoạt động với hex, ví dụ: #fefefe

function isTooDark(hexcolor){
    var r = parseInt(hexcolor.substr(1,2),16);
    var g = parseInt(hexcolor.substr(3,2),16);
    var b = parseInt(hexcolor.substr(4,2),16);
    var yiq = ((r*299)+(g*587)+(b*114))/1000;
    // Return new color if to dark, else return the original
    return (yiq < 40) ? '#2980b9' : hexcolor;
}

Bạn có thể đổi nó để trả lại truehoặc falseđổi

return (yiq < 40) ? '#2980b9' : hexcolor;

đến

return (yiq < 40);

@Vivek rằng vì bạn cần phải sử dụng các giá trị hex đầy đủ (# 000000 và # ffffff)
TheCrazyProfessor

2

Một giải pháp khả thi là chuyển đổi màu của bạn từ RGB sang HSB . HSB là viết tắt của màu sắc, độ bão hòa và độ sáng (còn được gọi là HSV, trong đó V là giá trị). Sau đó, bạn chỉ có một tham số để kiểm tra: độ sáng.


1

Tôi nhận ra cuộc trò chuyện này đã được vài năm, nhưng nó vẫn còn phù hợp. Tôi muốn nói thêm rằng nhóm của tôi đang gặp vấn đề tương tự trong Java (SWT) và thấy điều này chính xác hơn một chút:

private Color getFontColor(RGB bgColor) {
    Color COLOR_BLACK = new Color(Display.getDefault(), 0, 0, 0);
    Color COLOR_WHITE = new Color(Display.getDefault(), 255, 255, 255);

    double luminance = Math.sqrt(0.241 
       * Math.pow(bgColor.red, 2) + 0.691 * Math.pow(bgColor.green, 2) +  0.068 
       * Math.pow(bgColor.blue, 2));
    if (luminance >= 130) {
        return COLOR_BLACK;
    } else {
        return COLOR_WHITE;
    }
}
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.