Bagaimana cara memeriksa apakah warna hex "terlalu hitam"?

111

Saya mencoba mengevaluasi kegelapan warna yang dipilih oleh pemilih warna untuk melihat apakah itu "terlalu hitam", dan jika demikian, setel ke putih. Saya pikir saya bisa menggunakan karakter pertama dari nilai hex untuk melakukan ini. Ini berfungsi, tetapi juga mengganti beberapa warna yang "terang".

Saya memiliki kode yang melakukan ini:

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

Harus ada cara yang lebih efisien dengan matematika hex untuk mengetahui bahwa warna telah melampaui tingkat kegelapan tertentu? Seperti jika lightcolor + "beberapa nilai hex" <= "beberapa nilai hex" lalu atur menjadi putih.

Saya menambahkan tinyColor, yang mungkin berguna untuk ini, tapi saya tidak tahu pasti.

Dshiz
sumber
1
Sudahkah Anda mencoba mendapatkan pemilih warna dan memeriksa nilainya? Saya perhatikan bahwa ketika R, G dan B semuanya di bawah ~ 70 hari menjadi gelap. Ini mungkin bukan cara yang tepat, tapi itu salah satunya.
Rick Kuipers
1
Karena Anda sudah menggunakan tinyColor, ubah warnanya menjadi HSL dan lihat komponen L. 1 = putih, 0 = hitam
Andreas
4
@Andreas HSL ringan tidak memperhitungkan persepsi manusia. Nilai L 0,5 akan memiliki persepsi kecerahan yang berbeda untuk warna yang berbeda.
Alnitak
1
@Alnitak Anda benar tetapi deskripsi TO tidak tepat. Jadi nilai apa pun di bawah 3/8 bisa jadi cukup gelap untuk tujuannya.
Andreas
1
@Andreas yang bergantung - jika Anda melihat nilai luminansi ITU di jawaban saya, Anda akan melihat bahwa biru dianggap hanya 1/10 seterang hijau.
Alnitak

Jawaban:

226

Anda harus mengekstrak tiga komponen RGB satu per satu, dan kemudian menggunakan rumus standar untuk mengubah nilai RGB yang dihasilkan menjadi kecerahan yang dirasakan.

Dengan asumsi warna enam karakter:

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
}

EDIT

Sejak Mei 2014 tinycolorsekarang memiliki getBrightness()fungsi, meskipun menggunakan faktor pembobotan CCIR601, bukan yang ITU-R di atas.

EDIT

Rentang nilai luma yang dihasilkan adalah 0..255, di mana 0 adalah yang paling gelap dan 255 adalah yang paling terang. Nilai yang lebih besar dari 128 dianggap ringan tinycolor. (disalin tanpa malu-malu dari komentar oleh @ pau.moreno dan @Alnitak)

Alnitak
sumber
11
sudah lama tidak melihat manipulasi bit yang bagus di javascript. barang keren. en.wikipedia.org/wiki/Rec._709#Luma_coefficients
jbabey
2
Kode yang bagus, tetapi setelah pengujian saya menyarankan var luma = (r + g + b) / 3; if (luma <128) {// akan lebih berguna. }
Terry Lin
2
@TerryLin Mengapa? Koefisien yang diberikan adalah nilai standar ITU yang memungkinkan fakta bahwa hijau dianggap lebih cerah daripada merah (lalu biru).
Alnitak
1
lumaRentang nilai yang dihasilkan adalah 0..255, di mana 0paling gelap dan 255paling terang (ketiga koefisien berjumlah satu).
pau.moreno
1
@gabssnake hanya sejak Mei 2014, dan isDark()ambang batasnya di-hardcode pada 128
Alnitak
21

The TinyColor perpustakaan (Anda sudah disebutkan itu) menyediakan beberapa fungsi untuk memeriksa dan memanipulasi warna, di antaranya:

  • getBrightness

    Mengembalikan kecerahan warna yang dirasakan, dari 0-255, seperti yang ditentukan oleh Panduan Aksesibilitas Konten Web (Versi 1.0) .

    tinycolor("#fff").getBrightness(); // 255
  • isLight

    Kembalikan boolean yang menunjukkan apakah kecerahan warna yang dirasakan terang.

    tinycolor("#fff").isLight(); // true
    tinycolor("#000").isLight(); // false
  • gelap

    Kembalikan boolean yang menunjukkan apakah kecerahan warna yang dirasakan gelap.

    tinycolor("#fff").isDark(); // false
    tinycolor("#000").isDark(); // true
  • getLuminance

    Mengembalikan kecerahan warna yang dirasakan, dari 0-1 seperti yang ditentukan oleh Panduan Aksesibilitas Konten Web (Versi 2.0) .

    tinycolor("#fff").getLuminance(); // 1
skalee
sumber
15

Saya menemukan fungsi PHP Wordpress WooCommerce ini ( wc_hex_is_light ) dan saya mengubahnya ke JavaScript. Bekerja dengan baik!

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;
}
Sergio Cabral
sumber
1
Sangat keren, terima kasih! Saya mengujinya dengan beberapa warna, deteksi benar dengan semuanya :)
David Dal Busco
colorIsDarkOrLight (color) {var hex = color.replace ("#", ""); var c_r, c_g, c_b, brightness = ""; 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); kecerahan = (c_r * 299 + c_g * 587 + c_b * 114) / 1000; } lain {c_r = parseInt (hex.substr (0, 2), 16); c_g = parseInt (hex.substr (2, 2), 16); c_b = parseInt (hex.substr (4, 2), 16); } kecerahan balik> 155; },
Pedro Henrique
Gunakan dengan hex 3 karakter dan 6
Pedro Henrique
6

Anda dapat menghitung luminansi :

Luminance dengan demikian merupakan indikator seberapa terang permukaan akan tampak.

Jadi, sangat bagus untuk memilih apakah teks harus putih atau hitam.

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

Metode di atas memungkinkan Anda untuk mengirimkan warna dalam format yang berbeda, tetapi algoritme pada dasarnya hanya masuk luminance_get.

Saat saya menggunakannya, saya mengatur warna menjadi hitam jika luminansi lebih besar dari 180, putih sebaliknya.

Robin
sumber
5

Ada perbedaan penting di sini antara pencahayaan dan kecerahan. Luminance, pada akhirnya, adalah ukuran berapa banyak energi yang mengalir melalui area tertentu dan sepenuhnya mengabaikan bagaimana sistem persepsi kita memandang energi itu. Kecerahan, di sisi lain, adalah ukuran bagaimana kita merasakan energi itu dan memperhitungkan hubungan antara pencahayaan dan sistem persepsi kita. (Sebagai titik kebingungan, ada istilah yang disebut luminansi relatif, yang tampaknya digunakan secara sinonim dengan istilah kecerahan. Itu membuat saya tersandung).

Tepatnya, Anda mencari "kecerahan" atau "nilai" atau "luminansi relatif" seperti yang disarankan orang lain. Anda dapat menghitung ini dengan beberapa cara berbeda (seperti menjadi manusia!) Http://en.wikipedia.org/wiki/HSL_and_HSV#Lightness

  1. Ambil nilai maksimal R, G, dan B.
  2. Ambil rata-rata maks dan min dari R, G, dan B.
  3. Ambil rata-rata dari ketiganya.
  4. Gunakan beberapa rata-rata tertimbang seperti yang disarankan orang lain di sini.
David Nguyen
sumber
AFAIK hanya perhitungan luma yang dijelaskan di halaman Wikipedia adalah model berbasis persepsi.
Alnitak
2
Senang rasanya menunjukkan perbedaan antara energi cahaya fisik dan kecerahan yang dirasakan, tetapi saya pikir ada beberapa hal yang agak campur aduk. Bagian dari artikel Wikipedia yang Anda tautkan memiliki poin keempat, yang menyatakan "Alternatif yang lebih relevan secara perseptual adalah menggunakan luma, Y ′, sebagai dimensi ringan" (penekanan saya) dan kemudian memberikan rumus yang disajikan di Alnitak's dan jawaban Robin. Dengan kata lain, metode yang Anda tinggalkan dan rekomendasikan adalah yang paling cocok dengan persepsi manusia.
John Y
@JohnY ya, itulah yang ingin saya katakan - dia meninggalkan satu-satunya yang benar-benar cocok dengan sisa jawabannya.
Alnitak
Ya, ternyata hanya saya yang bingung disini. Saya tidak keberatan :) Saya hanya ingin menyampaikan poin utama bahwa ada perbedaan antara energi dan persepsi. Saya akan memperbarui jawaban saya.
David Nguyen
4

Ini bekerja dengan hex misalnya #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;
}

Anda dapat mengubahnya untuk kembali trueatau falsedengan mengubah

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

untuk

return (yiq < 40);
Profesor Gila
sumber
@Vivek itu karena Anda perlu menggunakan nilai hex penuh (# 000000 dan #ffffff)
TheCrazyProfessor
2

Solusi yang mungkin adalah mengubah warna Anda dari RGB ke HSB . HSB adalah singkatan dari hue, saturation, dan brightness (juga dikenal sebagai HSV, di mana V untuk nilai). Maka Anda hanya memiliki satu parameter untuk diperiksa: kecerahan.

Ohad
sumber
1

Saya menyadari percakapan ini berumur beberapa tahun, tetapi masih relevan. Saya ingin menambahkan bahwa tim saya mengalami masalah yang sama di Java (SWT) dan menemukan ini sedikit lebih akurat:

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;
    }
}
Chris Clark
sumber