モダンWEBサイトにおける画像最適化について

# はじめに
この記事は『Picture perfect images with the modern img element』という記事を参考に画像最適化に関しての重要な観点をまとめたものです。

https://stackoverflow.blog/2022/12/27/picture-perfect-images-with-the-modern-element/

画像はWEBにおいても重要な役割を担っており、2021年時点で1つ以上の画像を含むWEBサイトは`95.9%`、faviconなども含めて1回でも画像取得のリクエストを送信するWEBサイトは`99.9%`であるという調査もあります。

**さらには、LCP要素の`約70%`が画像であるという調査が出ています。**

Largest Contentful Paint（LCP）は、Googleが提唱するCore Web Vitalsの1つで、ウェブページのパフォーマンスを測る指標です。詳しくは次の章で解説します。

つまり、WEBのパフォーマンスにおいて画像の最適化は重要な観点でありユーザ体験に直結することがわかります。

https://almanac.httparchive.org/en/2021/media

# 画像最適化に関するCore Web Vitalの指標について
まずはWEBサイトのパフォーマンスの指標となるCore Web Vitalについて解説します。またここでは、画像の最適化に関係する指標を抽出して、`LCP` `CLS` `FID`について扱います。

https://web.dev/i18n/ja/vitals/#core-web-vitals

## Largest Contentful Paint (LCP)

> Largest Contentful Paint （最大視覚コンテンツの表示時間、LCP） は、**知覚される読み込み速度を測定するための重要なユーザーを中心とした指標です。** これは、LCP がページの読み込みタイムラインにおいてページのメイン コンテンツが読み込まれたと思われる時点を示すためです。LCP を高速にすることで、そのページが便利であることをユーザーに強く印象付けることができるようになります。

良好なユーザー体験を提供するために、サイトはLCPが**2.5秒以下**になるように努力する必要があります。

### 考慮される要素
- `<img>` 要素
- `<svg>` 要素内の `<image>` 要素
- `<video> 要素`
- `url()` 関数を介して読み込まれた背景画像が含まれている要素
- テキスト ノードやその他のインラインレベルのテキスト要素の子要素を含むブロックレベル要素

### LCPが影響を受ける要因

- サーバーの応答時間が遅い
- レンダリングを妨げる JavaScript および CSS
- リソースの読み込み時間
- クライアント側のレンダリング

https://web.dev/lcp/

## Cumulative Layout Shift (CLS)

> Cumulative Layout Shift （累積レイアウト シフト数、CLS） は、**視覚的な安定性を測定するための重要なユーザーを中心とした指標です。** これは、ユーザーが予期しないレイアウトシフトに遭遇する頻度の数値化に役立つ指標であり、CLS が低ければ低いほど、そのページが快適であることが保証されます。

ページコンテンツの予期しない移動は、一般的にリソースが非同期的に読み込まれたり、ページ上の既存のコンテンツの上側にDOM要素が動的に追加されたりする場合に発生します。

良好なユーザー体験を提供するために、サイトはCLSスコアが**0.1以下**になるように努力する必要があります。

### CLSの改善方法
- 画像要素や動画要素に必ず`size`属性を付ける手法や、CSSを駆使したアスペクト比対応ボックスなどの手法を用いて必要なスペースを確保する。
- ユーザーの操作に応じる場合を除き、既存のコンテンツの上側にコンテンツを挿入しない。
- レイアウト変更のトリガーとなるプロパティを利用したアニメーションよりも`transform`アニメーションを優先する。

https://web.dev/cls/

## First Input Delay (FID)
> First Input Delay (初回入力までの遅延時間、FID) は、**読み込みの応答性を測定するための重要なユーザーを中心とした指標です。** これは、応答のないページを操作する場合のユーザー体験を数値化したものであり、FID が短ければ短いほど、そのページがユーザーにとって使いやすいものであることが保証されます。

良好なユーザー体験を提供するために、サイトはFIDが**100ミリ秒以下**になるように努力する必要があります。

https://web.dev/fid/

# LCPを改善する画像最適化
多くの現代のウェブ体験では、ページの読み込みが完了したときに最も大きな可視要素となるのが画像です。これにはヒーロー画像やカルーセル、ストーリー、バナーからの画像などが含まれます。

画像が最大のコンテンツ要素である場合、画像の読み込み速度がLCPに大きな影響を与えることがあります。その際、画像の圧縮を適用する（例えばSquooshやImageOptimなど）と、モダンな画像フォーマットを使用することに加えて、`<img>`要素を微調整して、画像の最適なレスポンシブバージョンを提供したり、遅延ロード（lazy-load）したりすることができます。

https://squoosh.app/

https://imageoptim.com/mac

## LCPが最大である要素を特定する
Lighthouseには「Largest Contentful Paint element」という項目があり、どの要素が最大のLCP要素であるかを特定する事ができます。また、ホバーするとメインブラウザ内でその要素をハイライトしてくれるため、LCP要素の特定も容易になります。
![LCP1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1071889/883ad286-fced-8f0b-dbce-287f2ff16ffd.png)

さらに詳しい画像の情報を得たい場合は、Chrome DevToolsの「Elements」パネルにて画像の詳細情報を得る事ができます。まずはこのようにして、LCP要素の特定と情報の整理をしましょう。
![LCP2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1071889/a0cbda9b-4dca-4e0d-b579-b5c2625e83ce.png)

## 画像のレスポンシブ化
もしも画像の解像度をレスポンシブに切り替えたい場合はどのように実装すればいいでしょうか？一般的な`<img>`では、ブラウザに対して単一のソースファイルしか提供していません。

その場合、**`srcset`属性と`sizes`属性**を利用することでブラウザが最適な情報を選べるようになり、大小サイズの異なる画像をレスポンシブに表示させることが可能になります。

```html
<img src="keyboard-800w.jpg"
    alt="A beautiful pink keyboard."
    width="400"
    height="400"
    srcset="keyboard-400w.jpg 400w,
            keyboard-800w.jpg 800w"
    sizes="(max-width: 640px) 400px,
            800px">
```

### srcset属性
`srcset`属性は、ブラウザが選択することができる画像のセットと、それぞれの画像の寸法を定義します。

- 画像ファイル名 （keyboard-800w.jpg）
- 画像の幅の内在的なピクセル数 （400w）
    - なお、これは単位に`px`ではなく`w`を使用します

https://developer.mozilla.org/ja/docs/Web/API/HTMLImageElement/srcset

### sizes属性
`sizes`属性は、一連のメディア条件（例えば画面の幅）であり、特定のメディア条件が成立したときに、どの寸法の画像を選択するのが最適かを示します。

- メディア条件（max-width:640px）
- メディア条件が成立したときに埋めるスロットの幅 （400px）

これらの属性を使用すると、ブラウザーは次のようになります。

1. その端末の幅を見る。
2. `sizes`リストの中のどのメディア条件が真であるかを確認する。
3. そのメディア照会で与えられたスロットサイズを見る。
4. 選択したスロットサイズに最も近い`srcset`リストで参照される画像を読み込みます。

https://developer.mozilla.org/ja/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images

## Device Pixel Ratio （DPR）を調整する
デバイスピクセル比（Device Pixel Ratio、DPR）は、**物理的なピクセルと論理的なピクセル（CSS ピクセル）の比率を示す値です。**

物理的なピクセルとは、ディスプレイに実際に存在する微小な点（ピクセル）のことを指します。一方、論理的なピクセル（CSSピクセル）は、ウェブページやアプリケーションのデザイン要素がどのように表示されるかを決定する抽象的な単位です。

人の目では2倍から3倍のDPRの画像と、それ以上の高解像度の画像との違いを区別することは難しいです。そのため、必要以上に高解像度な画像を使用することはパフォーマンスに影響を与えます。
![LCP3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1071889/dc1c8be4-b38a-b767-6c51-9f4d4ad8c295.png)

## 最新の画像フォーマットに対応させる
アートディレクションによって私たちはユーザのディスプレイに応じて異なる画像を提供する事ができます。画像のレスポンシブ化では画面に応じて異なるサイズの画像を読み込んでいました。その一方、アートディレクションではユーザの画面に応じて全く異なる画像を読み込む事ができます。

```html
<picture>
    <source srcset="keyboard.avif" type="image/avif">
    <source srcset="keyboard.webp" type="image/webp">
    <source srcset="keyboard.jpg" type="image/jpeg">
    <img src="keyboard.jpg" alt="Omg a keyboard">
</picture>
```

ブラウザは`<picture>`要素を使用して表示する画像の形式を選択できます。`<picture>`要素は複数の`<source>`要素と単一の`<img>`要素を受け取って異なるフォーマットの画像を参照します。もしも一致するソースがない場合は、`<img>`要素に指定された画像をfallbackする仕組みです。

各画像フォーマットによる比較は以下の画像が参考になります。
![LCP.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1071889/a8e89db1-f058-5f85-3675-d87a83156210.png)

https://cloudinary.com/blog/time_for_next_gen_codecs_to_dethrone_jpeg

## 画像をプリロードする
LCPを最適化したい場合にプリロードは役に立ちます。プリロードを使用すると、HTMLで検出される前にできるだけ早く読み込む必要がある重要なリソースをブラウザに知らせることができます。

```html
<link rel="preload" as="image" href="keyboard.webp" type="image/webp">
```

また、Chrome 73から、ブラウザはimgタグを検出する前に、srcsetに指定されたレスポンシブ画像の適切なバリアントをプリロードできるようになったため、サイトの構造によっては、画像の表示が大幅に高速化される可能性があります。

```html
<link rel="preload" as="image" 
     href="keyboard.jpg" 
     imagesrcset="
        poster_400px.jpg 400w, 
        poster_800px.jpg 800w, 
        poster_1600px.jpg 1600w" 
    imagesizes="50vw">
```

https://web.dev/preload-responsive-images/

# CLSを改善する画像最適化
意図しないレイアウトの移動はユーザの気を散らしてしまいます。CLSの最も一般的な原因にはサイズがあらかじめ指定されていない画像が含まれ、読み込み時にコンテンツを押し上げたり、定位置に収まりきらない事があります。つまり、CLSを無視するということは読み込み前に十分なスペースが確保されていないことを意味します。

```html
<img src="keyboard.jpg"
    alt="A beautiful pink keyboard."
    width="400"
    height="400">
```

画像に幅と高さが指定されると、ブラウザは画像がダウンロードされるまでの間、その画像のために確保するべきスペースを知る事ができます。逆に指定しなかった場合は、ブラウザが画像のスペースを把握できないため、レイアウトがずれてしまう事があります。

## 画像のサイズ指定におけるベストプラクティス
最近のブラウザーでは画像のデフォルトのアスペクト比を画像の`width`と`height`属性に基づいて設定するようになっているため、レイアウトシフトの発生を防ぐためにそれらを設定しておくと便利です。

```html
<!-- アスペクト比を 640:360 (16:9) に設定します -->
<img src="puppy.jpg" width="640" height="360" alt="風船と子犬" />
```

```css
img {
  aspect-ratio: attr(width) / attr(height);
}
```

アスペクト比は、画像が読み込まれる前に`width`と`height`属性に基づいて計算されます。この情報は、レイアウト計算の初期段階で供給され、画像の幅を特定の値 (たとえば width: 100%) に設定するように指示があると、アスペクト比はすぐに高さの計算に使用されます。



https://web.dev/i18n/ja/optimize-cls/#%E6%9C%80%E6%96%B0%E3%81%AE%E3%83%99%E3%82%B9%E3%83%88-%E3%83%97%E3%83%A9%E3%82%AF%E3%83%86%E3%82%A3%E3%82%B9

## オプションのフォントを事前読み込みすることにより、レイアウトシフトや非表示テキストの瞬間的表示 （FOIT）を防ぐ
CLSの悪化する原因には画像のサイズ未指定の他に、WEBフォントのダウンロードとレンダリング時にも該当します。

- FOIT（Flash of invisible text）
    - ナビゲーション直後のWebフォントがまだ読み込まれていない状態でページが表示され、その部分のテキストが空白または透明になり、font block period 経過後に突然表示される現象
- FOUT（Flash of unstyled text）
    - 表示されているフォールバックフォントが、font swap period 中に新しいフォントに突然入れ替わる現象

この影響に対しては以下の対応で最小限に抑える事ができます。

- Webフォントに`<link rel=preload>`を使用する
    - 事前読み込みされたフォントは初期の描画に含まれている可能性が高く、その場合にはレイアウトシフトは発生しません。
- `<link rel=preload>`と`font-display: optional`を組み合わせる
    - `font-display: optional`の指定により、何らかの理由でウェブフォントが読み込めない場合でも、ブラウザはスムーズにフォールバックフォントに切り替えることができます。

https://web.dev/preload-optional-fonts/

# FIDを改善する画像最適化
画像はページ読み込み時にユーザーの帯域幅やCPUをブロックする可能性があります。特に低速の接続や低端末のモバイルデバイスでは帯域幅の飽和につながり、重要なリソースのロードの邪魔になる可能性があります。

First Input Delay（FID）は、サイトのインタラクティブ性と応答性に関するユーザーの第一印象を把握するための指標です。メインスレッドのCPU使用率を減らすことで、FIDも減らすことができます。

## 画像の遅延ローディング
ユーザがページをスクロールするまで見えないような画像について考えてみましょう。ユーザが画面描画時に見ることのできない画像を必死に読み込む必要はありません。その画像がページの重要なコンテンツの読み込みを遅くしてしまう場合は特にです。

**その場合は、loading属性を用いて遅延ロードしましょう**

```html
<img src="donut.jpg"
    alt="A delicious pink donut."
    loading="lazy"
    width="400"
    height="400">
```

LighthouseのOpportunities欄に遅延ロードが可能な画像がリストアップされていますので、こちらも参考に改善する事ができます。
![FID2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1071889/66f705a1-a6b3-21cd-44c9-ef80bf64a7d5.png)

# まとめ
今回は`LCP` `CLS` `FID`というCore Web Vitalの指標を改善することによる画像の最適化についてまとめました。より高速で快適なWEBサイトの構築において画像の存在は非常に重要です。Lighthouseの結果やDev Toolも活用していきながら、画像の最適化に取り組んでいきましょう。

# 参考資料
https://stackoverflow.blog/2022/12/27/picture-perfect-images-with-the-modern-element/

https://web.dev/i18n/ja/vitals/#core-web-vitals

https://web.dev/lcp/

https://web.dev/cls/

https://web.dev/fid/

https://squoosh.app/

https://developer.mozilla.org/ja/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images

https://cloudinary.com/blog/time_for_next_gen_codecs_to_dethrone_jpeg

https://web.dev/preload-responsive-images/

https://web.dev/i18n/ja/optimize-cls/#%E6%9C%80%E6%96%B0%E3%81%AE%E3%83%99%E3%82%B9%E3%83%88-%E3%83%97%E3%83%A9%E3%82%AF%E3%83%86%E3%82%A3%E3%82%B9

https://web.dev/preload-optional-fonts/