👌

color-thiefで画像に応じて背景色を動的に変える

2022/10/08に公開

はじめに

画像に応じて背景色(css による background-color 等)を動的に変える実装をしました。

今ある画像だけでいい感じに一覧表示したい

以下のような透過画像を表示する場合、背景色によっては見づらくなってしまいます。
白い四角形の画像

特に画像を一覧表示する場合、様々な色合いの画像が存在するので、背景色を一意に定めるだけでは不十分です。
画像を一覧表示のサムネイル画像を用意する手段もありますが、既存の画像のサムネイル画像を作る作業が必要になります。

そこで、今回はcolor-thief-reactで画像の色を取得し、背景色を動的に変える実装をしました。

今回行った実装

  1. color-thief-reactで画像からDominatColorを取得
  2. dominant colorを元に、適した背景色を決定する
    • 明度が高ければ、背景色をグレーにする
    • 明度が低ければ、背景色を白にする

コード

主要なコードは以下の通りです
https://github.com/yukyu30/change-background-with-color-thief-react/blob/main/src/App.tsx

https://github.com/yukyu30/change-background-with-color-thief-react/blob/main/src/ImageCard.tsx

背景色を変える

color-thiefは以下のような使い方をします。dataには画像で一番使われている色(Dominant Color)が保存されます。

<Color src={IMAGE_URL}>
  {({ data, loading, error }) => (
    <div style={{ color: data }}>
      Text with the predominant color
    </div>
  )}
</Color>

以下のようにして、画像の背景色が変わるようにします。

 <Color src={props.imgSrc} format="rgbArray">
  {({ data }) => {
    //dominant colorから背景色を決定する
    const bgColor = getBgColor(data)
    return(     
      <div>
        <img src={props.imgSrc} style={{ backgroundColor: bgColor }}/>
        <div>dominant color: {data?.map((value: any) => <span>{value} </span>)}</div>
      </div>
    )
  }}
</Color>

getBgColor

dominant colorの明度が高いか、低いかで背景色を決定します。
白のみの画像では、dominant colorが取得できず、undefinedになるので注意が必要です。

const getBgColor = ( rgb: ArrayRGB | undefined ) => {
  if ( rgb !== undefined ) {
     //明度が高い画像の背景色はgray
    //明度が低い画像の背景色はwhite
    return isLight( rgb ) ? 'gray' : 'white'
  } else {
    //白のみの画像の場合は背景色をgrayにする
    return 'gray'
  }
}

isLight

const isLight = ( rgb: ArrayRGB ) => {
  const [r, g, b] = rgb.map(( value: any ) => value )
  //130は自分で決めた値
  if (( r * 299 + g * 587 + b * 114 ) / 1000 >= 130 ){
    return true
  }
  return false
}

背景色から適した文字色が白か黒か判定する方法を応用して、明度が高いか、低いかを決定します。今回の場合は

 ( r * 299 + g * 587 + b * 114 ) / 1000 >= 130

と書かれている通り、左辺の値が130以上だったら、明度が高いとして、trueを返します(=背景色はグレー)。

https://lab.syncer.jp/Web/JavaScript/Snippet/55/
https://www.w3.org/TR/AERT/#color-contrast

実装後で比較

  1. 背景色を白色に固定
  2. 背景色をグレーに固定
  3. 今回の実装:背景色を動的に変える

画像によって、背景色が変わっており、画像が見やすくなりました。

おわりに

画像の色に応じて、背景色を変えるという手法について書いてみました。
color-thiefには画像からカラーパレットを作成できるので、アカウントのプロフィール画像でWebサイトのテーマが変わるといった実装もできそうですね。


おまけ:他に試した実装方法

背景色を動的に変えるために、試した手法を書いておきます。

canvasで1pxの画像で貼り付け、色を取得

  1. canvasで画像を読み込み、縦横1pxのサイズで貼り付ける
  2. 貼り付けた1pxの画像の色を取得する
    透過されている部分が多いと、取得した色が透明rgba(0,0,0,0)になりました。今回はアルファ値のない色が欲しかったので、こちらの実装は見送りました。

vibrant.jsで色を取得

https://jariz.github.io/vibrant.js/

color-thiefのように画像からカラーパレットを生成してくれるライブラリです。

Extract prominent colors from an image.

とあるように、画像で一番使われている色ではなく、目立った色を取得します。そのため、主要な色から明度を判定をするには不向きでした。

Discussion