インテリアの色合わせを便利にしたい

## この記事について
この記事は、[某企業アドベントカレンダー2024](https://adventar.org/calendars/10291)の15日目の記事です。

## やりたいこと
突然ですがみなさん、インテリアの家具選びで困っていませんか？お店に行って「これいい！」と思って購入しても、家に置いてみたら「あれ？なんか違う…」となった経験ありませんか？僕個人的に、この問題の多くは家具の「大きさ」と「色」の2つが原因だと考えています。

- 思ってたよりも大きかった
- 家に置いてみたら圧迫感がすごい
- 他の家具と色が違って浮いて見える
- カラフルになってしまって統一感がない　etc.

大きさの問題に関しては、事前に部屋の寸法を測っておいて、動線を意識してどのくらいの大きさがちょうどいいのかを計算すればどうにかなります。一方で、色の問題はかなり難しいです。自分の今の部屋にはどういう色があって、買おうとしているそれは同じ色をしているのか、違う色の場合トーンは合っているのかなど、意識するべきポイントがたくさんあります。あらかじめ自分の部屋の写真を撮っておくでも良いですが、レンズ、ソフトウェア、ディスプレイの発色に依存するため、「肉眼で見ている色と違う！」となってしまう可能性があります。

前置きが長くなりましたが、まとめると今回の要件は以下の2つです。
- 自分の部屋にある色を「正確に」保存したい
- 自分の部屋に合う色を楽に見つけたい

## 実装
要件を満たすアプリケーションを作っていきます。使用する技術は、プラットフォームの拡張性を考えてWeb、iOS、Androidの3つを同時に開発できるReact Nativeにします。フロントエンドだけで要件を満たせるため、バックエンドの構築は必要ありません。全部を解説すると冗長になるので、ピックアップして書いていきます。

### プロジェクトの作成
```bash
npx create-expo-app --template    
```

### 依存関係のインストール
```bash
# Expo関連パッケージをインストール
npx expo install @expo/metro-runtime @react-native-async-storage/async-storage @react-native-clipboard/clipboard @react-native-community/slider expo-clipboard expo-gl

# その他のパッケージをインストール
npm install gl-react gl-react-expo   
```

### 色をローカルストレージに保存
今回はデータベースなどは用意しません。ローカルストレージに色を保存します。ローカルストレージに保存したデータは、手動で削除しない限り消えることはありません。

```ts
  const PRESETS_KEY = "@ColorPicker:presets";
  
  // プリセットカラーの状態管理
  const [presets, setPresets] = useState<RGB[]>([]);
  
  /**
   * プリセットカラーをローカルストレージに保存する関数
   * @param presets 保存するプリセットカラーの配列
   */
  const savePresets = async (presets: RGB[]) => {
    try {
      const jsonValue = JSON.stringify(presets);
      await AsyncStorage.setItem(PRESETS_KEY, jsonValue);
    } catch (e) {
      console.error("プリセットカラーの保存に失敗しました:", e);
    }
  };

  /**
   * ローカルストレージからプリセットカラーを取得する関数
   * @returns プリセットカラーの配列
   */
  const loadPresets = async (): Promise<RGB[]> => {
    try {
      const jsonValue = await AsyncStorage.getItem(PRESETS_KEY);
      return jsonValue != null ? JSON.parse(jsonValue) : [];
    } catch (e) {
      console.error("プリセットカラーの取得に失敗しました:", e);
      return [];
    }
  };

  useEffect(() => {
    const initializePresets = async () => {
      const storedPresets = await loadPresets();
      if (storedPresets.length > 0) {
        setPresets(storedPresets);
      } else {
        // 初期プリセットが存在しない場合はデフォルトを保存
        await savePresets(presets);
      }
    };
    initializePresets();
  }, []);

  useEffect(() => {
    savePresets(presets);
  }, [presets]);
```

### 同じトーンの色を生成
トーンを合わせるとインテリアにまとまりが生まれます。そこで、同じトーンの色を12色生成するコードを実装します。

参考：https://blind-mart.com/guide/7276/?srsltid=AfmBOooRgzhFHQsmydmmv3ucPMS46zbR30GxNB5yBDEBzTjEZkaHnxyc

```ts
  /**
   * 現在のHSLから色相を30度ずつ変えた12色を生成する関数
   * @param currentHSL 現在の色のHSL値
   * @returns 12色のRGB配列
   */
  const generateSimilarTones = (currentHSL: HSL): RGB[] => {
    const { h, s, l } = currentHSL;
    const tones: RGB[] = [];
    const step = 360 / 12; // 30度ずつ

    for (let i = 0; i < 12; i++) {
      const newHue = (h + step * i) % 360;
      const rgb = hslToRgb(newHue, s, l);
      tones.push(rgb);
    }

    return tones;
  };
```

## 完成
上記に加えていろいろやるとこんな感じになりました。肉眼で見ている色とディスプレイに表示される色を比較しながら調整し、保存すればいつでもどこでも自宅にある色が正確に把握することができるようになります。また、合わせやすい同じトーンの色を選択しやすくなりました。

![スクリーンショット 2024-12-15 0.44.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/922250/345821c3-91df-9ee4-6683-5dfb58f0967b.png)

## 公開
今回は一旦Webアプリケーションとして公開します。iOSやAndroidアプリケーションとしての公開はまたの機会に。決してめんどくさいからではありません。決して。

ホスティングサービスとして、[Cloudflare Pages](https://www.cloudflare.com/ja-jp/developer-platform/products/pages/)を使用します。理由は無料利用で出来る範囲がめちゃくちゃ大きいからです。

### Cloudflare Pagesへデプロイ
1. Cloudflareにログインし、ダッシュボードの左メニューにある「Workers & Pages」 を選択します。

2. Pagesタブを開き、GitHubのリポジトリに接続します。
![スクリーンショット 2024-12-15 1.10.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/922250/c4dc757c-c0c5-f1aa-248d-40399569f3c2.png)

3. プロジェクト名とブランチを設定します。続けて、ビルドの設定でビルドコマンドを以下のように設定します。
    ```bash
    npx expo export --platform web
    ```

これでCloudflareが自動でビルドとデプロイをしてくれます。

ということで公開されたURLが[こちら](https://colormanager.pages.dev/)です。
ぜひ使ってみてね。

## まとめ
インテリアの色合わせに便利なアプリケーションを作ってみました。今回初めて記事を投稿しましたが、結構楽しいものですね。またやりたいです。

それでは、良いインテリアライフを。

## 参考文献
- [Expo Documentation](https://docs.expo.dev/workflow/web/)