画像の支配色を返すカスタムhook

画像の支配色を返すカスタムhook

投稿日: 2025年10月03日

Tips
要約
  • 画像の平均色を取得するためのカスタムフックuseImageAverageColorを作成した。
  • このフックはCanvasを使用して画像を描画し、ピクセルデータから平均RGB値を計算する。
  • CORS制限を考慮し、エラーハンドリングも実装されている。
音声で記事を再生
0:00

画像の背景色を、画像に近い色味にしたいと思いました。

↓こんな感じ

画像の支配色を返すカスタムhook|ShiftBブログ

画像から支配色のカラーコードを取得するライブラリがメンテされていないものばかりだったのですが、
canvasを用いることすぐに作れたので、共有します。

useImageAverageColor.ts

import { useEffect, useState } from "react";

const FALLBACK_COLOR = "#fff";

export function useImageAverageColor(src: string | null | undefined) {
  const [color, setColor] = useState<string>(FALLBACK_COLOR);

  useEffect(() => {
    if (!src) {
      setColor(FALLBACK_COLOR);
      return;
    }

    if (typeof window === "undefined") {
      return;
    }

    let isCancelled = false;
    const img = new window.Image();
    img.crossOrigin = "anonymous";
    img.src = src;

    const handleLoad = () => {
      if (isCancelled) return;

      const width = 32;
      const height = 32;
      const canvas = document.createElement("canvas");
      canvas.width = width;
      canvas.height = height;
      const ctx = canvas.getContext("2d");
      if (!ctx) return;

      try {
        ctx.drawImage(img, 0, 0, width, height);
        const { data } = ctx.getImageData(0, 0, width, height);

        let r = 0;
        let g = 0;
        let b = 0;
        let count = 0;

        for (let i = 0; i < data.length; i += 4) {
          r += data[i];
          g += data[i + 1];
          b += data[i + 2];
          count++;
        }

        if (count === 0) return;

        const red = Math.round(r / count);
        const green = Math.round(g / count);
        const blue = Math.round(b / count);

        setColor(`rgb(${red}, ${green}, ${blue})`);
      } catch {
        // CORS 制限などで getImageData が失敗した場合
        setColor(FALLBACK_COLOR);
      }
    };

    const handleError = () => {
      if (isCancelled) return;
      setColor(FALLBACK_COLOR);
    };

    img.onload = handleLoad;
    img.onerror = handleError;

    return () => {
      isCancelled = true;
      img.onload = null;
      img.onerror = null;
    };
  }, [src]);

  return color;
}

使い方

const agerageColor = useImageAverageColor("https://hogehoge.com/image_path")

シェア!

Threads
icon
ぶべ
Webの修行中 / 個人開発奮闘中 / ベンチプレス110kg / Reactの先生
Loading...
記事一覧に戻る
Threads
0