アシアルブログ

アシアルの中の人が技術と想いのたけをつづるブログです

色の処理に特化した軽量ライブラリ「Chroma.js」

こんにちは、古見澤(コミザワ)です。最近は寒いですね。
自宅に加湿器を導入して潤いが増した一方、結露が酷くて窓際や玄関がカビそうで困ってます。

さて今回は Chroma.js という色に関するJSライブラリに触れる機会があったのですが、日本語の記事が殆ど見つからなかったため、この紹介をしようと思います。

Chroma.js とは



色を補正したり、色空間の変換をしたり、カラースケールを設定して色を得る事ができる、色に関する振る舞いに特化した軽量(32.8 KB)なJSライブラリです。
多くの色空間に対応しており、CSSJavaScriptで扱いやすい形で色情報を取得することができます。
ソースはgit上にあり、ライセンスは BSD license です。

Chroma.js

サンプル






下の例は色の補正機能を使い、X11カラーのpink(#ffc0cb)をベースに、明るさを下げ、彩度を上げ、出来上がった色を16進数で返しています。


var myColor = chroma('<script>displayColor('pink')</script>').darken().saturate(2).hex();
console.log(myColor); // "<script>displayColor('#ff6c93')</script>"

次の例は、テストの点数を表にしたときに背景色に使う色を取得するものです。
仮に60点を合格点として背景色を白とし、そこから0点に近づくほど濃い青色が、100点に近づくほど濃い赤色を返すサンプルです。


var scores = [80, 98, 92, 75, 60, 74, 68, 90, 55, 76, 38], // テストの点数
    scale = chroma.scale(['<script>displayColor('#0000FF')</script>', '<script>displayColor('#FFFFFF')</script>', '<script>displayColor('#FF0000')</script>']).domain([0, 60, 100]);
 
scores.forEach(function(value){
  var bgColor = scale(value);

  // 取得した色を使って描画したり、プロパティに代入したり
  
  // ひとまず表示してみる
  document.write('<span style="background-color:' + bgColor + '; padding:6px">' + value + '</span> ');
});

実行結果:


導入



npmでインストールできます。


npm install chroma-js

readmeには書いてありませんが、bowerにもありました。


bower install chroma-js

あとは本体である chroma.min.js だけ直接ダウンロードしても大丈夫。

主な機能



以下に一部取り上げます、全ての情報はドキュメントを参照ください。見た目にも綺麗です。

コンストラクタ





chroma('hotpink');              // X11カラーの名称には全て対応
chroma('#ff3399');              // RRGGBBの6桁、3桁も対応。
chroma('F39');                  // # はあっても無くてもよく、大文字小文字も区別しない
chroma(0xff3399);               // 16進数でもOK
chroma(255, 51, 153);           // RGBをそれぞれ1つずつ渡すのもOK
chroma([255, 51, 153]);         // RGBを配列にして渡すのもOK
 
chroma(330, 1, 0.6, 'hsl');     // 最後の引数に明示的な色空間の指定が可能 省略時はRGB
chroma.hsl(330, 1, 0.6);        // 色空間名のfunctionを通しても作成可能
 
chroma(80, 40, 130, 'lch');     // LCH
chroma.lch(80, 40, 130);        // 同等
 
chroma(130, 40, 80, 'hcl');     // 上のLCHとはHとLの順序が逆なだけで、他は同じ
chroma.hcl(130, 40, 80);        // 同等
 
chroma(0.2, 0.8, 0, 0, 'cmyk'); // CMYK
chroma.cmyk(0.2, 0.8, 0, 0);    // 同等
 
chroma(0.6, 0, 0.8, 'gl');      // RGBとほぼ同じだが、引数の値の範囲が 0..1 となる
chroma.gl(0.6, 0, 0.8);         // 同等
chroma.gl(0.6, 0, 0.8, 0.5);    // また、オプションとして4番目の引数にアルファチャンネルを指定できる

色を混ぜる




chroma.mix('<script>displayColor('red')</script>', '<script>displayColor('blue')</script>');        // <script>displayColor('#7f007f')</script> 赤に青を混ぜる
chroma.mix('<script>displayColor('red')</script>', '<script>displayColor('blue')</script>', 0.25);  // <script>displayColor('#bf003f')</script> 3番目の引数で割合を指定可能、省略時は0.5。
 
// 混ぜあわせた結果は色空間によって変わる。4番目の引数で色空間を指定可能、省略時はrgb。
chroma.mix('<script>displayColor('red')</script>', '<script>displayColor('blue')</script>', 0.5, 'rgb'); // <script>displayColor('#7f007f')</script> <script>displayScale(chroma.scale(['red','blue']).mode('rgb'))</script>
chroma.mix('<script>displayColor('red')</script>', '<script>displayColor('blue')</script>', 0.5, 'hsl'); // <script>displayColor('#ff00ff')</script> <script>displayScale(chroma.scale(['red','blue']).mode('hsl'))</script>
chroma.mix('<script>displayColor('red')</script>', '<script>displayColor('blue')</script>', 0.5, 'lab'); // <script>displayColor('#ca0088')</script> <script>displayScale(chroma.scale(['red','blue']).mode('lab'))</script>
chroma.mix('<script>displayColor('red')</script>', '<script>displayColor('blue')</script>', 0.5, 'lch'); // <script>displayColor('#fa0080')</script> <script>displayScale(chroma.scale(['red','blue']).mode('lch'))</script>
 
// blend functionという形で、いくつか最初から用意されているものもある。
// 利用可能なのは multiply, darken, lighten, screen, overlay, burn, dogde の7つ。
chroma.blend('<script>displayColor('4CBBFC')</script>', '<script>displayColor('EEEE22')</script>', 'multiply'); // <script>displayColor('#46ae21')</script> 乗算
chroma.blend('<script>displayColor('4CBBFC')</script>', '<script>displayColor('EEEE22')</script>', 'darken');   // <script>displayColor('#4cbb22')</script> 比較(暗)
chroma.blend('<script>displayColor('4CBBFC')</script>', '<script>displayColor('EEEE22')</script>', 'lighten');  // <script>displayColor('#eeeefc')</script> 比較(明)
chroma.blend('<script>displayColor('4CBBFC')</script>', '<script>displayColor('EEEE22')</script>', 'screen');   // <script>displayColor('#f3fafc')</script> スクリーン
chroma.blend('<script>displayColor('4CBBFC')</script>', '<script>displayColor('EEEE22')</script>', 'overlay');  // <script>displayColor('#8df5f9')</script> オーバーレイ
chroma.blend('<script>displayColor('4CBBFC')</script>', '<script>displayColor('EEEE22')</script>', 'burn');     // <script>displayColor('#3fb6e8')</script> 焼き込み
chroma.blend('<script>displayColor('4CBBFC')</script>', '<script>displayColor('EEEE22')</script>', 'dogde');    // <script>displayColor('#ffffff')</script> 覆い焼き

ランダムな色を作成する




chroma.random();

明るさの上げ下げ




chroma('<script>displayColor('hotpink')</script>').darken();    // <script>displayColor('#c93384')</script> 暗くする
chroma('<script>displayColor('hotpink')</script>').darken(2);   // <script>displayColor('#930058')</script> 暗くする、引数で調整可能。省略時は1。
chroma('<script>displayColor('hotpink')</script>').brighten();  // <script>displayColor('#ff9ce6')</script> 明るくする

彩度の上げ下げ




chroma('<script>displayColor('slategray')</script>').saturate();   // <script>displayColor('#4b83ae')</script> 彩度を上げる
chroma('<script>displayColor('slategray')</script>').saturate(2);  // <script>displayColor('#0087cd')</script> 彩度を上げる、引数で調整可能、省略時は1
 
chroma('<script>displayColor('hotpink')</script>').desaturate();  // <script>displayColor('#e77dae')</script> 彩度を下げる
chroma('<script>displayColor('hotpink')</script>').desaturate(2); // <script>displayColor('#cd8ca8')</script> 彩度を下げる、引数で調整可能、省略時は1
chroma('<script>displayColor('hotpink')</script>').desaturate(3); // <script>displayColor('#b199a3')</script> 同上

色情報の変更




// 絶対指定の他、相対指定も可能
chroma('<script>displayColor('skyblue')</script>').set('hsl.h', 0);         // HSLのHの値を0にする
chroma('<script>displayColor('hotpink')</script>').set('lch.c', 30);        // LCHのCの値を30にする
chroma('<script>displayColor('orangered')</script>').set('lab.l', '*0.5');  // LABのLの値を半分にする
chroma('<script>displayColor('darkseagreen')</script>').set('lch.c', '*2'); // LCHのCの値を2倍にする

情報の取得




// 色の特定パラメータを取得する場合
chroma('<script>displayColor('orangered')</script>').get('lab.l'); // 57.582
chroma('<script>displayColor('orangered')</script>').get('hsl.l'); // 0.5
chroma('<script>displayColor('orangered')</script>').get('rgb.g'); // 69
 
// 16進数の文字列を取得
chroma('<script>displayColor('orange')</script>').hex(); // "#ffa500"
 
// RGBか、HSL表記の文字列を返す
chroma('<script>displayColor('teal')</script>').css();            // "rgb(0,128,128)" 省略時はRGB
chroma('<script>displayColor('teal')</script>').alpha(0.5).css(); // "rgba(0,128,128,0.5)"
chroma('<script>displayColor('teal')</script>').css('hsl');       // "hsl(180,100%,25.1%)"
 
// 各色空間毎の値を配列で返す
chroma('<script>displayColor('orange')</script>').rgb();       // [255,165,0]
chroma('<script>displayColor('orange')</script>').hsl();       // [38.82,1,0.5]
chroma('<script>displayColor('orange')</script>').hsv();       // [38.82,1,1]
chroma('<script>displayColor('orange')</script>').hsi();       // [39.64,1,0.55]
chroma('<script>displayColor('orange')</script>').lab();       // [74.94,23.93,78.95]
chroma('<script>displayColor('skyblue')</script>').lch();      // [79.21,25.94,235.11]
chroma('<script>displayColor('skyblue')</script>').hcl();      // [235.11,25.94,79.21]

コントラストを取得




// 2色間のコントラストを取得。
// 例えば背景色と白色を比較し、値が小さい(=文字が見づらい)場合は文字色を白くするといった判定に使える
chroma.contrast('<script>displayColor('pink')</script>', '<script>displayColor('white')</script>'); // 1.5379867185268434
chroma.contrast('<script>displayColor('blue')</script>', '<script>displayColor('white')</script>'); // 8.592471358428805

コントラスト比は最低でも4.5:1を目安にすれば大丈夫そうです。

カラースケールの作成



カラースケールはこのライブラリの主要機能の一つで、冒頭のサンプルにおいてテスト点数に応じて様々な濃さの赤や青を取得する際に利用しました。
グラデーションやヒートマップのように、特定の色との間の微妙な色合いを求めるのに便利です。

基本的な使い方



chroma.scale でスケールを作成し、スケールに対して数値(ドメイン)を指定することで色を取り出します。


// 引数無しのデフォルトでは 白→黒 、0~1の範囲(ドメイン)で作成される。
f = chroma.scale(); <script>displayScale(chroma.scale())</script>
f(0.25);  // <script>displayColor('#bfbfbf')</script>
f(0.5);   // <script>displayColor('#7f7f7f')</script>
f(0.75);  // <script>displayColor('#3f3f3f')</script>

上の例だと、白(#FFFFFF)から黒(#000000)のスケールを定義し、0が白、1が黒となります。
つまり0.5は白と黒の丁度中間、0.75は中間と黒の間の色です。

スケール作成時の引数に配列を渡すことにより、スケールの色を定義できます。


chroma.scale(['<script>displayColor('yellow')</script>', '<script>displayColor('green')</script>']); <script>displayScale(chroma.scale(['yellow','green']))</script>

上の例では 黄 → 緑 に移り変わるカラースケールを定義しています。

スケールの範囲はデフォルトで0~1ですが、自分で定義することも可能です。
以下の例では 黄 ~ 緑 を 0 ~ 100の範囲で定義しています。色を取り出す時の引数の範囲が広がります。


chroma.scale(['<script>displayColor('yellow')</script>', '<script>displayColor('green')</script>'])).domain([0, 100]); <script>displayScale(chroma.scale(['yellow','green']))</script>

冒頭のサンプルでは、テストの点数と対応させるために、0~100に設定していました。

複数色からなるカラースケールと、ドメインの区切り設定



色は2色でなくてもよく、例えば以下の例では 黄 → 緑 → 白 という3色の定義です。


chroma.scale(['<script>displayColor('yellow')</script>', '<script>displayColor('green')</script>', '<script>displayColor('white')</script>']); <script>displayScale(chroma.scale(['yellow','green','white']))</script>

0で黄、0.5で緑、1で白。なので、0.25は黄と緑の中間、0.75は緑と白の中間色が取得できます。

また、以下のように色数に応じたdomainを明示的に指定することで、各領域の区切り位置をずらせます。


chroma.scale(['<script>displayColor('yellow')</script>', '<script>displayColor('green')</script>', '<script>displayColor('white')</script>']).domain([0, 0.2, 1]);

<script>displayScale(chroma.scale(['yellow','green','white']))</script> // 一つ上の例
<script>displayScale(chroma.scale(['yellow','green','white']).domain([0,0.2,1]))</script> // ずらした例

domainを指定しなかった場合、緑は0.5の位置でしたが、この場合は0.2の位置が緑になります。

色空間による違い



カラースケールの色変化は色空間によって変わります。利用する色空間を指定する場合はmode関数で指定します。
何も指定しない場合のデフォルトはrgbです。


chroma.scale(['<script>displayColor('yellow')</script>', '<script>displayColor('navy')</script>']); // 省略時はrgb <script>displayScale(chroma.scale(['yellow','navy']))</script>
chroma.scale(['<script>displayColor('yellow')</script>', '<script>displayColor('navy')</script>']).mode('lab');   <script>displayScale(chroma.scale(['yellow','navy']).mode('lab'))</script>
chroma.scale(['<script>displayColor('yellow')</script>', '<script>displayColor('navy')</script>']).mode('hsl');   <script>displayScale(chroma.scale(['yellow','navy']).mode('hsl'))</script>
chroma.scale(['<script>displayColor('yellow')</script>', '<script>displayColor('navy')</script>']).mode('lch');   <script>displayScale(chroma.scale(['yellow','navy']).mode('lch'))</script>


色の区切る個数を指定する



例えば 黄色から緑へのカラースケールを作るけど、使う色はグラデーションみたいではなく、5色だけでいいのに…という時は以下のように書きます


chroma.scale(['<script>displayColor('yellow')</script>', '<script>displayColor('green')</script>']).classes(5); <script>displayScale(chroma.scale(['yellow','green']).classes(5))</script>

更に、ぴったり5等分ではなく「10%、20%、40%、20%、10%」みたいに真ん中の層を厚くしたい!という時は、引数を配列にして区切る位置を指定します。


chroma.scale(['<script>displayColor('yellow')</script>', '<script>displayColor('green')</script>']).classes([0, 0.1, 0.3, 0.7, 0.9, 1]);
<script>displayScale(chroma.scale(['yellow','green']).classes(5))</script> // 1つ上の例
<script>displayScale(chroma.scale(['yellow','green']).classes([0,0.1,0.3,0.7,0.9,1]))</script> // ずらした例


最後に



単なるテキストデータでも、データに応じた色がつくだけで大分印象も変わります。
数字の羅列だけでは伝わりにくい情報も、色で直感的に伝えられるようになる例もあります。
そんなアクセント付けの時に便利なのではないでしょうか。
また、Web上から色を合成したり補正するサービスを作るのにも便利そうです。