[CSS+Javascript] CMYの減法混色をmix-blend-modeを使ってシミュレートするためのベースロジック

## はじめに
[以前に書いた記事](https://qiita.com/STSHISHO/items/3611b16186ddeff958e8)の続きに当たります。あるWEBアプリケーションのデモ開発を行った後、UIの一部、表題内容の処理部分をパーツとして分解し、[JSFiddle](https://jsfiddle.net/STSHISHO/j5apfr94/)と[CodePen](https://codepen.io/STSHISHO/pen/BaQdELW)で公開しましたので、本記事はその詳細という位置づけとなります。

## 目的
Ｃ、Ｍ、Ｙ、それぞれのカラーブロックを重ね、実際に混色した際の色をシミュレートします。ベースロジックのデモのため、それぞれの色成分初期値は濃度１００％とし、濃度の調整はスライダーにより、３色同比率で増減させます。

つまり、結果的には単に見た目が黒から白への濃度変化するものになるのですが、その表現方法の構築が本記事でのメインとなります。[^1]

以下が今回の完成形です。

<p class="codepen" data-height="580" data-theme-id="dark" data-default-tab="html,result" data-user="STSHISHO" data-slug-hash="BaQdELW" style="height: 580px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="stCMYblend">
  <span>See the Pen <a href="https://codepen.io/STSHISHO/pen/BaQdELW">
  stCMYblend</a> by STSHISHO (<a href="https://codepen.io/STSHISHO">@STSHISHO</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>

## CSS設定
### 1-カラーブロックの定義

```HTML
<div class="container">
  <div class="box box-1"></div>
  <div class="box box-2"></div>
  <div class="box box-3"></div>
</div>
```

```CSS
.container {
  width: 100%;
}
.box {
  width: 300px;
  height: 300px;
}
.box-1 {
  /* cyan */
  background: rgba(0, 255, 255, 1);
}
.box-2 {
  /* magenta */
  background: rgba(255, 0, 255, 1);
}
.box-3 {
  /* yellow */
  background: rgba(255, 255, 0, 1);
}
```

上記コードの結果、以下のようになります。
![cmy-01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1111613/a54cdecc-c245-3fdf-36ea-39367e0d6562.png)

### 2-センター揃えで横に並べる

```CSS
.container {
  width: 100%;
  display: flex;
  justify-content: center;
  align-items: center; /* if you need */
  height: 400px;
  position: relative; /* 中央揃えの場合は指定しなくても意図通りに表示 */
}
.box {
  width: 300px;
  height: 300px;
}
```

`.container`の修正。以前なら`float`を使用していましたが、`display: flex;`で横並びにします。`justify-content: center;`で中央揃えになります。必要に応じて`align-items`を指定します。今回は上下左右中央揃えにしています。
![cmy-02-flex.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1111613/8ac3756a-654c-b3c1-e973-3d4e70780e82.png)

### 3-カラーブロックを重ねる
これは単純です。`.box`に`position: absolute;`を追加するだけです。一番最後に記述された要素が最前面になりますので、デモではYellowのカラーブロックが前面に表示されます。
![cmy-03-abslt.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1111613/024e46ee-9871-ec04-d38f-dbc2bf86cc54.png)

### 4-mix-blend-modeを使う
CSSでPhotoshopのような乗算効果は再現可能か調べていたところ、[こちらの記事](https://www.marineroad.com/staff-blog/12289.html)を見つけ、[ＭＤＮ](https://developer.mozilla.org/ja/docs/Web/CSS/mix-blend-mode)で仕様を確認しました。`.box`に`mix-blend-mode: multiply;`を追加して、設定完了。
![cmy-04-mltply.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1111613/048cf3ae-804d-5e98-a3cb-48176278b944.png)

## スライダーを設置し、イベントリスナーを設定

```HTML
<div class="slider">
  <span class="rowHead">濃度</span><input type="range" id="slider_all" min="0" max="100" step="1" value="100"><span class="rowTail">100%</span>
</div>
```

カラーブロックの直下にスライダーを設置し、イベントリスナーを設定します。

```Javascript
window.onload = () => {
  const t = document.querySelector('.container') //container全体を取得
  const s = document.querySelector('#slider_all')
  //スライダーにイベントリスナー設定
  s.addEventListener('input', e => t.style.opacity = e.target.value * 0.01, false)
}
```

`input`で、リアルタイムに濃度が変わります。スライダーの値が０から１００までなので、0.01を乗算することでそのままopacity値となり、`.container`全体の`opacity`を変更します。

## 最後に
上記を元に、スライダーをカラーごとに設置し、ターゲットをカラーブロックに変更することで、個別の濃度調整が可能になります。

なお、元になったWEBアプリケーションの詳細についてはいずれまとめる予定でいます。

### [追記]
今回はあくまでも実際のケースに沿った**表現方法の一例**として、`mix-blend-mode`を使った方法を紹介しています。そのため、タイトルも少し修正しました。シンプルにRGB→CMY変換、減法混色をしたい場合には、@il9437さんのコメントおよびコードの方がロジックとして王道ですし、参考になると思います。

### [参考]
+ [CSSでPhotoshopを使わずにレイヤーの描画効果を作るには？ | マリンロード](https://www.marineroad.com/staff-blog/12289.html)
+ [mix-blend-mode - CSS: カスケーディングスタイルシート | MDN](https://developer.mozilla.org/ja/docs/Web/CSS/mix-blend-mode)

[^1]: 当該のアプリケーションではＣ、Ｍ、Ｙ、個別の濃度調整スライダーを実装しています。また、それぞれの色成分初期値が、例えば、シアンの初期値（濃度１００％）は`RGB(0, 255, 255)`ではなく、`RGB(65,225,210)`という具合にクライアントによって変更できるようになっています。