162 votes

Changer la couleur du texte en fonction de la luminosité de la zone d'arrière-plan couverte ?

Je suis à la recherche d'un plugin ou d'une technique qui change la couleur d'un texte ou bascule entre des images/icônes prédéfinies en fonction de la luminosité moyenne des pixels couverts de l'image ou de la couleur d'arrière-plan de son parent.

Si la zone couverte par son fond est plutôt sombre, rendez le texte blanc ou changez les icônes.

De plus, ce serait génial si le script remarquait si le parent n'a pas de couleur de fond ou d'image définie et continuait ensuite à chercher la plus proche (de l'élément parent à son élément parent ).

Que pensez-vous, que savez-vous de cette idée ? Existe-t-il déjà quelque chose de similaire ? Des exemples ?

1 votes

Juste une pensée plutôt qu'une réponse. Il y a peut-être un moyen de définir vos couleurs en utilisant HSL puis en regardant la valeur de luminosité. Si cette valeur est supérieure à une certaine valeur, appliquez une règle css.

1 votes

On pourrait concevoir d'analyser la couleur d'arrière-plan d'un élément en valeurs R, G, B (et alpha facultatif), en remontant l'arbre DOM si le canal alpha est défini sur zéro. Cependant, essayer de déterminer la couleur d'une image d'arrière-plan est une toute autre affaire.

0 votes

6voto

Luke Robertson Points 179

Si vous utilisez l'ES6, convertissez les hexagones en RGB puis utilisez ceci :

const hexToRgb = hex => {
    // turn hex val to RGB
    const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex)
    return result
        ? {
              r: parseInt(result[1], 16),
              g: parseInt(result[2], 16),
              b: parseInt(result[3], 16)
          }
        : null
}

// calc to work out if it will match on black or white better
const setContrast = rgb =>
    (rgb.r * 299 + rgb.g * 587 + rgb.b * 114) / 1000 > 125 ? 'black' : 'white'

const getCorrectColor = setContrast(hexToRgb(#ffffff))

6voto

A. El-zahaby Points 469

En combinant les réponses [ @alex-ball , @jeremyharris ] j'ai trouvé que c'était la meilleure solution pour moi :

        $('.elzahaby-bg').each(function () {
            var rgb = $(this).css('backgroundColor');
            var colors = rgb.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/);

            var r = colors[1];
            var g = colors[2];
            var b = colors[3];

            var o = Math.round(((parseInt(r) * 299) + (parseInt(g) * 587) + (parseInt(b) * 114)) /1000);

            if(o > 125) {
                $(this).css('color', 'black');
            }else{
                $(this).css('color', 'white');
            }
        });

*{
    padding: 9px;
}

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.0/jquery.min.js"></script>
<div class='elzahaby-bg' style='background-color:#000'>color is white</div>

<div class='elzahaby-bg' style='background-color:#fff'>color is black</div>
<div class='elzahaby-bg' style='background-color:yellow'>color is black</div>
<div class='elzahaby-bg' style='background-color:red'>color is white</div>

4voto

Nathan MacInnes Points 6749

Voici ma tentative :

(function ($) {
    $.fn.contrastingText = function () {
        var el = this,
            transparent;
        transparent = function (c) {
            var m = c.match(/[0-9]+/g);
            if (m !== null) {
                return !!m[3];
            }
            else return false;
        };
        while (transparent(el.css('background-color'))) {
            el = el.parent();
        }
        var parts = el.css('background-color').match(/[0-9]+/g);
        this.lightBackground = !!Math.round(
            (
                parseInt(parts[0], 10) + // red
                parseInt(parts[1], 10) + // green
                parseInt(parts[2], 10) // blue
            ) / 765 // 255 * 3, so that we avg, then normalize to 1
        );
        if (this.lightBackground) {
            this.css('color', 'black');
        } else {
            this.css('color', 'white');
        }
        return this;
    };
}(jQuery));

Puis de l'utiliser :

var t = $('#my-el');
t.contrastingText();

Cela rendra immédiatement le texte noir ou blanc, selon le cas. Pour faire les icônes :

if (t.lightBackground) {
    iconSuffix = 'black';
} else {
    iconSuffix = 'white';
}

Chaque icône pourrait alors ressembler à 'save' + iconSuffix + '.jpg' .

Notez que cela ne fonctionnera pas si un conteneur déborde de son parent (par exemple, si la hauteur CSS est de 0 et que le débordement n'est pas masqué). Il serait beaucoup plus complexe de faire fonctionner cette fonction.

4voto

vanowm Points 313

En es6, le contraste d'une chaîne de couleurs à 6 caractères HEX (#123456) peut être calculé avec cette seule ligne :

const contrastColor = c=>["black","white"][~~([1,3,5].map(p=>parseInt(c.substr(p,2),16)).reduce((r,v,i)=>[.299,.587,.114][i]*v+r,0)<128)];

Voici la version décomposée et lisible :

const contrastColor = color =>
{
  const lum = [1,3,5].map(pos => //get RGB colors array from the string at positions 1, 3 and 5 (0 = # character)
  {
    // return converted hex value into decimal for each R, G, and B
    return parseInt(color.substr(pos, 2), 16);
  }).reduce((result, value, index) =>
  {
    // with reduce() we can convert an array of numbers into a single number
    // result = previous result returned by this function
    // value = Red, Green or Blue value of the color
    // index = current position index in the array
    // y = https://www.w3.org/TR/AERT/#color-contrast

    const y = [.299 /*red*/,.587 /*green*/,.114 /*blue*/][index];
    return result + y * value // return sum of previous results
  }
  , 0 /* result = 0 */);

  const isDark = lum < 128;
  const index = ~~isDark; // convert boolean into 0 or 1
  return ["black", "white"][index];
}

function setColors()
{

  for(let i = 0; i < 70; i++)
  {
    const bg = "#" + (~~(Math.random() * 16777216)).toString(16).padStart(6, 0),
          color = contrastColor(bg);
    node = test.children[i] || document.createElement("span");
    node.style.backgroundColor = bg;
    node.style.color = color;
    node.textContent = bg;
    if (!node.parentNode)
      test.appendChild(node);
  }
}

setColors();

#test
{
  display: flex;
  flex-wrap: wrap;
  font-family: monospace;
}
#test > *
{
  padding: 0.3em;
}

<button id="click" onclick="setColors()">change</button>
<div id="test"></div>

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X