¿Cómo decidir el color de la fuente en blanco o negro dependiendo del color de fondo?

Quiero mostrar algunas imágenes como este ejemplo texto alternativo

El color de relleno se decide por un campo en la base de datos con el color en hexadecimal (por ejemplo: ClaseX -> Color: # 66FFFF). Ahora, quiero mostrar los datos sobre un relleno con el color seleccionado (como en la imagen de arriba), pero necesito saber si el color es oscuro o claro, así que sé si las palabras deben ser blancas o negras. ¿Hay alguna manera? tks

Basándome en mi respuesta a una pregunta similar .

Debes dividir el código hexadecimal en 3 partes para obtener las intensidades individuales de rojo, verde y azul. Cada 2 dígitos del código representan un valor en notación hexadecimal (base-16). No entraré en los detalles de la conversión aquí, son fáciles de buscar.

Una vez que tenga las intensidades para los colores individuales, puede determinar la intensidad general del color y elegir el texto correspondiente.

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

Editar: Lo anterior es simple y funciona razonablemente bien, y parece tener una buena aceptación aquí en StackOverflow. Sin embargo, uno de los comentarios a continuación muestra que puede conducir al incumplimiento de las pautas W3C en algunas circunstancias. Con esto derivo una forma modificada que siempre elige el contraste más alto basado en las pautas.

La fórmula dada para el contraste en las Recomendaciones W3C es (L1 + 0.05) / (L2 + 0.05) , donde L1 es la luminancia del color más claro y L2 es la luminancia más oscura en una escala de 0.0-1.0. La luminancia del negro es 0.0 y el blanco es 1.0, por lo que sustituir esos valores le permite determinar el que tiene el mayor contraste. Si el contraste para negro es mayor que el contraste para blanco, use negro, de lo contrario use blanco. Dada la luminancia del color que está probando como L la prueba se convierte en:

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

Esto simplifica algebraicamente a:

 if L > sqrt(1.05 * 0.05) - 0.05 

O aproximadamente:

 if L > 0.179 use #000000 else use #ffffff 

Lo único que queda es calcular L Esa fórmula también se da en las directrices y parece la conversión de sRGB a RGB lineal seguido de la recomendación ITU-R BT.709 para luminancia.

 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 

¿Qué tal esto (código de 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'; } 

No me atribuyo el mérito de este código ya que no es mío, pero lo dejo aquí para que otros puedan encontrarlo rápidamente en el futuro:

Según la respuesta de Mark Ransoms, aquí hay un fragmento de código para la versión simple:

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

y aquí está el fragmento de código para la versión avanzada:

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

Para usarlos solo llame:

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

Además, gracias Alx y chetstone .

Aquí está mi solución en Java para 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); 

Además de las soluciones aritméticas, también es posible utilizar una neural network AI. La ventaja es que puede adaptarlo a su gusto y necesidades (es decir, el texto blanco apagado en rojos satinados shinys se ve bien y es tan legible como negro).

Aquí hay una demo de JavaScript ordenada que ilustra el concepto. También puede generar su propia fórmula JS directamente en la demostración.

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

A continuación se encuentran algunos cuadros que me ayudaron a resolver el problema . En el primer gráfico, la luminosidad es una constante de 128, mientras que el tono y la saturación varían. En el segundo gráfico, la saturación es una constante de 255, mientras que el tono y la luminosidad varían.

En el primer gráfico, la luminosidad es una constante de 128, mientras que el tono y la saturación varían:

La saturación es una constante de 255, mientras que el tono y la luminosidad varían:

Este es solo un ejemplo que cambiará el color de una marca de verificación SVG al hacer clic en un elemento. Establecerá el color de la marca de verificación en negro o blanco en función del color de fondo del elemento cliqueado.

 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

Uso esta función de JavaScript para convertir rgb / rgba en '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'; } } 

Puede ingresar cualquiera de estos formatos y obtendrá '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

Basado en la respuesta de @MarkRansom, creé un script PHP que puedes encontrar aquí:

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

Nunca he hecho algo como esto, pero ¿qué hay de escribir una función para verificar los valores de cada uno de los colores contra el color mediano de Hex 7F (FF / 2). Si dos de los tres colores son mayores que 7F, entonces estás trabajando con un color más oscuro.

La respuesta detallada de Marcos funciona muy bien. Aquí hay una implementación en 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'; } 

Entonces puede llamar a esta función lum([111, 22, 255]) para obtener blanco o negro.

@SoBiT, estaba viendo tu respuesta, que se ve bien, pero hay un pequeño error. Su función hexToG y hextoB necesitan una edición menor. El último número en substr es la longitud de la cadena, por lo que en este caso debería ser “2”, en lugar de 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)); } 

MENOS tiene una buena función de contrast() que funcionó bien para mí, ver http://lesscss.org/functions/#color-operations-contrast

“Elija cuál de los dos colores proporciona el mayor contraste con otro. Esto es útil para garantizar que un color sea legible sobre un fondo, lo que también es útil para el cumplimiento de accesibilidad. Esta función funciona de la misma manera que la función de contraste en Compass for SASS. De acuerdo con WCAG 2.0, los colores se comparan utilizando su valor de luma corregido gamma, no su ligereza “.

Ejemplo:

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

Salida:

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

De hexágono a negro o blanco:

 function hexToRgb(hex) { var result = /^#?([af\d]{2})([af\d]{2})([af\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"; } 

Código de la versión Objective-c para iOS basado en la respuesta de Mark:

 - (UIColor *)contrastForegroundColor { CGFloat red = 0, green = 0, blue = 0, alpha = 0; [self getRed:&red green:&green blue:&blue alpha:&alpha]; NSArray *rgbArray = @[@(red), @(green), @(blue)]; NSMutableArray *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; } 

Esta es una versión rápida de la respuesta de Mark Ransom como una extensión de 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 } }