Come decidere il colore del carattere in bianco o nero a seconda del colore di sfondo?


221

Voglio mostrare alcune immagini come questo esempiotesto alternativo

Il colore di riempimento è deciso da un campo nella base dati con il colore in esadecimale (es: ClassX -> Colore: # 66FFFF). Ora, voglio mostrare i dati sopra un riempimento con il colore selezionato (come nell'immagine sopra) ma ho bisogno di sapere se il colore è scuro o chiaro, quindi so se le parole dovrebbero essere in bianco o nero. C'è un modo? tks


Risposte:


347

Basandomi sulla mia risposta a una domanda simile .

Devi dividere il codice esadecimale in 3 pezzi per ottenere le singole intensità rosse, verdi e blu. Ogni 2 cifre del codice rappresentano un valore in notazione esadecimale (base-16). Non entrerò nei dettagli della conversione qui, sono facili da consultare.

Una volta che hai le intensità per i singoli colori, puoi determinare l'intensità complessiva del colore e scegliere il testo corrispondente.

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

La soglia di 186 si basa sulla teoria, ma può essere regolata a piacere. Sulla base dei commenti al di sotto di una soglia di 150 potrebbe funzionare meglio per te.


Modifica: quanto sopra è semplice e funziona abbastanza bene, e sembra avere una buona accettazione qui su StackOverflow. Tuttavia, uno dei commenti seguenti mostra che in alcune circostanze può portare alla non conformità alle linee guida del W3C. Con la presente ne ricavo una forma modificata che sceglie sempre il massimo contrasto sulla base delle linee guida. Se non è necessario conformarsi alle regole del W3C, attenersi alla formula più semplice sopra.

La formula indicata per il contrasto nelle raccomandazioni del W3C è (L1 + 0.05) / (L2 + 0.05), dove L1è la luminanza del colore più chiaro ed L2è la luminanza del più scuro su una scala di 0,0-1,0. La luminanza del nero è 0,0 e il bianco è 1,0, quindi sostituendo questi valori è possibile determinare quello con il contrasto più elevato. Se il contrasto per il nero è maggiore del contrasto per il bianco, utilizzare il nero, altrimenti utilizzare il bianco. Data la luminanza del colore che stai testando quando Lil test diventa:

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

Questo semplifica algebricamente:

if L > sqrt(1.05 * 0.05) - 0.05

O approssimativamente:

if L > 0.179 use #000000 else use #ffffff

L'unica cosa rimasta è calcolare L. Tale formula è anche indicata nelle linee guida e sembra la conversione da RGB RGB a RGB lineare seguita dalla raccomandazione ITU-R BT.709 per la luminanza.

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

La soglia di 0,179 non deve essere modificata poiché è legata alle linee guida del W3C. Se trovi che i risultati non ti piacciono, prova la formula più semplice sopra.


2
Tks Mark. Ho provato alcune modifiche: calcolato rosso verde e blu solo dalla prima cifra (meno precisa ma è la cifra con più peso) e invece di 186 usate 9. funziona un po 'meglio per me, specialmente con i verdi.
DJPB,

2
Questa formula è sbagliata, di molto. Per fare un esempio, dà al giallo #D6B508un valore di 171, quindi un contrasto bianco. Il contrasto dovrebbe essere nero, tuttavia (confermato qui: webaim.org/resources/contrastchecker )
McGarnagle,

1
@MarkRansom sì, ai miei occhi il nero sembra molto meglio con quel giallo. Dal momento che sto lavorando con un set limitato di colori, sono stato in grado di spostare il cut-off da 186 a un valore inferiore.
McGarnagle,

3
Ecco un'altra demo che confronta le due formule fornite in questa risposta. Utilizzando il selettore colore (in Firefox o Chrome recente) è possibile verificare il contrasto con qualsiasi colore.
Chetstone,

3
Dopo aver giocato con la demo di @ chetstone per un po ', penso che la formula semplice funzioni meglio per me, tranne per il fatto che non sono d'accordo con la raccomandazione sulla soglia. Sulla base dei risultati del verde puro, ho provato a impostare la soglia su 149 e secondo me funziona molto meglio. Ho fatto un violino davvero stupido per dimostrarlo ; puoi provare a cambiare la soglia in alto indietro per vedere il suggerimento originale.
Miral,

30

Non mi merito per questo codice in quanto non è mio, ma lo lascio qui affinché altri lo trovino rapidamente in futuro:

Sulla base della risposta di Mark Ransoms, ecco uno snippet di codice per la versione semplice:

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

ed ecco lo snippet di codice per la versione avanzata:

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

Per usarli basta chiamare:

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

Inoltre, grazie Alxe chetstone.


1
Ho usato la semplice funzione e l'ho ridotta un po 'di più: ho lasciato cadere gli ultimi 2 parametri e rinominato è semplicemente Il isDark(bgColor)mio uso è allora'color': isDark(color)?'white':'black'
diynevala

1
Ha funzionato benissimo per me. Grazie mille! L'ho fatto in php anche se semplicemente convertendo la sintassi e le funzioni.
CezarBastos,

19

Che ne dici di questo (codice 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
Funziona la maggior parte delle volte ma ci sono casi come i.imgur.com/3pOUDe5.jpg che sembrano strani, il colore di sfondo è in realtà rgb (6, 247, 241);
madprops,

Apprezzo che questo sia un modo relativamente economico per eseguire un calcolo del contrasto nei casi in cui non hai già convertito il colore in valori rgb (non è troppo difficile, solo passaggi matematici extra)
frumbert

12

Oltre alle soluzioni aritmetiche, è anche possibile utilizzare una rete neurale AI. Il vantaggio è che puoi personalizzarlo secondo i tuoi gusti e le tue esigenze (ad es. Il testo bianco sporco sui rossi saturi luminosi sembra buono ed è leggibile quanto il nero).

Ecco una bella demo di Javascript che illustra il concetto. Puoi anche generare la tua formula JS direttamente nella demo.

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

Di seguito sono riportati alcuni grafici che mi hanno aiutato a risolvere il problema . Nel primo grafico, la luminosità è una costante 128, mentre la tonalità e la saturazione variano. Nel secondo grafico, la saturazione è costante 255, mentre tonalità e luminosità variano.

Nel primo grafico, la luminosità è una costante 128, mentre tonalità e saturazione variano:

La saturazione è costante 255, mentre la tonalità e la luminosità variano:


8

Ecco la mia soluzione in Java per 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
È davvero 'perfetto'? Prova sfondo verde puro, # 00FF00.
Andreas Rejbrand,

È vero che questo non è testato per tutti i colori .... ma chi userebbe uno sfondo verde puro per tutto ciò che non è stato progettato per infastidire gli utenti?
mwieczorek,

Le persone di @mwieczorek che fanno affidamento su contenuti generati dagli utenti o su colori selezionati casualmente.
Marc Plano-Lesay,

4

Sulla base della risposta di @MarkRansom, ho creato uno script PHP che puoi trovare qui:

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

Questa è una versione rapida della risposta di Mark Ransom come estensione di 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

Questo è solo un esempio che cambierà il colore di un segno di spunta SVG quando si fa clic su un elemento. Imposta il colore del segno di spunta su bianco o nero in base al colore di sfondo dell'elemento selezionato.

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

Uso questa funzione JavaScript per convertire rgb/ rgbain 'white'o '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';
    }
}

È possibile inserire uno di questi formati e verrà visualizzato 'black'o'white'

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

Ora prova questo con uno sfondo verde puro: # 00FF00.
Andreas Rejbrand,

Grazie per questo! Tradotto in rapido e utilizzato nella mia app iOS!
Lucas P.

2

La risposta dettagliata di Mark funziona alla grande. Ecco un'implementazione in 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';
}

Quindi puoi chiamare questa funzione lum([111, 22, 255])per ottenere bianco o nero.


1

Non ho mai fatto nulla del genere, ma per quanto riguarda la scrittura di una funzione per verificare i valori di ciascuno dei colori rispetto al colore mediano di Hex 7F (FF / 2). Se due dei tre colori sono maggiori di 7F, stai lavorando con un colore più scuro.


1

Sulla base di input diversi dal link Rendi il colore di primo piano nero o bianco a seconda dello sfondo e di questo thread, ho creato una classe di estensione per Colore che ti fornisce i colori di contrasto richiesti.

Il codice va come di seguito:

 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

Se come me stavi cercando una versione RGBA che tenga conto dell'alfa, eccone una che funziona davvero bene per avere un contrasto elevato.

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

Questa è una versione R della risposta di Mark Ransom, usando solo la base R.

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, stavo guardando la tua risposta, che sembra buona, ma c'è un piccolo errore. La tua funzione hexToG e hextoB richiedono una modifica minore. L'ultimo numero nel substr è la lunghezza della stringa, quindi in questo caso dovrebbe essere "2", anziché 4 o 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

MENO ha una bella contrast()funzione che ha funzionato bene per me, vedi http://lesscss.org/functions/#color-operations-contrast

"Scegli quale dei due colori fornisce il massimo contrasto con un altro. Questo è utile per garantire che un colore sia leggibile su uno sfondo, che è anche utile per la conformità dell'accessibilità. Questa funzione funziona allo stesso modo della funzione di contrasto in Compass for SASS. In conformità con WCAG 2.0, i colori vengono confrontati utilizzando il loro valore di luminanza con correzione gamma, non la loro leggerezza. "

Esempio:

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%);
}

Produzione:

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

0

Dall'esagono al bianco o nero:

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

Codice versione Objective-c per iOS basato sulla risposta di 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

Che dire del test con tutti i colori a 24 bit?

Tenere presente che il metodo YIQ restituirà un rapporto di contrasto minimo di 1,9: 1, che non supera i test AAAG e AAA WCAG2.0, ipotizzando una soglia di 128.

Per il metodo W3C, restituirà un rapporto di contrasto minimo di 4,58: 1, che supera i test AA e AAA per testo di grandi dimensioni e test AA per testo piccolo, non supererà il test AAA per testo piccolo per tutti i colori.


0

Ecco il mio metodo che ho usato e con cui non ho avuto problemi finora 😄

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

Ecco il mio codice per Java Swing basato sulla straordinaria risposta di Mark:

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;
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.