Arka plan rengine bağlı olarak yazı tipi rengini beyaz veya siyah olarak nasıl belirleyebilirim?


221

Bu örnek gibi bazı görüntüleri göstermek istiyorumalternatif metin

Dolgu rengine veri tabanındaki alan onaltılık renkte (örn: ClassX -> Renk: # 66FFFF) bir alan karar verir. Şimdi, seçili renk ile bir dolgu üzerinde veri göstermek istiyorum (yukarıdaki resimde olduğu gibi) ama renk koyu veya açık olup olmadığını bilmek gerekir, bu yüzden kelimelerin beyaz veya siyah olması gerektiğini biliyorum. Bir yolu var mı? tks


Yanıtlar:


347

Benzer bir soruya verdiğim cevaba dayanarak .

Bireysel kırmızı, yeşil ve mavi yoğunluklarını elde etmek için hex kodunu 3 parçaya bölmeniz gerekir. Kodun her 2 basamağı onaltılık (taban-16) gösterimdeki bir değeri temsil eder. Burada dönüşümün ayrıntılarına girmeyeceğim, aramaları kolaydır.

Tek tek renkler için yoğunluklara sahip olduğunuzda, rengin toplam yoğunluğunu belirleyebilir ve ilgili metni seçebilirsiniz.

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

186 eşiği teoriye dayanır, ancak tada göre ayarlanabilir. Aşağıdaki yorumlara dayanarak 150 eşiği sizin için daha iyi çalışabilir.


Düzenleme: Yukarıdaki basit ve oldukça iyi çalışıyor ve burada StackOverflow iyi kabul gibi görünüyor. Bununla birlikte, aşağıdaki yorumlardan biri, bazı durumlarda W3C yönergelerine uyulmamasına neden olabileceğini göstermektedir. Bu vesileyle, kurallara göre her zaman en yüksek kontrastı seçen değiştirilmiş bir form elde ediyorum. Eğer varsa yok W3C kurallarına uyması gerekir o zaman ben yukarıdaki basit formülü ile sopa olur.

Kontrast için verilen formül W3C Öneriler olup (L1 + 0.05) / (L2 + 0.05)burada, L1en açık renk parlaklığının ve L20.0-1.0 bir ölçekte karanlık parlaklığı olan. Siyahın parlaklığı 0.0 ve beyaz 1.0'dır, bu nedenle bu değerlerin değiştirilmesi en yüksek kontrastlı olanı belirlemenizi sağlar. Siyahın kontrastı beyazın kontrastından büyükse, siyah kullanın, aksi takdirde beyaz kullanın. LTest olurken test ettiğiniz rengin parlaklığı göz önüne alındığında :

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

Bu cebirsel olarak basitleştirir:

if L > sqrt(1.05 * 0.05) - 0.05

Veya yaklaşık olarak:

if L > 0.179 use #000000 else use #ffffff

Geriye kalan tek şey hesaplamak L. Bu formül kılavuzlarda da verilmiştir ve sRGB'den lineer RGB'ye dönüşüm ve ardından parlaklık için ITU-R tavsiyesi BT.709'a benziyor .

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

W3C yönergelerine bağlı olduğu için 0.179 eşiği değiştirilmemelidir. Sonuçları beğeninize uygun bulmazsanız, yukarıdaki daha basit formülü deneyin.


2
Tks Mark. Birkaç değişiklik denedim: sadece ilk basamak tarafından kırmızı yeşil ve mavi olarak hesaplandı (daha az hassas ancak daha fazla ağırlıkla basamak) ve 186 yerine 9 kullanıldı. Benim için, özellikle yeşillikler ile biraz daha iyi çalışıyor.
DJPB

2
Bu formül çok yanlış. Bir örnek vermek gerekirse sarıya #D6B508171, dolayısıyla beyaz bir kontrast verir. Ancak kontrast siyah olmalıdır (burada onaylanmıştır: webaim.org/resources/contrastchecker )
McGarnagle

1
@ MarkRansom evet, gözlerime siyah bu sarı ile çok daha iyi görünüyor. Sınırlı bir renk setiyle çalıştığım için, kesimi 186'dan daha düşük bir değere kaydırmayı başardım.
McGarnagle

3
İşte bu cevapta verilen iki formülü karşılaştıran başka bir demo daha . Renk seçiciyi kullanarak (en son Firefox veya Chrome'da) kontrastı herhangi bir renge karşı kontrol edebilirsiniz.
chetstone

3
@ Chetstone'un demosunu biraz oynadıktan sonra, Eşik önerisine katılmıyorum dışında, Basit formül benim için en iyi şekilde çalışıyor. Saf yeşilin sonuçlarına dayanarak, eşiği 149'a ayarlamayı denedim ve bence bu çok daha iyi çalışıyor. Bunu göstermek için gerçekten aptalca bir keman yaptım ; orijinal öneriyi görmek için üstteki eşiği değiştirmeyi deneyebilirsiniz.
Miral

30

Bu kod benim için olmadığı için kredi almıyorum, ancak gelecekte başkalarının hızlı bir şekilde bulması için burada bırakıyorum:

Mark Ransoms cevabına dayanarak, basit sürüm için bir kod snippet'i:

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

ve işte gelişmiş sürüm için kod snippet'i:

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

Bunları kullanmak için arayın:

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

Ayrıca, teşekkürler Alxve chetstone.


1
Basitçe 2 son parametrelerini düştü ve yeniden adlandırıldı: Basit fonksiyonu kullanılır ve daha biraz aşağı elimden isDark(bgColor)Benim kullanımı sadece daha sonra'color': isDark(color)?'white':'black'
diynevala

1
Benim için bir cazibe gibi çalıştı. Çok teşekkür ederim! Sadece sözdizimi ve işlevleri dönüştürme rağmen php yaptı.
CezarBastos

18

Buna ne dersiniz (JavaScript kodu)?

/**
 * 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';
}

1
Bu çoğu zaman işe yarar ancak i.imgur.com/3pOUDe5.jpg gibi garip görünen durumlar vardır , arka plan rengi aslında rgb'dir (6, 247, 241);
madprops

Rengin rgb değerlerine dönüştürmediğiniz durumlarda kontrast hesaplaması yapmanın nispeten ucuz bir yolu olduğunu takdir ediyorum (bu çok zor değil, sadece fazladan matematik adımları)
frumbert

12

Aritmetik çözümlere ek olarak, bir AI sinir ağı kullanmak da mümkündür. Avantajı, kendi zevkinize ve ihtiyaçlarınıza göre uyarlayabilmenizdir (yani, parlak doymuş kırmızılar üzerindeki kirli beyaz metin iyi görünür ve aynı siyah kadar okunabilir).

İşte konsepti gösteren düzgün bir Javascript demosu. Demoda kendi JS formülünüzü de oluşturabilirsiniz.

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

Aşağıda, aklıma sorunu çözmeme yardımcı olan bazı grafikler verilmiştir . İlk grafikte hafiflik sabittir 128, renk tonu ve doygunluk değişmektedir. İkinci grafikte doygunluk sabit 255'tir, renk tonu ve açıklık değişkendir.

İlk grafikte, renk tonu ve doygunluk değişirken hafiflik sabittir 128:

Renk doygunluğu ve hafiflik değişirken doygunluk 255'tir.


8

İşte Android için Java'daki çözümüm:

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

2
Gerçekten 'mükemmel' mi? Saf yeşil arka planı deneyin, # 00FF00.
Andreas Rejbrand

Bunun tüm renkler için test edilmediği doğrudur .... ama kullanıcıları rahatsız etmek için tasarlanmamış herhangi bir şey için saf yeşil bir arka plan kimler kullanır?
mwieczorek

mwieczorek kullanıcı tarafından oluşturulan içeriğe veya rastgele seçilen renklere güvenen kişiler yapar.
Marc Plano-Lesay

4

@MarkRansom cevabına dayanarak, burada bulabileceğiniz bir PHP betiği oluşturdum:

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

3

Bu, UIColor'un bir uzantısı olarak Mark Ransom'un cevabının hızlı bir versiyonudur.

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

2

Bu, bir öğeye tıkladığınızda SVG onay işaretinin rengini değiştirecek bir örnektir. Tıklanan öğenin arka plan rengine bağlı olarak onay işareti rengini siyah veya beyaz olarak ayarlar.

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

Ben dönüştürmek için bu JavaScript işlevini kullanın rgb/ rgbaetmek 'white'veya 'black'.

function getTextColor(rgba) {
    rgba = rgba.match(/\d+/g);
    if ((rgba[0] * 0.299) + (rgba[1] * 0.587) + (rgba[2] * 0.114) > 186) {
        return 'black';
    } else {
        return 'white';
    }
}

Bu biçimlerin herhangi girebilir ve onu çıkış olacak 'black'ya'white'

  • rgb(255,255,255)
  • rgba(255,255,255,0.1)
  • color:rgba(255,255,255,0.1)
  • 255,255,255,0.1

Şimdi bunu saf yeşil arka planla deneyin: # 00FF00.
Andreas Rejbrand

Bunun için teşekkür ederim! Hızlı tercüme ve benim ios app kullanılan!
Lucas P.

2

Mark'ın ayrıntılı cevabı harika çalışıyor. Javascript bir uygulama:

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

Sonra lum([111, 22, 255])beyaz veya siyah almak için bu işlevi çağırabilir .


1

Daha önce hiç böyle bir şey yapmadım, ama Hex 7F (FF / 2) 'nin medyan rengine göre renklerin her birinin değerlerini kontrol etmek için bir işlev yazmaya ne dersiniz. Üç renkten ikisi 7F'den büyükse, daha koyu bir renkle çalışıyorsunuz demektir.


1

Bağlantıdaki farklı girişlere dayanarak Arka plana bağlı olarak ön plan rengini siyah veya beyaz yapın ve bu iş parçacığına , size gerekli kontrast renklerini veren bir uzantı sınıfı yaptım.

Kod aşağıdaki gibi gider:

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

1

Benim gibi, alfa'yı dikkate alan bir RGBA sürümü arıyordunuz, işte yüksek kontrast için gerçekten iyi çalışan bir sürüm.

function getContrastColor(R, G, B, A) {
  const brightness = R * 0.299 + G * 0.587 + B * 0.114 + (1 - A) * 255;

  return brightness > 186 ? "#000000" : "#FFFFFF";
}

1

Bu, Mark Ransom'un cevabının sadece R tabanını kullanan bir R sürümüdür.

hex_bw <- function(hex_code) {

  myrgb <- as.integer(col2rgb(hex_code))

  rgb_conv <- lapply(myrgb, function(x) {
    i <- x / 255
    if (i <= 0.03928) {
      i <- i / 12.92
    } else {
      i <- ((i + 0.055) / 1.055) ^ 2.4
    }
    return(i)
  })

 rgb_calc <- (0.2126*rgb_conv[[1]]) + (0.7152*rgb_conv[[2]]) + (0.0722*rgb_conv[[3]])

 if (rgb_calc > 0.179) return("#000000") else return("#ffffff")

}

> hex_bw("#8FBC8F")
[1] "#000000"
> hex_bw("#7fa5e3")
[1] "#000000"
> hex_bw("#0054de")
[1] "#ffffff"
> hex_bw("#2064d4")
[1] "#ffffff"
> hex_bw("#5387db")
[1] "#000000"

0

@SoBiT, cevabınıza bakıyordum, ki bu iyi görünüyor, ama içinde küçük bir hata var. HexToG ve hextoB işlevleriniz küçük bir düzenlemeye ihtiyaç duyar. Substr'deki son sayı dizenin uzunluğudur ve bu durumda 4 veya 6 yerine "2" olmalıdır.

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

LESS'in contrast()benim için iyi çalışan güzel bir işlevi var, bkz. Http://lesscss.org/functions/#color-operations-contrast

"İki renkten hangisinin diğeriyle en büyük kontrastı sağladığını seçin. Bu, bir rengin bir arka plana karşı okunabilir olmasını sağlamak için yararlıdır, bu da erişilebilirlik uyumluluğu için de yararlıdır. Bu işlev, SASS için Pusula'daki kontrast işleviyle aynı şekilde çalışır. WCAG 2.0 uyarınca renkler, hafiflikleri değil gama düzeltmeli luma değerleri kullanılarak karşılaştırılır. "

Misal:

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

Çıktı:

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

0

Onaltılıktan siyah veya beyaza:

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

Mark'ın cevabına göre iOS için Objective-c sürüm kodu:

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

24 bit renkle test etmeye ne dersiniz?

YIQ yönteminin, 128 eşik olduğu varsayılarak AA ve AAA WCAG2.0 testlerini geçmeyen en az 1.9: 1 kontrast oranı döndüreceğini unutmayın.

W3C yöntemi için, büyük metin için AA ve AAA testlerini ve küçük metin için AA testini geçen 4.58: 1'lik minimum kontrast oranını döndürür, her renk için küçük metin için AAA testini geçmez.


0

İşte benim kullandığım ve şu ana kadar sorun yaşamadığım kendi yöntemim 😄

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

Mark'ın şaşırtıcı cevabına dayanan Java Swing kodum:

public static Color getColorBasedOnBackground(Color background, Color darkColor, Color lightColor) {
    // Calculate foreground color based on background (based on https://stackoverflow.com/a/3943023/)
    Color color;
    double[] cL = new double[3];
    double[] colorRGB = new double[] {background.getRed(), background.getGreen(), background.getBlue()};

    for (int i = 0; i < colorRGB.length; i++)
        cL[i] = (colorRGB[i] / 255.0 <= 0.03928) ? colorRGB[i] / 255.0 / 12.92 :
                Math.pow(((colorRGB[i] / 255.0 + 0.055) / 1.055), 2.4);

    double L = 0.2126 * cL[0] + 0.7152 * cL[1] + 0.0722 * cL[2];
    color = (L > Math.sqrt(1.05 * 0.05) - 0.05) ? darkColor : lightColor;

    return color;
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.