Wie kann das Kontrastverhältnis zwischen zwei Farben programmgesteuert berechnet werden?


75

Ziemlich einfach, nimm gelb und weiß:

back_color = {r:255,g:255,b:255}; //white
text_color = {r:255,g:255,b:0}; //yellow

Welches Gesetz der Physik auf Gottes Erde der universellen Konstanten macht die Tatsache aus, dass gelber Text nicht auf weißem Hintergrund gelesen werden kann, blauer Text jedoch?

Für mein anpassbares Widget habe ich alle möglichen Farbmodelle ausprobiert, für die ich Konvertierungsfunktionen gefunden habe. Keiner kann sagen, dass Grün auf Weiß sein kann und Gelb nicht, basierend auf nur numerischen Vergleichen.

Ich habe mir Adsense angesehen (das von der Budda des gesamten Internets erstellt wurde) und erraten, was sie getan haben. Sie haben Voreinstellungen und Berechnungen der Entfernung von Farbzellen vorgenommen. Das kann ich nicht machen. Meine Benutzer haben das Recht, selbst die entzündungshemmendsten, unästhetischsten Kombinationen auszuwählen, solange der Text noch gelesen werden kann.


Ihre Benutzer können also zwei beliebige Farben auswählen, solange sie lesbar kontrastieren?
DanRedux

Ich finde das aus technischer Sicht interessant, aber praktischer, wenn Ihre Benutzer das "Recht" haben, Farben auszuwählen, warum interessiert es Sie überhaupt, ob sie gelesen werden können? Ist es nicht an ihnen, es richtig zu machen?
nnnnnn

@nnnnnn Es ist mir egal, welche Farben sie auswählen, sie können mischen, was sie wollen, aber es ist mir wichtig, wie lesbar die (c) 2012 Company Inc. ist.
Silviu-Marian

Ich habe eine jsfiddle eingerichtet, um selbst zu sehen, wie genau die Antworten waren, und sie sehen, dass sie die Lesbarkeit ziemlich gut vorhersagen können: jsfiddle.net/UVUZC
mowwwalker

Falls jemand ShaoKahns Antwort verpasste, war dies der Link .
Silviu-Marian

Antworten:


94

Laut Wikipedia muss bei der Konvertierung in die Graustufendarstellung der Luminanz "die Werte für Rot, Grün und Blau ermittelt werden" und im nächsten Verhältnis gemischt werden: R: 30% G: 59% B: 11%

Daher hat Weiß eine Leuchtdichte von 100% und Gelb eine Leuchtdichte von 89%. Gleichzeitig hat Grün nur 59%. 11% sind fast viermal niedriger als 41% Unterschied!

Und selbst lime ( #00ff00) eignet sich nicht zum Lesen großer Textmengen.

IMHO für gute Kontrastfarben sollte die Helligkeit mindestens um 50% abweichen. Und diese Helligkeit sollte als in Graustufen umgewandelt gemessen werden.

upd : Kürzlich wurde ein umfassendes Tool für das im Web gefunden, das in der Reihenfolge die Formel aus dem w3-Dokument verwendet. Die Schwellenwerte können aus # 1.4 entnommen werden. Hier ist eine Implementierung für diese fortgeschrittenere Sache.

function luminanace(r, g, b) {
    var a = [r, g, b].map(function (v) {
        v /= 255;
        return v <= 0.03928
            ? v / 12.92
            : Math.pow( (v + 0.055) / 1.055, 2.4 );
    });
    return a[0] * 0.2126 + a[1] * 0.7152 + a[2] * 0.0722;
}
function contrast(rgb1, rgb2) {
    var lum1 = luminanace(rgb1[0], rgb1[1], rgb1[2]);
    var lum2 = luminanace(rgb2[0], rgb2[1], rgb2[2]);
    var brightest = Math.max(lum1, lum2);
    var darkest = Math.min(lum1, lum2);
    return (brightest + 0.05)
         / (darkest + 0.05);
}
contrast([255, 255, 255], [255, 255, 0]); // 1.074 for yellow
contrast([255, 255, 255], [0, 0, 255]); // 8.592 for blue
// minimal recommended contrast ratio is 4.5, or 3 for larger font-sizes

Weitere Informationen zur Berechnung dieses Werts finden Sie in der WCAG 2.0-Dokumentation .


2
+1 Das stimmt. Im wirklichen Leben - Die Umstellung auf Graustufen zur Überprüfung der Lesbarkeit von Designs ist ein Muss (speziell für Logos).
Roko C. Buljan

Ich werde diese Antwort als Lösung auswählen, da sie die umfassendste ist und ich nicht zwei Lösungen auswählen kann. Aber ich hätte die Antwort von HondaSuzukiShaolinShaorma gewählt, weil sie gebrauchsfertigen Code enthält.
Silviu-Marian

11
Während contrast([255, 255, 255], [0, 0, 255])8,592 zurückgegeben werden, ergeben dieselben umgekehrten Zahlen contrast([0, 0, 255], [255, 255, 255])0,116 - dh 1 / 8,592. Um ein Kontrastverhältnis zwischen 1 und 21 zu erhalten, müssen Sie 1 durch die Ausgabe teilen, wenn die Ausgabe <1 ist. function contrast(rgb1, rgb2) { var result = (luminanace(rgb1[0], rgb1[1], rgb1[2]) + 0.05) / (luminanace(rgb2[0], rgb2[1], rgb2[2]) + 0.05); if (result < 1) result = 1/result; return result; }
mattsoave

3
Hier ist ein kleiner Fehler : return (luminanace(rgb1[0], rgb1[1], rgb1[2]) + 0.05) / (luminanace(rgb2[0], rgb2[1], rgb2[2]) + 0.05); . Sie müssen den größeren Wert durch den kleineren teilen. Verwenden Sie dies besser:l1=luminanace(rgb1[0], rgb1[1], rgb1[2]) + 0.05; l2=luminanace(rgb2[0], rgb2[1], rgb2[2]) + 0.05; return (Math.max(l1,l2) / Math.min(l1,l2));
Zuluk

1
@zuluk, du hast vollkommen recht. Bearbeitet. w3.org/TR/WCAG20-TECHS/G17.html#G17-procedure
Bryan Rayner

24

Es gibt verschiedene Methoden zur Berechnung des Kontrasts, aber eine übliche Methode ist diese Formel:

brightness = (299*R + 587*G + 114*B) / 1000

Sie tun dies für beide Farben und nehmen dann den Unterschied. Dies ergibt offensichtlich einen viel größeren Kontrast für Blau auf Weiß als für Gelb auf Weiß.


2
Nur als Referenz anscheinend ist dies von w3c trendct.org/2016/01/22/… w3.org/TR/AERT#color-contrast
Cody Moniz

2
Nur zu Ihrer Information, die oben gezeigten Koeffizienten (299 * R + 587 * G + 114 * B) beziehen sich auf NTSC und sind für Webfarben, die sRGB sind, veraltet / falsch. Die richtigen sRGB-Koeffizienten sind 0,2126 * R + 0,7152 * G + 0,0722 * B (Beachten Sie, dass für sRGB die RGB-Komponenten zuerst linearisiert werden müssen).
Myndex

2

Kürzlich bin ich auf die Antwort auf dieser Seite gestoßen und habe mit dem Code ein Skript für Adobe Illustrator erstellt, um die Kontrastverhältnisse zu berechnen.

Hier sehen Sie das Ergebnis: http://screencast.com/t/utT481Ut

Einige der Kurznotationen des obigen Skripts waren für mich verwirrend und funktionierten nicht in Adobe Extend Script. Daher dachte ich, es wäre schön, meine Verbesserung / Interpretation des Codes zu teilen, den kirilloid geteilt hat.

function luminance(r, g, b) {
    var colorArray = [r, g, b];
    var colorFactor;
    var i;
    for (i = 0; i < colorArray.length; i++) {
        colorFactor = colorArray[i] / 255;
        if (colorFactor <= 0.03928) {
            colorFactor = colorFactor / 12.92;
        } else {
            colorFactor = Math.pow(((colorFactor + 0.055) / 1.055), 2.4);
        }
        colorArray[i] = colorFactor;
    }
    return (colorArray[0] * 0.2126 + colorArray[1] * 0.7152 + colorArray[2] * 0.0722) + 0.05;
}

Und natürlich müssen Sie diese Funktion aufrufen

Innerhalb einer for-Schleife bekomme ich alle Farben von meinem Illustrator-Objekt

//just a snippet here to demonstrate the notation
var selection = app.activeDocument.selection;
for (i = 0; i < selection.length; i++) {
   red[i] = selection[i].fillColor.red;
   //I left out the rest,because it would become to long
}

//this can then be used to calculate the contrast ratio.
var foreGround = luminance(red[0], green[0], blue[0]);
var background = luminance(red[1], green[1], blue[1]);
luminanceValue = foreGround / background;
luminanceValue = round(luminanceValue, 2);

//for rounding the numbers I use this function:
function round(number, decimals) {
   return +(Math.round(number + "e+" + decimals) + "e-" + decimals);
}

Weitere Informationen zum Kontrastverhältnis: http://webaim.org/resources/contrastchecker/


1

Basierend auf der kirilloiden Antwort:

Angular Service, der Kontrast und Lumineszenz berechnet, indem der Hex-Wert eingegeben wird:

.service('ColorContrast', [function() {
var self = this;

/**
 * Return iluminance value (base for getting the contrast)
 */
self.calculateIlluminance = function(hexColor) {
    return calculateIluminance(hexColor);
};

/**
 * Calculate contrast value to white
 */
self.contrastToWhite = function(hexColor){
    var whiteIlluminance = 1;
    var illuminance = calculateIlluminance(hexColor);
    return whiteIlluminance / illuminance;
};

/**
* Bool if there is enough contrast to white
*/
self.isContrastOkToWhite = function(hexColor){
    return self.contrastToWhite(hexColor) > 4.5;
};

/**
 * Convert HEX color to RGB
 */
var hex2Rgb = function(hex) {
    var 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;
};

/**
 * Calculate iluminance
 */
var calculateIlluminance = function(hexColor) {
    var rgbColor = hex2Rgb(hexColor);
    var r = rgbColor.r, g = rgbColor.g, b = rgbColor.b;
    var a = [r, g, b].map(function(v) {
        v /= 255;
        return (v <= 0.03928) ?
            v / 12.92 :
            Math.pow(((v + 0.055) / 1.055), 2.4);
    });
    return a[0] * 0.2126 + a[1] * 0.7152 + a[2] * 0.0722;
};

}]);

0
module.exports = function colorcontrast (hex) {
    var color = {};

    color.contrast = function(rgb) {
        // check if we are receiving an element or element background-color
        if (rgb instanceof jQuery) {
            // get element background-color
            rgb = rgb.css('background-color');
        } else if (typeof rgb !== 'string') {
            return;
        }

        // Strip everything except the integers eg. "rgb(" and ")" and " "
        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';
    };

    // Return public interface
    return color;

};

Das sieht gut aus, aber warum colorcontrastakzeptiert Ihre Methode hexArgumente, aber dieses Argument wird nicht verwendet?
Ctlockey

0

const getLuminanace = (values) => {
  const rgb = values.map((v) => {
    const val = v / 255;
    return val <= 0.03928 ? val / 12.92 : ((val + 0.055) / 1.055) ** 2.4;
  });
  return Number((0.2126 * rgb[0] + 0.7152 * rgb[1] + 0.0722 * rgb[2]).toFixed(3));
};

const getContrastRatio = (colorA, colorB) => {
  const lumA = getLuminanace(colorA);
  const lumB = getLuminanace(colorB);

  return (Math.max(lumA, lumB) + 0.05) / (Math.min(lumA, lumB) + 0.05);
};

// usage:
const back_color = [255,255,255]; //white
const text_color = [255,255,0]; //yellow

getContrastRatio(back_color, text_color); // 1.0736196319018405

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.