「動的に変化する背景色に対して、文字色は黒がいいか白がいいかを自動判断する」
という必要にかられました。のびーです。
というわけで、カラーコードから、その色が暗いのか明るいのか、
すなわち明度をとってくるJavascriptを書いてみたので、まとめです。
色表現の話は面白いですよねー。
基本
HTMLで使われているカラーコード(#ff0000みたいなやつ)は、
RGBで表現された色です。
最初の2桁の16進数が赤(R)、次が緑(G)、次が青(B)を表し、
それぞれをどのくらいの強さで出すかで、色を表しています。
一方、明度とは何なんでしょうか。
実はこれは、HSBという別の表現方法の、Bにあたるものです。
HSBは、色相(Hue)・彩度(Saturation)・明度(Blightness)の組み合わせで色を表しています。
HSBの詳細は割愛。
でもHSBの方が人間の感覚的に分りやすい色表現と言われてます。
変換しよう
そんなわけなので、まずはRGBをHSBに変換する方法を押さえなきゃいけないわけです。
以下のページが分りやすかったです。
HとSの計算式も面白いんですが、とりあえずいまはBの話だけ。
なんとRGBの最大値
がBlightnessにあたるらしいです。しんぷるすぎるわ!!
てなわけで、RGB(それぞれ256段階)から明度を出すサンプルコードはこんな感じになります。
var rgb = [128, 30, 200]; // R=128, G=30, B=200
// 0〜255の値が入っているので、255で割っておく
var blightness = Math.max(rgb[0], rgb[1], rgb[2]) / 255;
しかしこれだけだと納得いかない
しかし、ちょっとこれだけだと人間の感覚に合わないんですよね。
たとえば、
#888800と#000088は、明度は同じはずですが、
どう見たって前者の方が明るく見えます。
原因はまぁいろいろ考えられるんですが、
RGBそれぞれの原色が持っている明るさが違うってのが大きい気がします。
それぞれのMAXである、
#ff0000・#00ff00・#0000ff
を見比べてみても、青が一番暗い感じがしますよね。
この感覚をうまく、今回出す明度にも反映したいんですが、
感覚の話なので、うまいこと数式にはできなさそう。
RGBそれぞれに、補正値(ハンデ)を付けられるようにする
ってのが、とりあえずの対応としてはいい気がします。
最終的に
その後、16進数のparseとか、引数を色々対応させるとかして、
こんな感じのものができました。
// 補正付きの明度取得
var getBright = function (colorcode, mod) {
// 先頭の#は、あってもなくてもOK
if (colorcode.match(/^#/)) {
colorcode = colorcode.slice(1);
}
// 無駄に、ケタを動的に判断してるので、
// 3の倍数ケタの16進数表現ならOK etc) #ff0000 #f00 #fff000000
var keta = Math.floor(colorcode.length / 3);
if (keta < 1) {
return false;
}
// 16進数をparseして、RGBそれぞれに割り当て
var rgb = [];
for (var i = 0; i < 3; i++) {
rgb.push(parseInt(colorcode.slice(keta * i, keta * (i + 1)), 16));
}
// 青は暗めに見えるなど、見え方はRGBそれぞれで違うので、
// それぞれ補正値を付けて、人間の感覚に寄せられるようにした
var rmod = mod.r || 1;
var gmod = mod.g || 1;
var bmod = mod.b || 1;
// 明度 = RGBの最大値
var bright = Math.max(rgb[0] * rmod, rgb[1] * gmod, rgb[2] * bmod) / 255;
// 明度を返す
return bright;
};
// 補正はとりあえず、こんなもんがよさげだった
var mod = { r: 0.9, g: 0.8, b: 0.4 };
getBright('#ffc300', mod);
// 薄い背景色なら黒文字、明るい背景色なら黒文字、とかに使う
getBright('', mod) > 0.5 ? 'black' : 'white'
まとめ
- RGB → HSBの変換自体たのしい。もっとあそびたい。
- HSLのLを使った方がいい説も出てきたので、また調べたい