Comment vérifier si la couleur hexadécimale est «trop noire»?


111

J'essaie d'évaluer l'obscurité d'une couleur choisie par un sélecteur de couleur pour voir si elle est "trop ​​noire", et si oui, réglez-la sur blanc. J'ai pensé que je pourrais utiliser les premiers caractères de la valeur hexadécimale pour retirer cela. Cela fonctionne, mais cela change également certaines couleurs légitimement «claires».

J'ai du code pour faire ceci:

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

Il doit y avoir un moyen plus efficace avec les mathématiques hexadécimales de savoir qu'une couleur a dépassé un certain niveau d'obscurité? Comme si lightcolor + "une valeur hexadécimale" <= "une valeur hexadécimale", réglez-le sur blanc.

J'ai ajouté tinyColor, qui pourrait être utile pour cela, mais je ne sais pas avec certitude.


1
Avez-vous essayé de créer un sélecteur de couleurs et de vérifier les valeurs? J'ai remarqué que lorsque R, G et B ont tous moins de 70 ans, il fait noir. Ce n'est peut-être pas la bonne façon, mais c'est une.
Rick Kuipers

1
Comme vous utilisez déjà tinyColor, transformez la couleur en HSL et regardez le composant L. 1 = blanc, 0 = noir
Andreas

4
La légèreté @Andreas HSL ne prend pas en compte la perception humaine. Une valeur L de 0,5 aura une luminosité perçue différente pour différentes teintes.
Alnitak

1
@Alnitak Vous avez raison, mais la description du TO n'est pas si précise. Ainsi, toute valeur inférieure à 3/8 aurait pu être suffisamment sombre pour son objectif.
Andreas

1
@Andreas qui dépend - si vous regardez les valeurs de luminance de l'UIT dans ma réponse, vous verrez que le bleu est perçu comme seulement 1 / 10ème aussi brillant que le vert.
Alnitak

Réponses:


226

Vous devez extraire les trois composants RVB individuellement, puis utiliser une formule standard pour convertir les valeurs RVB résultantes en leur luminosité perçue.

En supposant une couleur à six caractères:

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
}

ÉDITER

Depuis mai 2014, a tinycolormaintenant une getBrightness()fonction, bien que utilisant les facteurs de pondération CCIR601 au lieu de ceux de l'UIT-R ci-dessus.

ÉDITER

La plage de valeurs de luminance résultante est comprise entre 0 et 255, où 0 est le plus sombre et 255 le plus clair. Les valeurs supérieures à 128 sont considérées comme légères par tinycolor. (copié sans vergogne des commentaires de @ pau.moreno et @Alnitak)


12
Je n'ai pas vu de bonnes manipulations en javascript depuis un moment. truc cool. en.wikipedia.org/wiki/Rec._709#Luma_coefficients
jbabey

2
Bon code, mais après avoir testé, je suggère var luma = (r + g + b) / 3; if (luma <128) {// sera plus utile. }
Terry Lin

2
@TerryLin Pourquoi? Les coefficients donnés sont les valeurs standard de l' UIT qui tiennent compte du fait que le vert est perçu plus vivement que le rouge (puis le bleu).
Alnitak du

1
La lumaplage de valeurs résultante est 0..255, où 0est le plus sombre et 255est le plus clair (les trois coefficients totalisent un).
pau.moreno

1
@gabssnake uniquement depuis mai 2014, et le isDark()seuil est codé en dur à 128
Alnitak

21

La bibliothèque TinyColor (vous l'avez déjà mentionnée) fournit plusieurs fonctions pour inspecter et manipuler les couleurs, parmi lesquelles:


15

J'ai trouvé cette fonction PHP WooCommerce Wordpress ( wc_hex_is_light ) et je l'ai convertie en JavaScript. Fonctionne très bien!

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
Super cool, merci! Je l'ai testé avec plusieurs couleurs, la détection était correcte avec toutes :)
David Dal Busco

colorIsDarkOrLight (couleur) {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); } retourne la luminosité> 155; },
Pedro Henrique

Utiliser avec hex 3 caractères et 6
Pedro Henrique

6

Vous pouvez calculer la luminance :

La luminance est donc un indicateur de la luminosité de la surface.

C'est donc génial de choisir si le texte doit être blanc ou noir.

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

La méthode ci-dessus vous permet de transmettre la couleur dans différents formats, mais l'algorithme est fondamentalement juste dedans luminance_get.

Quand je l'ai utilisé, je réglais la couleur sur noir si la luminance était supérieure à 180, blanc sinon.


5

Il y a ici une distinction importante entre la luminance et la luminosité. La luminance, en fin de compte, est une mesure de la quantité d'énergie qui traverse une certaine zone et ignore complètement la façon dont nos systèmes perceptifs perçoivent cette énergie. La luminosité, en revanche, est une mesure de la façon dont nous percevons cette énergie et prend en compte la relation entre la luminance et notre système perceptif. (Comme point de confusion, il y a un terme appelé luminance relative, qui semble être utilisé comme synonyme de termes de luminosité. Cela m'a bien fait trébucher).

Pour être précis, vous recherchez "luminosité" ou "valeur" ou "luminance relative" comme d'autres l'ont suggéré. Vous pouvez le calculer de plusieurs manières différentes (comme être humain!) Http://en.wikipedia.org/wiki/HSL_and_HSV#Lightness

  1. Prenez le maximum de R, G et B.
  2. Prenez la moyenne du max et du min de R, G et B.
  3. Prenez la moyenne des trois.
  4. Utilisez une moyenne pondérée comme d'autres l'ont suggéré ici.

AFAIK seul le luma calcul décrit sur la page Wikipedia est un modèle basé sur la perception.
Alnitak

2
C'est bien de souligner la distinction entre l'énergie lumineuse physique et la luminosité perçue, mais je pense que les choses sont plutôt mélangées. La section de l'article de Wikipédia à laquelle vous avez lié a un quatrième point, qui déclare: "Une alternative plus pertinente pour la perception est d'utiliser luma, Y ′, comme dimension de légèreté" (c'est moi qui souligne), puis donne la formule présentée dans Alnitak. et les réponses de Robin. En d'autres termes, la méthode que vous avez laissée de côté et que vous avez recommandée est celle qui correspond le mieux à la perception humaine.
John Y

@JohnY oui, c'est ce que j'essayais de dire - il a laissé de côté le seul qui correspond réellement au reste de sa réponse.
Alnitak

Oui, il s'avère que j'étais le seul à être confus ici. Je suis d'accord avec ça :) Je voulais juste faire comprendre qu'il y a une différence entre l'énergie et la perception. Je mettrai à jour ma réponse en conséquence.
David Nguyen

4

Cela fonctionne avec hex par exemple #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;
}

Vous pouvez le modifier en retour trueou falseen le modifiant

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

à

return (yiq < 40);

@Vivek c'est parce que vous devez utiliser des valeurs hexadécimales complètes (# 000000 et #ffffff)
TheCrazyProfessor

2

Une solution possible serait de convertir votre couleur de RVB en TSL . HSB signifie teinte, saturation et luminosité (également connu sous le nom de HSV, où V est la valeur). Ensuite, vous n'avez qu'un seul paramètre à vérifier: la luminosité.


1

Je me rends compte que cette conversation date de quelques années, mais elle est toujours d'actualité. Je voulais ajouter que mon équipe avait le même problème en Java (SWT) et j'ai trouvé que c'était un peu plus précis:

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;
    }
}
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.