Come verificare se il colore esadecimale è "troppo nero"?


111

Sto cercando di valutare l'oscurità di un colore scelto da un selettore di colori per vedere se è "troppo nero" e, in tal caso, impostarlo su bianco. Ho pensato di poter utilizzare i primi caratteri del valore esadecimale per ottenere questo risultato. Funziona, ma cambia anche alcuni colori legittimamente "chiari".

Ho un codice che fa questo:

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

Ci deve essere un modo più efficiente con la matematica esadecimale per sapere che un colore è andato oltre un certo livello di oscurità? Come se colore chiaro + "un valore esadecimale" <= "un valore esadecimale", impostalo su bianco.

Ho aggiunto tinyColor, che potrebbe essere utile per questo, ma non lo so per certo.


1
Hai provato ad aprire un selettore di colori e controllare i valori? Ho notato che quando R, G e B sono tutti sotto i ~ 70 diventa buio. Questo potrebbe non essere il modo corretto, ma lo è.
Rick Kuipers

1
Dato che stai già usando tinyColor, trasforma il colore in HSL e dai un'occhiata al componente L. 1 = bianco, 0 = nero
Andreas

4
@Andreas HSL la leggerezza non tiene conto della percezione umana. Un valore L di 0,5 avrà una diversa luminosità percepita per diverse tonalità.
Alnitak

1
@Alnitak Hai ragione ma la descrizione del TO non è esattamente quella. Quindi qualsiasi valore inferiore a 3/8 avrebbe potuto essere sufficientemente scuro per il suo scopo.
Andreas

1
@Andreas dipende - se guardi i valori di luminanza ITU nella mia risposta, vedrai che il blu è percepito come solo 1/10 della luminosità del verde.
Alnitak

Risposte:


226

È necessario estrarre i tre componenti RGB individualmente e quindi utilizzare una formula standard per convertire i valori RGB risultanti nella loro luminosità percepita.

Supponendo un colore di sei caratteri:

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
}

MODIFICARE

Da maggio 2014 tinycolorha ora una getBrightness()funzione, seppur utilizzando i fattori di ponderazione CCIR601 invece di quelli ITU-R sopra.

MODIFICARE

L'intervallo di valori della luminanza risultante è 0..255, dove 0 è il più scuro e 255 è il più chiaro. I valori maggiori di 128 sono considerati leggeri da tinycolor. (spudoratamente copiato dai commenti di @ pau.moreno e @Alnitak)


11
non vedo qualche buona manipolazione in javascript da un po '. roba forte. en.wikipedia.org/wiki/Rec._709#Luma_coefficients
jbabey

2
Buon codice, ma dopo aver testato suggerisco var luma = (r + g + b) / 3; if (luma <128) {// sarà più utile. }
Terry Lin

2
@TerryLin Why? I coefficienti forniti sono i valori ITU standard che tengono conto del fatto che il verde è percepito più brillantemente del rosso (e quindi del blu).
Alnitak

1
L' lumaintervallo di valori risultante è 0..255, dove 0è il più scuro ed 255è il più chiaro (i tre coefficienti si sommano a uno).
pau.moreno

1
@gabssnake solo da maggio 2014, e la isDark()soglia è hardcoded a 128
Alnitak

21

La libreria TinyColor (l'hai già menzionata) fornisce diverse funzioni per l'ispezione e la manipolazione dei colori, tra cui:


15

Ho trovato questa funzione PHP di WooCommerce Wordpress ( wc_hex_is_light ) e l'ho convertita in JavaScript. Funziona bene!

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
Fantastico, grazie! L'ho provato con diversi colori, il rilevamento era corretto con tutti :)
David Dal Busco

colorIsDarkOrLight (color) {var hex = color.replace ("#", ""); var c_r, c_g, c_b, luminosità = ""; 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); luminosità = (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); } restituisce luminosità> 155; },
Pedro Henrique

Usa con 3 caratteri esadecimali e 6
Pedro Henrique

6

Puoi calcolare la luminanza :

La luminanza è quindi un indicatore di quanto luminosa apparirà la superficie.

Quindi è fantastico scegliere se il testo deve essere bianco o nero.

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

Il metodo sopra ti consente di passare il colore in diversi formati, ma l'algoritmo è fondamentalmente solo in luminance_get.

Quando l'ho usato, stavo impostando il colore su nero se la luminanza era maggiore di 180, altrimenti bianco.


5

C'è un'importante distinzione qui tra luminanza e luminosità. La luminanza, alla fine della giornata, è una misura di quanta energia viaggia attraverso una certa area e ignora completamente il modo in cui i nostri sistemi percettivi percepiscono quell'energia. La luminosità, d'altra parte, è una misura di come percepiamo quell'energia e tiene conto della relazione tra la luminanza e il nostro sistema percettivo. (Come punto di confusione, c'è un termine chiamato luminanza relativa, che sembra essere usato come sinonimo di termini di luminosità. Mi ha fatto scattare bene).

Per essere precisi, stai cercando "luminosità" o "valore" o "luminanza relativamente" come altri hanno suggerito. Puoi calcolarlo in molti modi diversi (come essere umani!) Http://en.wikipedia.org/wiki/HSL_and_HSV#Lightness

  1. Prendi il massimo di R, G e B.
  2. Prendi la media del massimo e del minimo da R, G e B.
  3. Prendi la media di tutti e tre.
  4. Usa una media ponderata come altri hanno suggerito qui.

Per quanto ne so, solo il calcolo della luminanza descritto nella pagina di Wikipedia è un modello basato sulla percezione.
Alnitak

2
È bello sottolineare la distinzione tra l'energia fisica della luce e la luminosità percepita, ma penso che le cose siano piuttosto confuse. La sezione dell'articolo di Wikipedia a cui ti sei collegato ha un quarto punto elenco, che afferma "Un'alternativa più percettivamente rilevante è usare luma, Y ′, come dimensione della leggerezza" (enfasi mia) e quindi procede a dare la formula presentata in Alnitak e le risposte di Robin. In altre parole, il metodo che hai tralasciato e sconsigliato è quello che meglio corrisponde alla percezione umana.
John Y

@ JohnY sì, è quello che stavo cercando di dire - ha escluso l'unico che corrisponda effettivamente al resto della sua risposta.
Alnitak

Sì, è venuto fuori che ero l'unico confuso qui. Mi va bene :) Volevo solo far capire che c'è una differenza tra energia e percezione. Aggiornerò la mia risposta di conseguenza.
David Nguyen

4

Funziona con esadecimale, ad esempio #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;
}

Puoi cambiarlo per tornare trueo falseper cambio

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

per

return (yiq < 40);

@Vivek è perché è necessario utilizzare valori esadecimali completi (# 000000 e #ffffff)
TheCrazyProfessor

2

Una possibile soluzione sarebbe convertire il tuo colore da RGB a HSB . HSB sta per tonalità, saturazione e luminosità (noto anche come HSV, dove V sta per valore). Allora hai solo un parametro da controllare: la luminosità.


1

Mi rendo conto che questa conversazione risale a pochi anni fa, ma è ancora rilevante. Volevo aggiungere che il mio team stava riscontrando lo stesso problema in Java (SWT) e l'ho trovato un po 'più preciso:

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