web-dev-qa-db-fra.com

Comment choisir la couleur de la police en blanc ou noir en fonction de la couleur de fond?

Je veux montrer des images comme cet exemplealt text

La couleur de remplissage est déterminée par un champ dans la base de données avec la couleur hexadécimale (ex: ClassX -> Couleur: # 66FFFF) . Maintenant, je souhaite afficher les données au-dessus d’un remplissage avec la couleur image ci-dessus), mais j’ai besoin de savoir si la couleur est sombre ou claire, donc je sais si les mots doivent être en blanc ou en noir . Y at-il un moyen? tks

139
DJPB

Construire sur mon réponse à une question similaire .

Vous devez diviser le code hexadécimal en 3 morceaux pour obtenir les intensités individuelles de rouge, de vert et de bleu. Chacun des 2 chiffres du code représente une valeur en notation hexadécimale (base 16). Je ne vais pas entrer dans les détails de la conversion ici, ils sont faciles à rechercher.

Une fois que vous avez les intensités pour les couleurs individuelles, vous pouvez déterminer l’intensité globale de la couleur et choisir le texte correspondant.

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

Le seuil de 186 est basé sur la théorie, mais peut être ajusté au goût. Sur la base des commentaires, un seuil inférieur à 150 pourrait mieux fonctionner pour vous .


Edit: Ce qui précède est simple, fonctionne assez bien et semble être bien accepté ici, chez StackOverflow. Cependant, l'un des commentaires ci-dessous montre que cela peut entraîner une non-conformité aux directives du W3C dans certaines circonstances. Ici, je dérive une forme modifiée qui choisit toujours le contraste le plus élevé en fonction des directives. Si vous n'avez pas besoin de vous conformer aux règles du W3C, la formule plus simple ci-dessus resterait.

La formule donnée pour le contraste dans les Recommandations du W3C est (L1 + 0.05) / (L2 + 0.05), où L1 est la luminance de la couleur la plus claire et L2 est la luminance de la plus sombre sur une échelle de 0,0-1,0. La luminance du noir est de 0,0 et celle du blanc de 1,0, la substitution de ces valeurs vous permet donc de déterminer celle qui présente le contraste le plus élevé. Si le contraste pour le noir est supérieur au contraste pour le blanc, utilisez le noir, sinon utilisez le blanc. Étant donné la luminance de la couleur que vous testez en tant que L, le test devient:

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

Cela se simplifie algébriquement à:

if L > sqrt(1.05 * 0.05) - 0.05

Ou environ:

if L > 0.179 use #000000 else use #ffffff

La seule chose qui reste à faire est de calculer L. Cette formule est également donnée dans les instructions et elle ressemble à la conversion de sRGB en RVB linéaire suivie du recommandation UIT-R BT.709 pour la luminance.

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

Le seuil de 0,179 ne doit pas être modifié car il est lié aux directives du W3C. Si les résultats ne vous conviennent pas, essayez la formule plus simple ci-dessus.

245
Mark Ransom

Je ne prends aucun crédit pour ce code car ce n'est pas le mien, mais je le laisse ici pour que d'autres le découvrent rapidement:

Sur la base de la réponse de Mark Ransoms, voici un extrait de code pour la version 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;
}

et voici l'extrait de code pour la version avancée:

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

Pour les utiliser, il suffit d'appeler:

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

Merci également Alx et chetstone.

18
SudoPlz

Que diriez-vous de cela (code 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';
}
15
George Kagan

Voici ma solution en Java pour 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);
6
mwieczorek

En plus des solutions arithmétiques, il est également possible d’utiliser un réseau de neurones en IA. L’avantage est que vous pouvez l’adapter à vos goûts et à vos besoins (par exemple, un texte en blanc cassé sur des rouges saturés brillants a une belle apparence et est aussi lisible que le noir).

Voici une belle démo Javascript qui illustre le concept. Vous pouvez également générer votre propre formule JS directement dans la démo.

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

Vous trouverez ci-dessous des graphiques qui m'ont aidé à comprendre autour du problème . Dans le premier graphique, la luminosité est constante 128, tandis que la teinte et la saturation varient. Dans le deuxième graphique, la saturation est constante à 255, tandis que la teinte et la luminosité varient.

 In the first chart, lightness is a constant 128, while hue and saturation vary:

 Saturation is a constant 255, while hue and lightness vary:

4
Félix Ménard

Ceci est juste un exemple qui changera la couleur d'une coche SVG en cliquant sur un élément. Il va définir la couleur de coche en noir ou blanc en fonction de la couleur d'arrière-plan de l'élément cliqué.

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
davidcondrey

Basé sur la réponse de @MarkRansom, j'ai créé un script PHP que vous pouvez trouver ici:

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

Je n'ai jamais rien fait de tel, mais qu'en est-il de l'écriture d'une fonction pour vérifier les valeurs de chacune des couleurs par rapport à la couleur médiane de Hex 7F (FF/2). Si deux des trois couleurs sont supérieures à 7 ° F, vous travaillez avec une couleur plus sombre.

1
DJ Quimby

La réponse détaillée de Mark fonctionne très bien. Voici une implémentation 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';
}

Ensuite, vous pouvez appeler cette fonction lum([111, 22, 255]) pour obtenir du blanc ou du noir.

1
fireant

LESS a une fonction Nice contrast() qui a bien fonctionné pour moi, voir http://lesscss.org/functions/#color-operations-contrast

"Choisir laquelle des deux couleurs offre le plus grand contraste avec une autre . Ceci est utile pour garantir la lisibilité d'une couleur sur un fond, ce qui est également utile pour la conformité de l'accessibilité. Cette fonction fonctionne de la même manière que la fonction de contraste dans Compass pour SASS. Conformément à WCAG 2.0, les couleurs sont comparées en utilisant leur valeur de luma corrigée par les rayons gamma, pas leur légèreté. "

Exemple:

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

Sortie:

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

@ SoBiT, je regardais ta réponse, qui a l'air bien, mais il y a une petite erreur dedans. Votre fonction hexToG et hextoB nécessitent une modification mineure. Le dernier nombre dans substr est la longueur de la chaîne et, dans ce cas, il devrait être "2", plutôt que 4 ou 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
Patch

Ceci est une version Swift de la réponse de Mark Ransom en tant qu’extension 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
} }
0
Rydell

Code de version d'Objective-c pour iOS basé sur la réponse de 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
simonwjw

Sur la base de différentes entrées du lien Rendre la couleur d’avant-plan noire ou blanche en fonction de l’arrière-plan et ce fil, j’ai créé une classe d’extension pour Couleur qui vous donne les couleurs de contraste requises.

Le code va comme ci-dessous:

 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);
    }
}
0
Kasim Husaini

Qu'en est-il des tests avec toutes les couleurs 24 bits?

Sachez que la méthode YIQ renvoie un rapport de contraste minimal de 1,9: 1, qui ne réussit pas les tests AA et AAA WCAG2.0, en supposant un seuil de 128.

Pour la méthode W3C, un rapport de contraste minimal de 4,58: 1, répondant aux tests AA et AAA pour les textes volumineux, et au test AA pour les textes courts, ne passera pas le test AAA pour les textes de petite taille pour toutes les couleurs.

0
Phiphou

Voici ma propre méthode que j'utilise et que je n'ai pas eu de problème jusqu'à présent ????

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
Godstime Osarobo

De l'hex au noir ou blanc:

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
Dorian