2009-12-06 14 views
174

Dato un sistema (un sito Web ad esempio) che consente all'utente di personalizzare il colore di sfondo per alcune sezioni ma non il colore del carattere (per mantenere il numero di opzioni al minimo), c'è un modo determinare a livello di codice se è necessario un colore carattere "chiaro" o "scuro"?Determina il colore del carattere in base al colore di sfondo

Sono sicuro che c'è qualche algoritmo, ma non ne so abbastanza di colori, luminosità, ecc. Per capirlo da solo.

risposta

350

Ho riscontrato un problema simile. Ho dovuto trovare un buon metodo per selezionare il colore del carattere contrastivo per visualizzare etichette di testo su scale colori/mappe di calore. Doveva essere un metodo universale e il colore generato doveva essere "bello", il che significa che la semplice generazione di colori complementari non era una buona soluzione - a volte generava colori strani e molto intensi che erano difficili da guardare e leggere.

Dopo lunghe ore di test e cercando di risolvere questo problema, ho scoperto che la soluzione migliore è selezionare il carattere bianco per i colori "scuri" e il carattere nero per i colori "luminosi".

Ecco un esempio della funzione sto utilizzando in C#:

Color ContrastColor(Color color) 
{ 
    int d = 0; 

    // Counting the perceptive luminance - human eye favors green color... 
    double a = 1 - (0.299 * color.R + 0.587 * color.G + 0.114 * color.B)/255; 

    if (a < 0.5) 
     d = 0; // bright colors - black font 
    else 
     d = 255; // dark colors - white font 

    return Color.FromArgb(d, d, d); 
} 

Questo è stato testato per molti vari colorscales (arcobaleno, in scala di grigi, calore, ghiaccio, e molti altri) ed è l'unico metodo di "universale" Ho scoperto.

Modifica
cambiato la formula di contare a a "luminanza percettiva" - è davvero un aspetto migliore! Già implementato nel mio software, sembra fantastico.

Edit 2 @WebSeed fornito un grande esempio di lavoro di questo algoritmo: http://codepen.io/WebSeed/full/pvgqEq/

+10

Probabilmente non è importante, ma si potrebbe desiderare un migliore funzione per calcolare la luminosità http: // StackOverflow.it/questions/596216/formula-per-determinare-luminosità-di-colore-rgb –

+0

Sembra che sarà perfetto. –

+0

Migliorato il codice - ora è ancora meglio – Gacek

0

Se stai manipolando gli spazi colore per effetto visivo è generalmente più facile da lavorare in HSL (tonalità, saturazione e luminosità) di RGB. Spostare i colori in RGB per ottenere effetti piacevoli naturalmente tende ad essere abbastanza concettualmente difficili, mentre la conversione in HSL, la manipolazione in quel punto, la riconversione in uscita è più intuitiva e invariabilmente dà risultati migliori.

Wikipedia ha un good introduction in HSL e HSV strettamente correlato. E c'è il codice gratuito in rete per fare la conversione (ad esempio here is a javascript implementation)

Quale trasformazione precisa si usa è una questione di gusti, ma personalmente avrei pensato che invertire le componenti Tonalità e Luminosità sarebbe certo di generare un buon colore ad alto contrasto come prima approssimazione, ma puoi facilmente ottenere effetti più sottili.

+1

Sì, ma considera anche che l'occhio umano può vedere il verde molto più in modo dominante rispetto ad altri colori, e in blu meno (motivo per cui il blu ottiene meno bit di colore nei formati di immagine). – wchargin

+1

Infatti. Se ci trasferiremo su HSL, potremmo fare il salto completo a YUV e prendere in considerazione la percezione umana. –

3

Grazie per questo post.

Per chi potrebbe essere interessato, ecco un esempio di tale funzione in Delphi:

function GetContrastColor(ABGColor: TColor): TColor; 
var 
    ADouble: Double; 
    R, G, B: Byte; 
begin 
    if ABGColor <= 0 then 
    begin 
    Result := clWhite; 
    Exit; // *** EXIT RIGHT HERE *** 
    end; 

    if ABGColor = clWhite then 
    begin 
    Result := clBlack; 
    Exit; // *** EXIT RIGHT HERE *** 
    end; 

    // Get RGB from Color 
    R := GetRValue(ABGColor); 
    G := GetGValue(ABGColor); 
    B := GetBValue(ABGColor); 

    // Counting the perceptive luminance - human eye favors green color... 
    ADouble := 1 - (0.299 * R + 0.587 * G + 0.114 * B)/255; 

    if (ADouble < 0.5) then 
    Result := clBlack; // bright colors - black font 
    else 
    Result := clWhite; // dark colors - white font 
end; 
+0

è possibile modificare e aggiornare la propria risposta. – wmk

0

si può avere qualsiasi testo tonalità su qualsiasi sfondo tonalità e assicurarsi che sia leggibile. Lo faccio tutto il tempo. C'è una formula per questo in Javascript su Readable Text in Colour – STW* Come si dice su quel collegamento, la formula è una variazione sul calcolo di regolazione inversa-gamma, anche se un IMHO un po 'più gestibile. I menu sul lato destro di quel collegamento e le pagine associate utilizzano colori generati a caso per testo e sfondo, sempre leggibili. Quindi sì, chiaramente può essere fatto, nessun problema.

5

Solo nel caso qualcuno volesse una versione più breve, forse più facile da capire di GaceK's answer:

public Color ContrastColor(Color iColor) 
{ 
    // Calculate the perceptive luminance (aka luma) - human eye favors green color... 
    double luma = ((0.299 * iColor.R) + (0.587 * iColor.G) + (0.114 * iColor.B))/255; 

    // Return black for bright colors, white for dark colors 
    return luma > 0.5 ? Color.Black : Color.White; 
} 

Nota: ho rimosso l'inversione del valore luma (per rendere i colori brillanti hanno una maggiore valore, ciò che mi sembra più naturale ed è anche il metodo di calcolo "predefinito"

Ho utilizzato le stesse costanti di GaceK dal here poiché hanno funzionato perfettamente per me.

(è possibile anche implementare questo come un Extension Method utilizzando la seguente firma:.

public static Color ContrastColor(this Color iColor) 

È quindi possibile chiamare tramite foregroundColor = background.ContrastColor())

2

mio Swift implementazione della risposta di Gacek:

func contrastColor(color: UIColor) -> UIColor { 
    var d = CGFloat(0) 

    var r = CGFloat(0) 
    var g = CGFloat(0) 
    var b = CGFloat(0) 
    var a = CGFloat(0) 

    color.getRed(&r, green: &g, blue: &b, alpha: &a) 

    // Counting the perceptive luminance - human eye favors green color... 
    let luminance = 1 - ((0.299 * r) + (0.587 * g) + (0.114 * b)) 

    if luminance < 0.5 { 
     d = CGFloat(0) // bright colors - black font 
    } else { 
     d = CGFloat(1) // dark colors - white font 
    } 

    return UIColor(red: d, green: d, blue: d, alpha: a) 
} 
+0

In rapido, poiché r/g/b sono CGFloats, non è necessario il "/ 255" per calcolare la luminanza: let luminance = 1 - ((0,299 * r) + (0,587 * g) + (0,114 * b)) – SuperDuperTango

1

Python Ugly se non avete voglia di scriverlo :)

''' 
Input a string without hash sign of RGB hex digits to compute 
complementary contrasting color such as for fonts 
''' 
def contrasting_text_color(hex_str): 
    (r, g, b) = (hex_str[:2], hex_str[2:4], hex_str[4:]) 
    return '000' if 1 - (int(r, 16) * 0.299 + int(g, 16) * 0.587 + int(b, 16) * 0.114)/255 < 0.5 else 'fff' 
5

Questa è una risposta così utile. Grazie per questo!

Mi piacerebbe condividere una versione SCSS:

@function is-color-light($color) { 

    // Get the components of the specified color 
    $red: red($color); 
    $green: green($color); 
    $blue: blue($color); 

    // Compute the perceptive luminance, keeping 
    // in mind that the human eye favors green. 
    $l: 1 - (0.299 * $red + 0.587 * $green + 0.114 * $blue)/255; 
    @return ($l < 0.5); 

} 

Ora per capire come utilizzare l'algoritmo per creare automaticamente i colori hover per i link del menu. Le intestazioni chiare ottengono un hover più scuro e viceversa.

8

Grazie @Gacek. Ecco una versione per Android:

@ColorInt 
public static int getContrastColor(@ColorInt int color) { 
    // Counting the perceptive luminance - human eye favors green color... 
    double a = 1 - (0.299 * Color.red(color) + 0.587 * Color.green(color) + 0.114 * Color.blue(color))/255; 

    int d; 
    if (a < 0.5) { 
     d = 0; // bright colors - black font 
    } else { 
     d = 255; // dark colors - white font 
    } 

    return Color.rgb(d, d, d); 
} 

e una versione migliorata (più breve):

@ColorInt 
public static int getContrastColor(@ColorInt int color) { 
    // Counting the perceptive luminance - human eye favors green color... 
    double a = 1 - (0.299 * Color.red(color) + 0.587 * Color.green(color) + 0.114 * Color.blue(color))/255; 
    return a < 0.5 ? Color.BLACK : Color.WHITE; 
} 
+0

Sarebbe ancora più breve e più facile da leggere, se applicassi le modifiche di @Marcus Mangelsdorf (elimina la parte '1 - ...' e rinomina 'a' in' luminance' – Ridcully

+0

Utilizzo del primo uno, puoi anche catturare alfa. –

2

iOS Swift 3.0 (estensione UIColor):

func isLight() -> Bool 
{ 
    let components = self.cgColor.components 

    let firstComponent = ((components?[0])! * 299) 
    let secondComponent = ((components?[1])! * 587) 
    let thirdComponent = ((components?[2])! * 114) 
    let brightness = (firstComponent + secondComponent + thirdComponent)/1000 

    if brightness < 0.5 
    { 
     return false 
    } 
    else 
    { 
     return true 
    } 
} 
3

JavaScript [ES2015]

const hexToLuma = (colour) => { 
    const hex = colour.replace(/#/, ''); 
    const r  = parseInt(hex.substr(0, 2), 16); 
    const g  = parseInt(hex.substr(2, 2), 16); 
    const b  = parseInt(hex.substr(4, 2), 16); 

    return [ 
     0.299 * r, 
     0.587 * g, 
     0.114 * b 
    ].reduce((a, b) => a + b)/255; 
}; 
0

Come Kotlin/Android e xtension:

fun Int.getContrastColor(): Int { 
    // Counting the perceptive luminance - human eye favors green color... 
    val a = 1 - (0.299 * Color.red(this) + 0.587 * Color.green(this) + 0.114 * Color.blue(this))/255 
    return if (a < 0.5) Color.BLACK else Color.WHITE 
} 
0

Una variazione Android che cattura l'alfa pure.

(grazie @ Thomas-vos)

/** 
* Returns a colour best suited to contrast with the input colour. 
* 
* @param colour 
* @return 
*/ 
@ColorInt 
public static int contrastingColour(@ColorInt int colour) { 
    // XXX https://stackoverflow.com/questions/1855884/determine-font-color-based-on-background-color 

    // Counting the perceptive luminance - human eye favors green color... 
    double a = 1 - (0.299 * Color.red(colour) + 0.587 * Color.green(colour) + 0.114 * Color.blue(colour))/255; 
    int alpha = Color.alpha(colour); 

    int d = 0; // bright colours - black font; 
    if (a >= 0.5) { 
     d = 255; // dark colours - white font 
    } 

    return Color.argb(alpha, d, d, d); 
} 
Problemi correlati