2009-12-06 7 views
174

Dado un sistema (un sitio web, por ejemplo) que permite a un usuario personalizar el color de fondo para alguna sección, pero no el color de la fuente (para mantener el número de opciones a un mínimo), hay una manera de para determinar programáticamente si es necesario un color de letra "claro" u "oscuro"?Determinar la fuente de colores basada en el color de fondo

estoy seguro de que hay algún algoritmo, pero no saben lo suficiente acerca de los colores, la luminosidad, etc que averiguarlo por mi cuenta.

Respuesta

350

me encontré con un problema similar. Tenía que encontrar un buen método para seleccionar el color de fuente contrastivo para mostrar etiquetas de texto en escala de colores/mapas de calor. Tenía que ser un método universal y el color generado tenía que ser "atractivo", lo que significa que la simple generación de colores complementarios no era una buena solución; a veces generaba colores extraños, muy intensos, difíciles de ver y leer.

Después de largas horas de pruebas y tratando de resolver este problema, descubrí que la mejor solución es seleccionar la fuente blanca de colores "oscuras", y la fuente para los colores negro "brillantes".

Aquí está un ejemplo de la función que estoy usando en 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); 
} 

Esto fue probado para muchas diversas colorscales (arco iris, escala de grises, el calor, el hielo, y muchos otros) y es el único método "universal" Lo descubrí.

Editar
Un cambio en la fórmula de contar a a "luminancia perceptiva" - lo que realmente se ve mejor! Ya lo implementé en mi software, se ve genial.

Editar 2 @WebSeed proporciona un gran ejemplo de trabajo de este algoritmo: http://codepen.io/WebSeed/full/pvgqEq/

+10

probablemente no es importante, pero es posible que desee una mejor función para calcular el brillo http: // stackoverflow.com/preguntas/596216/fórmula para determinar-brillo-de-color RGB –

+0

Esto parece que va a ser perfecto. –

+0

mejorado el código - ahora es aún mejor – Gacek

0

Si está manipulando los espacios de color para el efecto visual es generalmente más fácil para trabajar en HSL (Tono, Saturación y Luminosidad) de RGB. Mover colores en RGB para dar efectos naturalmente agradables tiende a ser conceptualmente difícil, mientras que convertirlos en HSL, manipularlos allí, luego convertirlos de nuevo es más intuitivo en concepto e invariablemente da mejores resultados.

Wikipedia tiene un good introduction a HSL y HSV estrechamente relacionados. Y hay un código libre en la red para hacer la conversión (por ejemplo here is a javascript implementation)

Lo que la transformación precisa que usted utiliza es una cuestión de gustos, pero personalmente habría pensado invertir los componentes Hue y Lightness sin duda generaría un buen color de alto contraste como primera aproximación, pero puede elegir efectos más sutiles.

+1

Sí, pero considere también que el ojo humano puede ver el verde mucho más predominantemente que otros colores, y azul menos (por eso el azul obtiene menos bits de color en los formatos de imagen). – wchargin

+1

De hecho. Si vamos a estar en movimiento a HSL, podemos también dar el salto completo a YUV y tomar la percepción humana en cuenta. –

3

Gracias por este post.

Porque el que podría estar interesado, he aquí un ejemplo de esa función en 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

puede editar y actualizar su propia respuesta. – wmk

0

Usted puede tener cualquier texto tonalidad en cualquier fondo tonalidad y garantizar que sea legible. Lo hago todo el tiempo. Hay una fórmula para esto en Javascript en Readable Text in Colour – STW* Como se dice en ese enlace, la fórmula es una variación en el cálculo del ajuste de gamma inversa, aunque en mi humilde opinión un poco más manejable. Los menús en el lado derecho de ese enlace y sus páginas asociadas usan colores generados aleatoriamente para texto y fondo, siempre legibles. Entonces sí, claramente se puede hacer, no hay problema.

5

Sólo en caso de que alguien quisiera una versión más corta, tal vez más fácil de entender de 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: Quité la inversión del valor luma (para que los colores brillantes tienen un mayor valor, lo que parece más natural para mí y es también el método de cálculo 'por defecto'.

que utilizan las mismas constantes como GACEK de here ya que funcionaba muy bien para mí.

(También se puede aplicar esto como una Extension Method utilizando la siguiente firma:.

public static Color ContrastColor(this Color iColor) 

entonces le puede llamar a través de foregroundColor = background.ContrastColor())

2

Mi rápida aplicación de la respuesta de 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

En swift, ya que r/g/b son CGFloats, no necesita el "/ 255" para calcular la luminancia: deje luminancia = 1 - ((0.299 * r) + (0.587 * g) + (0.114 * b)) – SuperDuperTango

1

Ugly Python si no tiene ganas de escribirlo :)

''' 
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

Esta es una respuesta tan útil. ¡Gracias por eso!

Me gustaría compartir una versión 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); 

} 

Ahora encontrar la manera de utilizar el algoritmo para la creación automática de colores de la libración de los enlaces del menú. Los encabezados de luz obtienen un vuelo más oscuro y viceversa.

8

Gracias @Gacek. Aquí hay una versión para 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); 
} 

Y una versión mejorada (más corto):

@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

Sería aún más corto y más fácil de leer, si aplicas los cambios de @Marcus Mangelsdorf (elimina la parte '1 - ...' y cambia el nombre 'a' a' luminance' – Ridcully

+0

Usando la primera uno, podrías capturar alfa también –

2

iOS Swift 3.0 (extensión 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

As 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 variación Android que captura la alfa también.

(gracias @ 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); 
} 
Cuestiones relacionadas