Textfarbe basierend auf der Helligkeit des abgedeckten Hintergrundbereichs ändern?

114

Ich habe bereits eine Weile über Folgendes nachgedacht, daher möchte ich jetzt Ihre Meinungen, mögliche Lösungen usw. kennen.

Ich suche nach einem Plugin oder einer Technik, die die Farbe eines Textes ändert oder zwischen vordefinierten Bildern / Symbolen wechselt, abhängig von der durchschnittlichen Helligkeit der abgedeckten Pixel des Hintergrundbilds oder der Farbe des Elternteils.

Wenn der abgedeckte Bereich des Hintergrunds eher dunkel ist, machen Sie den Text weiß oder wechseln Sie die Symbole.

Darüber hinaus wäre es großartig, wenn das Skript feststellen würde, ob das übergeordnete Element keine definierte Hintergrundfarbe oder kein definiertes Bild hat, und dann weiter nach dem nächstgelegenen suchen würde (vom übergeordneten Element zum übergeordneten Element).

Was denkst du, weißt du über diese Idee? Gibt es da draußen schon etwas Ähnliches? Skriptbeispiele?

Cheers, J.

James Cazzetta
quelle
1
Nur ein Gedanke statt eine Antwort. Möglicherweise gibt es eine Möglichkeit, Ihre Farben mithilfe von HSL einzustellen und dann den Helligkeitswert zu überprüfen. Wenn dieser Wert über einem bestimmten Wert liegt, wenden Sie eine CSS-Regel an.
Scott Brown
1
Es ist denkbar, dass Sie die Hintergrundfarbe eines Elements in R-, G-, B- (und optionale Alpha-) Werte zerlegen und den DOM-Baum aufarbeiten, wenn der Alpha-Kanal auf Null gesetzt ist. Der Versuch, die Farbe eines Hintergrundbildes zu bestimmen, ist jedoch eine ganz andere Sache.
Jackwanders
@Pascal Ganz ähnlich und gute Eingabe .. aber es ist nicht die genaue Antwort auf meine Frage.
James Cazzetta

Antworten:

175

Interessante Ressourcen dafür:

Hier ist der W3C-Algorithmus (auch mit JSFiddle-Demo ):

const rgb = [255, 0, 0];

// Randomly change to showcase updates
setInterval(setContrast, 1000);

function setContrast() {
  // Randomly update colours
  rgb[0] = Math.round(Math.random() * 255);
  rgb[1] = Math.round(Math.random() * 255);
  rgb[2] = Math.round(Math.random() * 255);

  // http://www.w3.org/TR/AERT#color-contrast
  const brightness = Math.round(((parseInt(rgb[0]) * 299) +
                      (parseInt(rgb[1]) * 587) +
                      (parseInt(rgb[2]) * 114)) / 1000);
  const textColour = (brightness > 125) ? 'black' : 'white';
  const backgroundColour = 'rgb(' + rgb[0] + ',' + rgb[1] + ',' + rgb[2] + ')';
  $('#bg').css('color', textColour); 
  $('#bg').css('background-color', backgroundColour);
}
#bg {
  width: 200px;
  height: 50px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<div id="bg">Text Example</div>

Alex Ball
quelle
Felix du hast recht! Ich bin jetzt weg, aber sicher, wenn ich zurückkomme, werde ich die Antwort aktualisieren
Alex Ball
1
Kann auf Folgendes kurzgeschlossen werden, vorausgesetzt, Sie übergeben ihm ein Objekt :::: const setContrast = rgb => (rgb.r * 299 + rgb.g * 587 + rgb.b * 114) / 1000> 125? 'schwarz': 'weiß'
Luke Robertson
Benötigen Sie nur eine Abfrage, um das CSS zu ändern?
Bluejayke
@bluejayke nein, es gibt andere Möglichkeiten ;-)
Alex Ball
62

Dieser Artikel über 24 Möglichkeiten zur Berechnung des Farbkontrasts könnte für Sie von Interesse sein. Ignorieren Sie die ersten Funktionen, da sie falsch sind. Mit der YIQ-Formel können Sie jedoch feststellen, ob Sie eine helle oder dunkle Vordergrundfarbe verwenden möchten.

Sobald Sie die Hintergrundfarbe des Elements (oder des Vorfahren) erhalten haben, können Sie diese Funktion aus dem Artikel verwenden, um eine geeignete Vordergrundfarbe zu bestimmen:

function getContrastYIQ(hexcolor){
    hexcolor = hexcolor.replace("#", "");
    var r = parseInt(hexcolor.substr(0,2),16);
    var g = parseInt(hexcolor.substr(2,2),16);
    var b = parseInt(hexcolor.substr(4,2),16);
    var yiq = ((r*299)+(g*587)+(b*114))/1000;
    return (yiq >= 128) ? 'black' : 'white';
}
Cyang
quelle
Danke, das ist wirklich hilfreich. Dies hängt von der eingestellten Hintergrundfarbe ab. Aber wissen Sie, wie Sie die durchschnittliche Farbe eines Bildes erhalten, indem Sie durch jedes Pixel laufen (wie in einer Schleife)?
James Cazzetta
4
In es6 können Sie dies tun mit:const getContrastYIQ = hc => { const [r, g, b] = [0, 2, 4].map( p => parseInt( hc.substr( p, 2 ), 16 ) ); return ((r * 299) + (g * 587) + (b * 114)) / 1000 >= 128; }
Centril
Ich habe diese Funktion übernommen und ein wenig erweitert, sodass Sie zwei benutzerdefinierte Farben anstelle von immer Schwarzweiß zurückgeben können. Beachten Sie, dass, wenn die Farben zwei nahe beieinander liegen, möglicherweise immer noch Kontrastprobleme auftreten. Dies ist jedoch eine gute Alternative zur Rückgabe absoluter Farben. Jsfiddle.net/1905occv/1
Hanna
1
Dieser ist coo, ich würde einfach den yiq auf> = 160 einstellen, hat besser für mich funktioniert.
Arturo
15

Interessante Frage. Mein unmittelbarer Gedanke war, die Farbe des Hintergrunds als Text umzukehren. Dazu müssen Sie lediglich den Hintergrund analysieren und seinen RGB-Wert invertieren.

So etwas wie das: http://jsfiddle.net/2VTnZ/2/

var rgb = $('#test').css('backgroundColor');
var colors = rgb.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/);
var brightness = 1;

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

var ir = Math.floor((255-r)*brightness);
var ig = Math.floor((255-g)*brightness);
var ib = Math.floor((255-b)*brightness);

$('#test').css('color', 'rgb('+ir+','+ig+','+ib+')');
Jeremyharris
quelle
Sie möchten wahrscheinlich Ihre "invertierte" Farbe entsättigen, indem Sie die invertierten R-, G-, B-Werte mitteln und sie gleich einstellen. Diese Lösung bezieht ihre Grundfarbe jedoch aus einer Zeichenfolge und nicht aus der CSS-Eigenschaft des Elements. Um zuverlässig zu sein, müsste die Lösung dynamisch Hintergrundfarben erhalten, die normalerweise rgb () - oder rgba () -Werte zurückgeben, sich jedoch je nach Browser unterscheiden können.
Jackwanders
Ja. Um das Parsen zu vereinfachen, habe ich nur einen Hex-Wert verwendet. Ich habe die Geige aktualisiert, um die Farbe des Elements aus dem CSS zu übernehmen. Ich habe die Geige aktualisiert und eine Art Helligkeitsregelung hinzugefügt (ich weiß nichts über Farbmathematik, daher ist es wahrscheinlich nicht wirklich Helligkeit).
Jeremyharris
1
Wie wäre es damit? stackoverflow.com/questions/2541481/…
jeremyharris
2
Was ist, wenn die Hintergrundfarbe ist #808080!?
Nathan MacInnes
1
@NathanMacInnes es wird es immer noch invertieren, es kommt einfach so vor, dass das Invertieren von etwas genau in der Mitte des Spektrums zu sich selbst führt. Dieser Code invertiert nur die Farbe, was mit seinen Einschränkungen verbunden ist.
Jeremyharris
9

mix-blend-mode macht den Trick:

header {
  overflow: hidden;
  height: 100vh;
  background: url(https://www.w3schools.com/html/pic_mountain.jpg) 50%/cover;
}

h2 {
  color: white;
  font: 900 35vmin/50vh arial;
  text-align: center;
  mix-blend-mode: difference;
  filter: drop-shadow(0.05em 0.05em orange);
}
<header>
  <h2 contentEditable role='textbox' aria-multiline='true' >Edit me here</h2>
</header>

Ergänzung (März 2018): Im Folgenden finden Sie ein nettes Tutorial, in dem alle Arten von Modi / Implementierungen erläutert werden: https://css-tricks.com/css-techniques-and-effects-for-knockout-text/

James Cazzetta
quelle
5

Ich habe festgestellt, dass das BackgroundCheck- Skript sehr nützlich ist.

Es erkennt die Gesamthelligkeit des Hintergrunds (sei es ein Hintergrundbild oder eine Farbe) und wendet abhängig von der Helligkeit des Hintergrunds eine Klasse auf das zugewiesene Textelement ( background--lightoder background--dark) an.

Es kann auf Standbilder und bewegliche Elemente angewendet werden.

( Quelle )

cptstarling
quelle
Danke für den Beitrag!
James Cazzetta
Funktioniert das für Hintergrundfarben? Ich habe das Skript schnell gelesen und kann nicht sehen, dass es die Hintergrundfarbe verwendet, um die Helligkeit zu überprüfen. Nur Bilder.
Jørgen Skår Fischer
1
Hallo Jørgen, ich denke, das colourBrightness-Skript könnte Ihren Zweck erfüllen: github.com/jamiebrittain/colourBrightness.js
cptstarling
5

Wenn Sie ES6 verwenden, konvertieren Sie hexadezimal in RGB und können Folgendes verwenden:

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))
Luke Robertson
quelle
4

Hier ist mein Versuch:

(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));

Dann, um es zu benutzen:

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

Dadurch wird der Text sofort entweder schwarz oder weiß. So machen Sie die Symbole:

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

Dann könnte jedes Symbol so aussehen 'save' + iconSuffix + '.jpg'.

Beachten Sie, dass dies nicht funktioniert, wenn ein Container seinen übergeordneten Container überläuft (z. B. wenn die CSS-Höhe 0 ist und der Überlauf nicht ausgeblendet ist). Das zum Laufen zu bringen wäre viel komplexer.

Nathan MacInnes
quelle
0

Durch die Kombination der Antworten [@ alex-ball, @jeremyharris] fand ich, dass dies der beste Weg für mich ist:

        $('.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>

A. El-zahaby
quelle