ブラウザ上で色の試行が手軽にできるChrome拡張機能を公開

# 0. はじめに
[LIFULL Advent Calendar 2023](https://qiita.com/advent-calendar/2023/lifull) 22日目の記事です。

普段は[LIFULL HOME'S不動産査定](https://www.homes.co.jp/satei/)と[ホームズマンション売却](https://lifullhomes-satei.jp/)の開発に携わっています。

サイト上で色の試行が手軽にできるChrome拡張機能を開発・公開したので紹介します。

https://chromewebstore.google.com/detail/brightnesscolor-changer/mdifhjjkanpdehdgkoieaofpjnnceglb

# 1. 使用方法

Ⅰ. 色を試行したいページを開く。

![image1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/484145/5ee566d8-9c2e-2fa7-6ced-1e6c9b03fbc8.png)

Ⅱ. 拡張機能のアイコンを押下してページ上で使用されている色の一覧を表示する。

![image2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/484145/cb15a43e-4db0-0622-f03e-7f55a5c49580.png)

Ⅲ.[明度の変更]　試行する対象の色を決定して、上矢印や下矢印を押下して明度を変更する。（#a58cdc -> #553c64）

![image3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/484145/e0d36c5d-4ab4-6232-39d0-0cbca4355eb5.png)

Ⅳ.[色の変更]　試行する対象の色を決定して、カラーピッカーを押下して色を選択後にOKボタンを押下して変更する。（#553c64 -> #5a5faa）

![image4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/484145/fecc5fd0-9346-7401-b364-a6fc35458d0f.png)
![image5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/484145/b63f30ec-ff6b-b66c-fc92-108bf2f12f45.png)
![image6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/484145/5c4e37ac-ceb8-da71-037f-d2c0572133de.png)

# 2. 構成
```
.
├── contentScript.js
├── manifest.json
├── popup.css
├── popup.html
└── popup.js
```

## manifest.json
- Chrome Extensionの設定ファイル

参考：[マニフェスト ファイル形式](https://developer.chrome.com/docs/extensions/reference/manifest?hl=ja)

## contentScript.js
- ブラウザに表示されているページに対してDOM操作をおこなうことができるファイル
- 今回はページの色の変更をおこなうためこちらを使用する

参考：[マニフェスト - コンテンツ スクリプト](https://developer.chrome.com/docs/extensions/reference/manifest/content-scripts?hl=ja)

## popup.html
- 拡張機能のアイコンを押下した時に表示される画面の構成ファイル
- ここから拡張機能の設定や操作をおこなうことができる

# 3. 仕組み
## 変更対象
CSSを使ったHTMLの要素への色の適用では以下のような方法が挙げられます。
- キーワード
    - 標準的な色名（`red`, `green`, `blue` など）
- RGB値
    - 16進数文字列表記
        - 16進数の数字で色の各要素 (赤、緑、青) を表したもの（`#ff0000`, `#00ff00`, `#0000ff` など）
    - RGB関数表記
        - CSS関数`rgb()`で色の各要素 (赤、緑、青) を表したもの（`rgb(255, 0, 0)`, `rgb(0, 255, 0)`, `rgb(0, 0, 255)` など）
- HSL関数表記
    - CSS関数`hsl()`で色の各要素 (hue：色相、saturation：彩度、lightness：輝度) を表したもの（`hsl(0,　100%,　50%)`, `hsl(120,　100%,　50%)`, `hsl(240,　100%,　50%)` など）

この拡張機能では、2023年12月22日現在、CSSにRGB値で指定されている色のみ変更が可能になっています。

参考：[CSS を使った HTML の要素への色の適用](https://developer.mozilla.org/ja/docs/Web/CSS/CSS_colors/Applying_color)

## 色の変更
カラーピッカー`<input type="color">`を使用しています。

## 明度の変更
RGBをHSVに変換して明度の変更をできるようにして、再度RGBに変換しています。

参考：[HSV色空間](https://www.peko-step.com/html/hsv.html)、[RGBからHSVへの変換計算式](https://www.peko-step.com/tool/hsvrgb.html#:~:text=%E5%A4%89%E6%8F%9B%E3%81%A8%E3%81%AA%E3%82%8A%E3%81%BE%E3%81%99%E3%80%82-,RGB%E3%81%8B%E3%82%89HSV%E3%81%B8%E3%81%AE%E5%A4%89%E6%8F%9B%E8%A8%88%E7%AE%97%E5%BC%8F,-%E8%89%B2%E7%9B%B8%E3%82%92%E6%B1%82%E3%82%81%E3%82%8B)

### HSV
- 色を「色相(Hue)」「彩度(Saturation)」「明度(Value・Brightness)」の3要素で表したもの

![HSV.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/484145/76c1bd93-e5a3-d8de-5d6b-a3123c8c3758.png)

#### 色相（Hue）
![色相.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/484145/2246fbe6-37eb-54cb-e492-8819df9ead90.png)
- 赤や緑や青といった具体的な色を定義する要素
- 0°～360°の範囲の値で表される
- 代表的なもの
    - 0° ： 赤（`#FF0000`）
    - 60° ： 黄（`#FFFF00`）
    - 120° ： 緑（`#00FF00`）
    - 180° ： シアン（`#00FFFF`）
    - 240° ： 青（`#0000FF`）
    - 300° ： マゼンタ（`#FF00FF`）

#### 彩度(Saturation)
![彩度.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/484145/07526a5a-1611-ac4d-7fb2-f9ac28d2ee2b.png)
- 色相で定義された色の鮮やかさ・濃さを表す要素
- 0%～100%の範囲で表される

#### 明度(Value・Brightness)
![明度.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/484145/e8e7c3cd-bd0a-fc69-d090-7e5c86447e61.png)
- 色相で定義された色の明るさ・暗さを表す要素
- 0%～100%の範囲で表される

### RGBからHSVの変換
#### 色相を求める
R G Bのうち`最も大きな値 = max`、`最も小さな値 = min`、`色相 = h` とする。
- `R = max` の場合：`h = 60 × ((B - G) ÷ (max - min)) + 180`
- `G = max` の場合：`h = 60 × ((R - B) ÷ (max - min)) +　300`
- `B = max` の場合：`h = 60 × ((G - R) ÷ (max - min)) + 60`
- `R = G = B` の場合：`h = 0`

    ```JavaScript
    const max = Math.max( r, g, b );
    const min = Math.min( r, g, b );
    const diff = max - min ;
    let h = 0;
    switch( min ) {
        case max:
            h = 0;
        break;
        case r:
            h = (60 * ((b - g) / diff)) + 180;
        break;
        case g :
            h = (60 * ((r - b) / diff)) + 300;
        break;
        case b :
            h = (60 * ((g - r) / diff)) + 60;
        break;
    }
    ```
#### 彩度を求める
R G Bのうち`最も大きな値 = max`、`最も小さな値 = min`、`彩度 = s` とする。
- `s = (max - min) ÷ max`

    ```JavaScript
    const s = max == 0 ? 0 : diff / max;
    ```

#### 明度を求める
R G Bのうち`最も大きな値 = max`、`明度 = v` とする。
- `v = max`

    ```JavaScript
    let v = max;
    ```

#### 明度の操作

- 上げるとき
    ```JavaScript
    if(v <= 0.99) {
        v = v + 0.01
    }
    ```

- 下げるとき
    ```JavaScript
    if(v >= 0.01) {
        v = v - 0.01
    }
    ```

### HSVからRGBからの変換
<details><summary>RGBからHSVの変換を逆算して、RGBに戻します。</summary><div>

```JavaScript
if ( s !== 0 ) {
    let rgb;
    const rangeH = parseInt( h / 60 ) ;
    const decimal = h / 60 - rangeH ;
    const v1 = v * (1 - s) ;
    const v2 = v * (1 - s * decimal) ;
    const v3 = v * (1 - s * (1 - decimal)) ;

    switch( rangeH ) {
        case 0 :
        case 6 :
            rgb = [ Math.round(v * 255), Math.round(v3 * 255), Math.round(v1 * 255)] ;
        break ;
        case 1 :
            rgb = [ Math.round(v2 * 255), Math.round(v * 255), Math.round(v1 * 255) ] ;
        break ;
        case 2 :
            rgb = [ Math.round(v1 * 255), Math.round(v * 255), Math.round(v3 * 255) ] ;
        break ;
        case 3 :
            rgb = [ Math.round(v1 * 255), Math.round(v2 * 255), Math.round(v * 255) ] ;
        break ;
        case 4 :
            rgb = [ Math.round(v3 * 255), Math.round(v1 * 255), Math.round(v * 255) ] ;
        break ;
        case 5 :
            rgb = [ Math.round(v * 255), Math.round(v1 * 255), Math.round(v2 * 255) ] ;
        break ;
    }
} else {
    rgb = [ Math.round(v * 255), Math.round(v * 255), Math.round(v * 255) ] ;
}
```
</div></details>

# 4. おわりに
サイト上で色の試行が手軽にできるChrome拡張機能の紹介をしました。


https://chromewebstore.google.com/detail/brightnesscolor-changer/mdifhjjkanpdehdgkoieaofpjnnceglb