Subterranean Flower

JavaScriptで綺麗な色を生成する

Author
古都こと
ふよんとえんよぷよぐやま!!!

人は見た目がn割(nには好きな数字を入れてね)だとよく言われます。そしてそれはウェブサイト/ウェブアプリケーションにおいても同じことです!1990年代丸出しのウェブサイトより、2018年的な見た目の方が好まれるでしょう。

見た目において大きな比重を占めているのが「色」です。色を工夫することで、我々はポップな新人歌手から、クールな高級ブランドまで幅広く表現できるのです。

そんな「色」ですが、コンピュータに生成させようとすると、なかなか苦労する羽目になります。そこでこの記事では、綺麗な「色」を生成するための工夫について紹介します。

色は色々

ウェブの世界と色とは、切っても切れない関係にあります。表示を司る以上、色から逃げることはできないからです。

例えばあなたの関わるプロジェクトで、配色に100色必要だとします。100色!なかなかの難問です。自分で一生懸命100個も考えるのは難しいですし、デザイナーさんに相談しても「100色もいらないと思いますが」としか帰ってきません。しかしなぜか今回は不思議な力により100色いるのです。

「デザイナーさんを拘束してカラーパレットを作ってもらおう!」というのもひとつの解決策ですが、ここはなんとか安く簡単に、できるだけ手を抜いて、それでいて仕事は頑張っている感じで用意したいところです。

そこで偶然にもあなたの目の前にブラウザとJavaScriptが現れます!あなたは魔法のJavaScriptでこの難問を解決しようと目論みます。

まずは愚直にランダム生成

コンピュータにおいて、一般的に色はRGB光の三原色で表されます。Red、Green、Blueの三色です。この3つを組み合わせることで様々な色を表現できます。RGBそれぞれの値域は媒体によって様々で、0.0から1.0の値で表すものもあれば、0から255までの値で表すものもあります。

HTML/CSSにおいてRGBはそれぞれ0から255までの値で表されます。rgb(142, 168, 89)のようにです。値の範囲は決まっているので、これらをランダム生成してやれば簡単に色を作れそうです。まずはこれをやってみましょう。

RGB値だけ生成しても何が何だかわからないので、画面に色として表示することにします。色のついた四角形を並べることにしましょう。

色のついた四角形を100個生成するコードは、たとえば以下のようになります:

// 四角形を入れるコンテナ。
// flexboxで横並びにして、横方向にはみ出したら折り返すようにする。
const colorBoxContainer = document.createElement('div');
colorBoxContainer.style.display = 'flex';
colorBoxContainer.style.flexWrap = 'wrap';

// 色のついた四角形を100個作って先ほどのコンテナの中に入れる。
for(let i=0; i<100; i++) {
  // サイズは100px x 100px。
  const colorBox = document.createElement('div');
  colorBox.style.width = '100px';
  colorBox.style.height = '100px';

  // 色は完全にランダム。
  const r = Math.round(Math.random() * 255);
  const g = Math.round(Math.random() * 255);
  const b = Math.round(Math.random() * 255);
  colorBox.style.backgroundColor = `rgb(${r},${g},${b})`;

  colorBoxContainer.appendChild(colorBox);
}

// コンテナをbodyに追加する。
document.body.appendChild(colorBoxContainer);

Math.random()は0.0から1.0までの間の値を生成するので、255をかけてやって、あとは四捨五入すれば0から255までの値が得られます。これをRGBそれぞれについて行います。

まずはこれを実行してみましょう!以下のようになります:

100色生成できました!しかし、うーん、なんというか、こう、でろーんとしてますね。毒沼のテクスチャを作るとかゾンビ・ランドを開園する準備をしているとかならこれでいいのですが、クールなウェブサービスのイメージからは程遠そうです。

次はこのでろーんとしたゾンビパレットを解決していきましょう。

RGBから離れる

ゾンビパレット問題から脱却するのに一番簡単な方法は、RGB表現を諦めることです。えっ、でもさっきコンピュータはRGBで表現するって言ったじゃん…。

RGBを諦める理由として、そもそもRGBは光の原理の利用であって、人間の感覚とは程遠いのです。RGBで値を扱おうとすると、あくまでも、ただ存在する色を乱雑に選ぶだけになってしまうのです。

なので、より人間の感覚近い表現方法を選びましょう。幸いHTML/CSSはもうひとつの色表現に対応しています。その名もHSLです。

HSLはHue(色相/色の種類)、Saturation(彩度/鮮やかさ)、Lightness(輝度/明るさ)の3つで色を表現する方法です。色相は「青」とか「赤」とかの色の種類です。色相は360度の角度で表します。

Hue(色相)

0度(上)が赤色で、右下(135度ぐらい?)が緑、左下(225度ぐらい?)が青、という感じです。角度を指定するだけで色が変わるので、R成分がどうだとかG成分がこれだけいるだとかB成分は抑えてとか、そういうことを一切気にしなくても使えます。あとはこれの彩度と明るさをうまく調整してやれば綺麗な色を出せそうです。

彩度と輝度は%で表します。50%とか、80%とか、そういうのです。色相で決めた色に対して鮮やかさと明るさを決めるイメージですね。

さっきのコードをHSLを用いたものに書き換えてみましょう:

// 四角形を入れるコンテナ。
// flexboxで横並びにして、横方向にはみ出したら折り返すようにする。
const colorBoxContainer = document.createElement('div');
colorBoxContainer.style.display = 'flex';
colorBoxContainer.style.flexWrap = 'wrap';

// 色のついた四角形を100個作って先ほどのコンテナの中に入れる。
for(let i=0; i<100; i++) {
  // サイズは100px x 100px。
  const colorBox = document.createElement('div');
  colorBox.style.width = '100px';
  colorBox.style.height = '100px';

  // 色はHue(色相)を変化させる。
  // Saturation(彩度)とLightness(輝度)は固定。
  const h = Math.random() * 360;
  colorBox.style.backgroundColor = `hsl(${h}, 80%, 60%)`;
  colorBoxContainer.appendChild(colorBox);
}

// コンテナをbodyに追加する。
document.body.appendChild(colorBoxContainer);

HSLのうちHを回して、SとLを固定にしてみました。これで彩度と輝度は固定のまま、色の種類だけがどんどん変わっていきます。

これを実行してみます:

だいぶポップな感じになりました!RGBを使ったときよりも圧倒的にカラフルです!

色の並びが気になるときは、色相の変化をランダムでなく、角度をどんどん増やしていくようにすれば綺麗になります:

// 四角形を入れるコンテナ。
// flexboxで横並びにして、横方向にはみ出したら折り返すようにする。
const colorBoxContainer = document.createElement('div');
colorBoxContainer.style.display = 'flex';
colorBoxContainer.style.flexWrap = 'wrap';

// 色のついた四角形を100個作って先ほどのコンテナの中に入れる。
for(let i=0; i<100; i++) {
  // サイズは100px x 100px。
  const colorBox = document.createElement('div');
  colorBox.style.width = '100px';
  colorBox.style.height = '100px';

  // 色はHue(色相)を変化させる。
  // Saturation(彩度)とLightness(輝度)は固定。
  const h = i * (360 / 100);
  colorBox.style.backgroundColor = `hsl(${h}, 80%, 60%)`;
  colorBoxContainer.appendChild(colorBox);
}

// コンテナをbodyに追加する。
document.body.appendChild(colorBoxContainer);

綺麗なカラーパレットができました!気に入らないときは彩度や輝度を調整してみてください。

HSLからRGBへの変換

HSLはRGBに比べて便利!……とはいえ、HSLが使えずRGBを使わざるを得ない場面も少なくありません。そういうときは初めはHSLで計算して、最後にRGBに変換してしまいましょう。

HSLからRGBへの変換は、WikipediaのHSLの記事が参考になります。これをJavaScriptコードに落とし込んでみましょう。場合分けの数は多いですが、計算自体はシンプルです。

// 四角形を入れるコンテナ。
// flexboxで横並びにして、横方向にはみ出したら折り返すようにする。
const colorBoxContainer = document.createElement('div');
colorBoxContainer.style.display = 'flex';
colorBoxContainer.style.flexWrap = 'wrap';

// 色のついた四角形を100個作って先ほどのコンテナの中に入れる。
for(let i=0; i<100; i++) {
  // サイズは100px x 100px。
  const colorBox = document.createElement('div');
  colorBox.style.width = '100px';
  colorBox.style.height = '100px';

  // 色はHue(色相)を変化させる。
  // Saturation(彩度)とLightness(輝度)は固定。
  const h = i * (360 / 100);
  const s = 0.8;
  const l = 0.6;

  // HSL -> RGBへの変換。
  // 詳しくは
  // https://en.wikipedia.org/wiki/HSL_and_HSV#From_HSL
  // を参照。
  const h360 = h % 360; // hueは360度までに収める。

  const c = (1 - Math.abs(2 * l - 1)) * s;
  const hp = h360 / 60;
  const x = c * (1 - Math.abs((hp % 2) - 1));

  let r1;
  let g1;
  let b1;

  if(hp >= 0 && hp <= 1) {
    r1 = c;
    g1 = x;
    b1 = 0;
  } else if(hp >= 1 && hp <= 2) {
    r1 = x;
    g1 = c;
    b1 = 0;
  } else if(hp >= 2 && hp <= 3) {
    r1 = 0;
    g1 = c;
    b1 = x;
  } else if(hp >= 3 && hp <= 4) {
    r1 = 0;
    g1 = x;
    b1 = c;
  } else if (hp >= 4 && hp <= 5) {
    r1 = x;
    g1 = 0;
    b1 = c;
  } else if (hp >= 5 && hp < 6) {
    r1 = c;
    g1 = 0;
    b1 = x;
  } else {
    r1 = 0;
    g1 = 0;
    b1 = 0;
  }

  const m = l - 0.5 * c;
  const r = r1 + m;
  const g = g1 + m;
  const b = b1 + m;

  // 上の計算で0.1から1.0までのRGB値が得られるので、255倍する。
  const r255 = r * 255;
  const g255 = g * 255;
  const b255 = b * 255;

  colorBox.style.backgroundColor = `rgb(${r255},${g255},${b255})`;
  colorBoxContainer.appendChild(colorBox);
}

// コンテナをbodyに追加する。
document.body.appendChild(colorBoxContainer);

これでHSLをRGBに変換できました。HSLが使えない古いシステムでも安心ですね。