Bagaimana cara menentukan warna font putih atau hitam tergantung pada warna latar belakang?


221

Saya ingin menunjukkan beberapa gambar seperti contoh initeks alternatif

Warna isi ditentukan oleh bidang dalam basis data dengan warna dalam hex (mis: ClassX -> Color: # 66FFFF). Sekarang, saya ingin menampilkan data di atas isian dengan warna yang dipilih (seperti pada gambar di atas) tetapi saya perlu tahu apakah warnanya gelap atau terang jadi saya tahu apakah kata-katanya harus putih atau hitam. Apakah ada cara? tks


Jawaban:


347

Membangun jawaban saya untuk pertanyaan serupa .

Anda perlu memecah kode hex menjadi 3 bagian untuk mendapatkan intensitas merah, hijau, dan biru individu. Setiap 2 digit kode mewakili nilai dalam notasi heksadesimal (basis-16). Saya tidak akan membahas perincian konversi di sini, mudah dilihat.

Setelah Anda memiliki intensitas untuk masing-masing warna, Anda dapat menentukan intensitas keseluruhan warna dan memilih teks yang sesuai.

if (red*0.299 + green*0.587 + blue*0.114) > 186 use #000000 else use #ffffff

Ambang batas 186 didasarkan pada teori, tetapi dapat disesuaikan dengan selera. Berdasarkan komentar di bawah ambang batas 150 dapat bekerja lebih baik untuk Anda.


Sunting: Di atas sederhana dan berfungsi dengan cukup baik, dan tampaknya memiliki penerimaan yang baik di sini di StackOverflow. Namun, salah satu komentar di bawah ini menunjukkan hal itu dapat menyebabkan ketidakpatuhan terhadap pedoman W3C dalam beberapa keadaan. Dengan ini saya memperoleh formulir yang dimodifikasi yang selalu memilih kontras tertinggi berdasarkan pedoman. Jika Anda tidak perlu mematuhi aturan W3C maka saya akan tetap menggunakan rumus sederhana di atas.

Rumus yang diberikan untuk kontras dalam Rekomendasi W3C adalah (L1 + 0.05) / (L2 + 0.05), di mana L1luminans warna paling L2terang dan luminans yang paling gelap pada skala 0,0-1,0. Luminance hitam adalah 0,0 dan putih 1,0, jadi dengan mengganti nilai-nilai itu Anda dapat menentukan nilai dengan kontras tertinggi. Jika kontras untuk hitam lebih besar dari kontras untuk putih, gunakan hitam, jika tidak gunakan putih. Mengingat pencahayaan warna yang Anda uji saat Ltes menjadi:

if (L + 0.05) / (0.0 + 0.05) > (1.0 + 0.05) / (L + 0.05) use #000000 else use #ffffff

Ini disederhanakan secara aljabar menjadi:

if L > sqrt(1.05 * 0.05) - 0.05

Atau kira-kira:

if L > 0.179 use #000000 else use #ffffff

Satu-satunya yang tersisa adalah menghitung L. Rumus itu juga diberikan dalam pedoman dan sepertinya konversi dari sRGB ke RGB linier diikuti oleh rekomendasi ITU-R BT.709 untuk pencahayaan.

for each c in r,g,b:
    c = c / 255.0
    if c <= 0.03928 then c = c/12.92 else c = ((c+0.055)/1.055) ^ 2.4
L = 0.2126 * r + 0.7152 * g + 0.0722 * b

Ambang batas 0,179 tidak boleh diubah karena terikat dengan pedoman W3C. Jika Anda menemukan hasilnya tidak sesuai dengan keinginan Anda, coba rumus sederhana di atas.


2
Tks Mark. Mencoba beberapa perubahan: dihitung merah hijau dan biru hanya dengan digit pertama (kurang tepat tetapi merupakan digit dengan bobot lebih) dan bukannya 186 yang digunakan 9. bekerja sedikit lebih baik untuk saya, khususnya dengan hijau.
DJPB

2
Formula ini salah, banyak. Untuk mengambil satu contoh, itu memberi nilai kuning #D6B508dari 171, maka kontras putih. Namun, kontrasnya harus hitam (dikonfirmasi di sini: webaim.org/resources/contrastchecker )
McGarnagle

1
@ Markarkom ya, di mataku hitam terlihat jauh lebih baik dengan kuning itu. Karena saya bekerja dengan serangkaian warna yang terbatas, saya dapat mengubah cut-off dari 186 ke nilai yang lebih rendah.
McGarnagle

3
Berikut ini demo lain yang membandingkan dua rumus yang diberikan dalam jawaban ini. Menggunakan color picker (di Firefox atau Chrome baru-baru ini) Anda dapat memeriksa kontras terhadap warna apa pun.
chetstone

3
Setelah bermain dengan demo @ chetstone sebentar, saya pikir rumus Simple paling cocok untuk saya, kecuali saya tidak setuju dengan rekomendasi ambang batas. Berdasarkan hasil hijau murni, saya mencoba menetapkan ambang batas ke 149 dan itu bekerja jauh lebih baik menurut saya. Saya membuat biola yang sangat bodoh untuk menunjukkan ini ; Anda dapat mencoba mengubah ambang batas di bagian belakang atas untuk melihat saran asli.
Miral

30

Saya tidak mengambil kredit untuk kode ini karena ini bukan milik saya, tetapi saya meninggalkannya di sini agar orang lain dapat menemukan dengan cepat di masa depan:

Berdasarkan jawaban Mark Ransoms, inilah cuplikan kode untuk versi sederhana:

function pickTextColorBasedOnBgColorSimple(bgColor, lightColor, darkColor) {
  var color = (bgColor.charAt(0) === '#') ? bgColor.substring(1, 7) : bgColor;
  var r = parseInt(color.substring(0, 2), 16); // hexToR
  var g = parseInt(color.substring(2, 4), 16); // hexToG
  var b = parseInt(color.substring(4, 6), 16); // hexToB
  return (((r * 0.299) + (g * 0.587) + (b * 0.114)) > 186) ?
    darkColor : lightColor;
}

dan inilah cuplikan kode untuk versi lanjutan:

function pickTextColorBasedOnBgColorAdvanced(bgColor, lightColor, darkColor) {
  var color = (bgColor.charAt(0) === '#') ? bgColor.substring(1, 7) : bgColor;
  var r = parseInt(color.substring(0, 2), 16); // hexToR
  var g = parseInt(color.substring(2, 4), 16); // hexToG
  var b = parseInt(color.substring(4, 6), 16); // hexToB
  var uicolors = [r / 255, g / 255, b / 255];
  var c = uicolors.map((col) => {
    if (col <= 0.03928) {
      return col / 12.92;
    }
    return Math.pow((col + 0.055) / 1.055, 2.4);
  });
  var L = (0.2126 * c[0]) + (0.7152 * c[1]) + (0.0722 * c[2]);
  return (L > 0.179) ? darkColor : lightColor;
}

Untuk menggunakannya cukup hubungi:

var color = '#EEACAE' // this can be any color
pickTextColorBasedOnBgColorSimple(color, '#FFFFFF', '#000000');

Juga, terima kasih Alxdan chetstone.


1
Saya menggunakan fungsi sederhana, dan menghapusnya lebih sedikit: menjatuhkan 2 parameter terakhir dan berganti nama menjadi hanya isDark(bgColor)Penggunaan saya kemudian hanya'color': isDark(color)?'white':'black'
diynevala

1
Bekerja seperti pesona bagi saya. Terima kasih banyak! Apakah itu di php meskipun hanya mengubah sintaks dan fungsi.
CezarBastos

19

Bagaimana dengan ini (kode JavaScript)?

/**
 * Get color (black/white) depending on bgColor so it would be clearly seen.
 * @param bgColor
 * @returns {string}
 */
getColorByBgColor(bgColor) {
    if (!bgColor) { return ''; }
    return (parseInt(bgColor.replace('#', ''), 16) > 0xffffff / 2) ? '#000' : '#fff';
}

1
Ini berfungsi sebagian besar waktu tetapi ada kasus seperti i.imgur.com/3pOUDe5.jpg yang terlihat aneh, warna latar belakang sebenarnya rgb (6, 247, 241);
madprops

Saya menghargai ini adalah cara yang relatif murah untuk melakukan perhitungan kontras dalam kasus di mana Anda belum mengonversi warna menjadi nilai rgb (bukan berarti itu terlalu sulit, hanya langkah matematika tambahan)
frumbert

12

Selain solusi aritmatika, juga dimungkinkan untuk menggunakan jaringan saraf AI. Keuntungannya adalah Anda dapat menyesuaikannya dengan selera dan kebutuhan Anda sendiri (mis. Teks off-white pada merah jenuh cerah terlihat bagus dan sama mudah dibaca dengan hitam).

Berikut ini adalah demo Javascript yang menggambarkan konsep tersebut. Anda juga dapat membuat rumus JS Anda sendiri di demo.

https://harthur.github.io/brain/

Di bawah ini adalah beberapa bagan yang membantu saya menyelesaikan masalah . Pada bagan pertama, cahaya adalah konstanta 128, sedangkan rona dan saturasi bervariasi. Pada bagan kedua, saturasi adalah konstan 255, sedangkan rona dan cahaya bervariasi.

Pada bagan pertama, cahaya adalah konstanta 128, sedangkan rona dan saturasi bervariasi:

Saturasi adalah konstan 255, sedangkan rona dan cahaya bervariasi:


8

Inilah solusi saya di Java untuk Android:

// Put this method in whichever class you deem appropriate
// static or non-static, up to you.
public static int getContrastColor(int colorIntValue) {
    int red = Color.red(colorIntValue);
    int green = Color.green(colorIntValue);
    int blue = Color.blue(colorIntValue);
    double lum = (((0.299 * red) + ((0.587 * green) + (0.114 * blue))));
    return lum > 186 ? 0xFF000000 : 0xFFFFFFFF;
}

// Usage
// If Color is represented as HEX code:
String colorHex = "#484588";
int color = Color.parseColor(colorHex);

// Or if color is Integer:
int color = 0xFF484588;

// Get White (0xFFFFFFFF) or Black (0xFF000000)
int contrastColor = WhateverClass.getContrastColor(color);

2
Apakah ini benar-benar 'sempurna'? Coba latar belakang hijau murni, # 00FF00.
Andreas Rejbrand

Memang benar ini tidak diuji untuk semua warna .... tapi siapa yang akan menggunakan latar belakang hijau murni untuk apa pun yang tidak dirancang untuk mengganggu pengguna?
mwieczorek

@mwieczorek orang yang mengandalkan konten buatan pengguna atau warna yang dipilih secara acak.
Marc Plano-Lesay

4

Berdasarkan jawaban @MarkRansom, saya membuat skrip PHP yang dapat Anda temukan di sini:

function calcC($c) {
    if ($c <= 0.03928) {
        return $c / 12.92;
    }
    else {
        return pow(($c + 0.055) / 1.055, 2.4);
    }
}

function cutHex($h) {
    return ($h[0] == "#") ? substr($h, 1, 7) : $h;
}

function hexToR($h) {
    return hexdec(substr(cutHex($h), 0, 2));
}

function hexToG($h) {
    return hexdec(substr(cutHex($h), 2, 2)); // Edited
}

function hexToB($h) {
    return hexdec(substr(cutHex($h), 4, 2)); // Edited
}

function computeTextColor($color) {
    $r = hexToR($color);
    $g = hexToG($color);
    $b = hexToB($color);
    $uicolors = [$r / 255, $g / 255, $b / 255];


    $c = array_map("calcC", $uicolors);

    $l = 0.2126 * $c[0] + 0.7152 * $c[1] + 0.0722 * $c[2];
    return ($l > 0.179) ? '#000000' : '#ffffff';
}

3

Ini adalah versi cepat dari jawaban Mark Ransom sebagai perpanjangan dari UIColor

extension UIColor {

// Get the rgba components in CGFloat
var rgba: (red: CGFloat, green: CGFloat, blue: CGFloat, alpha: CGFloat) {
    var red: CGFloat = 0, green: CGFloat = 0, blue: CGFloat = 0, alpha: CGFloat = 0

    getRed(&red, green: &green, blue: &blue, alpha: &alpha)

    return (red, green, blue, alpha)
}

/// Return the better contrasting color, white or black
func contrastColor() -> UIColor {
    let rgbArray = [rgba.red, rgba.green, rgba.blue]

    let luminanceArray = rgbArray.map({ value -> (CGFloat) in
        if value < 0.03928 {
            return (value / 12.92)
        } else {
            return (pow( (value + 0.55) / 1.055, 2.4) )
        }
    })

    let luminance = 0.2126 * luminanceArray[0] +
        0.7152 * luminanceArray[1] +
        0.0722 * luminanceArray[2]

    return luminance > 0.179 ? UIColor.black : UIColor.white
} }

2

Ini hanyalah contoh yang akan mengubah warna tanda centang SVG saat mengklik elemen. Ini akan mengatur warna tanda centang menjadi hitam atau putih berdasarkan warna latar belakang elemen yang diklik.

checkmarkColor: function(el) {
    var self = el;
    var contrast = function checkContrast(rgb) {
        // @TODO check for HEX value

        // Get RGB value between parenthesis, and remove any whitespace
        rgb = rgb.split(/\(([^)]+)\)/)[1].replace(/ /g, '');

        // map RGB values to variables
        var r = parseInt(rgb.split(',')[0], 10),
            g = parseInt(rgb.split(',')[1], 10),
            b = parseInt(rgb.split(',')[2], 10),
            a;

        // if RGBA, map alpha to variable (not currently in use)
        if (rgb.split(',')[3] !== null) {
            a = parseInt(rgb.split(',')[3], 10);
        }

        // calculate contrast of color (standard grayscale algorithmic formula)
        var contrast = (Math.round(r * 299) + Math.round(g * 587) + Math.round(b * 114)) / 1000;

        return (contrast >= 128) ? 'black' : 'white';
    };

    $('#steps .step.color .color-item .icon-ui-checkmark-shadow svg').css({
        'fill': contrast($(self).css('background-color'))
    });
}

onClickExtColor: function(evt) {
    var self = this;

    self.checkmarkColor(evt.currentTarget);
}

https://gist.github.com/dcondrey/183971f17808e9277572


2

Saya menggunakan fungsi JavaScript ini untuk mengonversi rgb/ rgbake 'white'atau 'black'.

function getTextColor(rgba) {
    rgba = rgba.match(/\d+/g);
    if ((rgba[0] * 0.299) + (rgba[1] * 0.587) + (rgba[2] * 0.114) > 186) {
        return 'black';
    } else {
        return 'white';
    }
}

Anda dapat memasukkan salah satu format ini dan itu akan menampilkan 'black'atau'white'

  • rgb(255,255,255)
  • rgba(255,255,255,0.1)
  • color:rgba(255,255,255,0.1)
  • 255,255,255,0.1

Sekarang coba ini dengan latar belakang hijau murni: # 00FF00.
Andreas Rejbrand

Terima kasih untuk ini! Diterjemahkan ke dalam cepat dan menggunakannya di aplikasi ios saya!
Lucas P.

2

Jawaban terperinci Markus sangat berhasil. Berikut ini adalah implementasi dalam javascript:

function lum(rgb) {
    var lrgb = [];
    rgb.forEach(function(c) {
        c = c / 255.0;
        if (c <= 0.03928) {
            c = c / 12.92;
        } else {
            c = Math.pow((c + 0.055) / 1.055, 2.4);
        }
        lrgb.push(c);
    });
    var lum = 0.2126 * lrgb[0] + 0.7152 * lrgb[1] + 0.0722 * lrgb[2];
    return (lum > 0.179) ? '#000000' : '#ffffff';
}

Kemudian dapat memanggil fungsi ini lum([111, 22, 255])untuk mendapatkan putih atau hitam.


1

Saya belum pernah melakukan hal seperti ini, tetapi bagaimana dengan menulis fungsi untuk memeriksa nilai masing-masing warna terhadap warna median Hex 7F (FF / 2). Jika dua dari tiga warna lebih besar dari 7F, maka Anda bekerja dengan warna yang lebih gelap.


1

Berdasarkan input berbeda dari tautan, buat foregroundcolor hitam atau putih tergantung pada latar belakang dan utas ini, saya membuat kelas ekstensi untuk Warna yang memberi Anda warna kontras yang diperlukan.

Kodenya seperti di bawah ini:

 public static class ColorExtension
{       
    public static int PerceivedBrightness(this Color c)
    {
        return (int)Math.Sqrt(
        c.R * c.R * .299 +
        c.G * c.G * .587 +
        c.B * c.B * .114);
    }
    public static Color ContrastColor(this Color iColor, Color darkColor,Color lightColor)
    {
        //  Counting the perceptive luminance (aka luma) - human eye favors green color... 
        double luma = (iColor.PerceivedBrightness() / 255);

        // Return black for bright colors, white for dark colors
        return luma > 0.5 ? darkColor : lightColor;
    }
    public static Color ContrastColor(this Color iColor) => iColor.ContrastColor(Color.Black);
    public static Color ContrastColor(this Color iColor, Color darkColor) => iColor.ContrastColor(darkColor, Color.White);
    // Converts a given Color to gray
    public static Color ToGray(this Color input)
    {
        int g = (int)(input.R * .299) + (int)(input.G * .587) + (int)(input.B * .114);
        return Color.FromArgb(input.A, g, g, g);
    }
}

1

Jika seperti saya Anda mencari versi RGBA yang memperhitungkan alpha, berikut ini yang berfungsi sangat baik untuk memiliki kontras tinggi.

function getContrastColor(R, G, B, A) {
  const brightness = R * 0.299 + G * 0.587 + B * 0.114 + (1 - A) * 255;

  return brightness > 186 ? "#000000" : "#FFFFFF";
}

1

Ini adalah versi R dari jawaban Mark Ransom, menggunakan basis R saja.

hex_bw <- function(hex_code) {

  myrgb <- as.integer(col2rgb(hex_code))

  rgb_conv <- lapply(myrgb, function(x) {
    i <- x / 255
    if (i <= 0.03928) {
      i <- i / 12.92
    } else {
      i <- ((i + 0.055) / 1.055) ^ 2.4
    }
    return(i)
  })

 rgb_calc <- (0.2126*rgb_conv[[1]]) + (0.7152*rgb_conv[[2]]) + (0.0722*rgb_conv[[3]])

 if (rgb_calc > 0.179) return("#000000") else return("#ffffff")

}

> hex_bw("#8FBC8F")
[1] "#000000"
> hex_bw("#7fa5e3")
[1] "#000000"
> hex_bw("#0054de")
[1] "#ffffff"
> hex_bw("#2064d4")
[1] "#ffffff"
> hex_bw("#5387db")
[1] "#000000"

0

@ SoBiT, saya melihat jawaban Anda, yang terlihat bagus, tetapi ada kesalahan kecil di dalamnya. Fungsi Anda hexToG, dan hextoB perlu diedit minor. Angka terakhir dalam substr adalah panjang string, dan dalam hal ini harus "2", bukan 4 atau 6.

function hexToR($h) {
    return hexdec(substr(cutHex($h), 0, 2));
}
function hexToG($h) {
    return hexdec(substr(cutHex($h), 2, 2));
}
function hexToB($h) {
    return hexdec(substr(cutHex($h), 4, 2));
}

0

KURANG memiliki contrast()fungsi bagus yang bekerja dengan baik untuk saya, lihat http://lesscss.org/functions/#color-operations-contrast

"Pilih mana dari dua warna yang memberikan kontras terbesar dengan yang lain. Ini berguna untuk memastikan bahwa warna dapat dibaca dengan latar belakang, yang juga berguna untuk kepatuhan aksesibilitas. Fungsi ini bekerja dengan cara yang sama seperti fungsi kontras dalam Kompas untuk SASS. Sesuai dengan WCAG 2.0, warna dibandingkan dengan menggunakan nilai luma yang dikoreksi gamma, bukan terang mereka. "

Contoh:

p {
    a: contrast(#bbbbbb);
    b: contrast(#222222, #101010);
    c: contrast(#222222, #101010, #dddddd);
    d: contrast(hsl(90, 100%, 50%), #000000, #ffffff, 30%);
    e: contrast(hsl(90, 100%, 50%), #000000, #ffffff, 80%);
}

Keluaran:

p {
    a: #000000 // black
    b: #ffffff // white
    c: #dddddd
    d: #000000 // black
    e: #ffffff // white
}

0

Dari hex menjadi hitam atau putih:

function hexToRgb(hex) {
  var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
  return result
    ? [
        parseInt(result[1], 16),
        parseInt(result[2], 16),
        parseInt(result[3], 16)
      ]
    : [0, 0, 0];
}

function lum(hex) {
  var rgb = hexToRgb(hex)
  var lrgb = [];
  rgb.forEach(function(c) {
    c = c / 255.0;
    if (c <= 0.03928) {
      c = c / 12.92;
    } else {
      c = Math.pow((c + 0.055) / 1.055, 2.4);
    }
    lrgb.push(c);
  });
  var lum = 0.2126 * lrgb[0] + 0.7152 * lrgb[1] + 0.0722 * lrgb[2];
  return lum > 0.179 ? "#000000" : "#ffffff";
}

0

Kode versi objektif-c untuk iOS berdasarkan pada jawaban Mark:

- (UIColor *)contrastForegroundColor {
CGFloat red = 0, green = 0, blue = 0, alpha = 0;
[self getRed:&red green:&green blue:&blue alpha:&alpha];
NSArray<NSNumber *> *rgbArray = @[@(red), @(green), @(blue)];
NSMutableArray<NSNumber *> *parsedRGBArray = [NSMutableArray arrayWithCapacity:rgbArray.count];
for (NSNumber *item in rgbArray) {
    if (item.doubleValue <= 0.03928) {
        [parsedRGBArray addObject:@(item.doubleValue / 12.92)];
    } else {
        double newValue = pow((item.doubleValue + 0.055) / 1.055, 2.4);
        [parsedRGBArray addObject:@(newValue)];
    }
}

double luminance = 0.2126 * parsedRGBArray[0].doubleValue + 0.7152 * parsedRGBArray[1].doubleValue + 0.0722 * parsedRGBArray[2].doubleValue;

return luminance > 0.179 ? UIColor.blackColor : UIColor.whiteColor;
}

0

Bagaimana dengan pengujian dengan semua 24 bit warna?

Sadarilah bahwa metode YIQ akan mengembalikan rasio kontras minimum 1,9: 1, yang tidak lulus tes AA dan AAA WCAG2.0, dengan asumsi ambang batas 128.

Untuk metode W3C, itu akan mengembalikan rasio kontras minimum 4,58: 1, yang lulus tes AA dan AAA untuk teks besar, dan tes AA untuk teks kecil, itu tidak akan lulus tes AAA untuk teks kecil untuk setiap warna.


0

Inilah metode saya sendiri yang telah saya gunakan dan belum memiliki masalah dengan sejauh ini 😄

const hexCode = value.charAt(0) === '#' 
                  ? value.substr(1, 6)
                  : value;

const hexR = parseInt(hexCode.substr(0, 2), 16);
const hexG = parseInt(hexCode.substr(2, 2), 16);
const hexB = parseInt(hexCode.substr(4, 2), 16);
// Gets the average value of the colors
const contrastRatio = (hexR + hexG + hexB) / (255 * 3);

contrastRatio >= 0.5
  ? 'black'
  : 'white';


0

Ini kode saya untuk Java Swing berdasarkan jawaban Mark yang luar biasa:

public static Color getColorBasedOnBackground(Color background, Color darkColor, Color lightColor) {
    // Calculate foreground color based on background (based on https://stackoverflow.com/a/3943023/)
    Color color;
    double[] cL = new double[3];
    double[] colorRGB = new double[] {background.getRed(), background.getGreen(), background.getBlue()};

    for (int i = 0; i < colorRGB.length; i++)
        cL[i] = (colorRGB[i] / 255.0 <= 0.03928) ? colorRGB[i] / 255.0 / 12.92 :
                Math.pow(((colorRGB[i] / 255.0 + 0.055) / 1.055), 2.4);

    double L = 0.2126 * cL[0] + 0.7152 * cL[1] + 0.0722 * cL[2];
    color = (L > Math.sqrt(1.05 * 0.05) - 0.05) ? darkColor : lightColor;

    return color;
}
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.