picture,imgタグについての備忘録

## はじめに
HTMLで画像を表示する際に利用する`<picture>` `<img>`には多くの属性があったり、適切な画像に切り替える方法などありますが、これまでに学んできた知識を一度まとめたいと思い記事にしました。
基本的によく利用する属性をまとめています。
（備忘録です）

## 目次
1. [基本形](#基本形)
1. [widthとheight](#widthとheight)
1. [loading属性](#loading属性)
1. [picture](#picture)
1. [画像ファイル形式について](#画像ファイル形式について)
1. [まとめ](#まとめ)
1. [参考文献](#参考文献)

## 基本形
```html
<img src="./sample.jpg" alt="画像の代替テキスト" />
```
`<img>`を利用する場合は`src`属性と`alt`属性を利用します。
`src`には画像へのパスを入れ、`alt`には画像を説明する代替テキストをいれます。

`alt`属性は必須ではありませんが、アクセシビリティ向上のため基本的に設定することが推奨されます。
スクリーンリーダーの読み上げや、ネットワークエラー・リンク切れなどで画像を表示できない場合で`alt`に設定したテキストが代替テキストとして表示されます。

`alt`に設定する代替テキストをどのように記述すればよいか迷うことがよくあると思いますが、以下のURLがとても参考になります。

https://jis8341.net/jirei_sample/jirei_chapter_01.html

## widthとheight
画像の固有の幅と高さを設定します。

```html
<img src="./sample.jpg" alt="" width="640" height="427" />
```

任意ですが、レイアウトシフト（CLS）の対策で基本的には記述するのがベターです。

レイアウトシフトとは、Webサイトを開いたときにページの読み込み中または読み込み後に発生する、予期せぬレイアウトの変化のことを指します。画像や広告などの動的に読み込まれるコンテンツなどが原因で起こり、ユーザー体験に悪影響を及ぼすことがあります。
（以下参考URL内の動画でページのレイアウトが変わる様子が示されており、慌てている様子がちょっとおもしろいです）

https://web.dev/articles/cls?hl=ja

VSCodeを利用している場合、コマンドパレットから「イメージサイズの更新」を選択することで、自動的に`width`と`height`を`<img>`に含めることが可能です。
（画像までのパスが正しく通っている必要があります）

![2024-03-0614.18.27-ezgif.com-video-to-gif-converter.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1995378/f82389c0-23c3-a6c0-23a9-a4943dd1dc25.gif)

## loading属性
`loading`属性はブラウザがどのように画像を読み込むかを示す属性値です。
いわゆる遅延読み込み設定です。

https://developer.mozilla.org/ja/docs/Web/Performance/Lazy_loading

```html
<img src="./sample.jpg" alt="" loading="lazy" width="640" height="427" />
```

`loading`属性を設定しない場合`eager`になります。（遅延読み込みしない設定）
`eager` `lazy`と2つの値がありますが、自分はファーストビュー付近にある画像以外の`<img>`要素すべてに`lazy`をつけています。
ページの読み込み速度に影響する画像は画面外で遅延読み込みすることによって、初回読み込みの時間を短縮することができます。

また`lazy`指定の場合は必ず`width` `height`をつけます。初期状態で幅・高さが0pxになるので、つけておかないと読み込み時にレイアウトシフトが発生してしまいます。

:::note warn
`decoding`属性について
`loading`属性と合わせてよく紹介されている`decoding="async"`ですが、挙動について理解できておらず、また以下のURL参考に自分もブラウザの挙動に任せることとしました。
:::

https://zenn.dev/ixkaito/articles/deep-dive-into-decoding

## picture
`<picture>`はPC・SPなどで表示する画像が違うものや、iPhoneのRetinaディスプレイなどの画面や端末の条件に応じて画像を出し分けられる要素です。

https://developer.mozilla.org/ja/docs/Web/HTML/Element/picture

### メディアクエリで画像を出し分け
例えばアスペクト比の異なるPC用とSP用の画像を切り替える場合、`source`を用いて以下のように分岐させることができます。

```html
<picture>
  <source
    media="(max-width: 576px)"
    srcset="/sample_sp.jpg"
    width="640"
    height="960"
  />
  <img src="/sample_pc.jpg" width="640" height="960" alt="" />
</picture>
```

これで基本sample_pc.jpgを読み込んで表示し、画面幅が576px以下の場合は代わりにsample_sp.jpgを表示するようになります。

:::note warn
display:noneでの切り替え
よく記事などで紹介されている、display:noneを利用した画像切り替えがありますが、pc・sp両方の画像を読み込んでしまうので、基本的にはpicture/sourceを利用する方法を推奨します。
（picture/sourceだと片方しか読み込まれないのでページの読み込み時間がその分早くなります。）
:::

ちなみに`source`にも`width` `height`を指定できるので、アスペクト比が異なる場合は記述したほうがよいです。

また、PC・SPで全く別の画像を出し分ける時は、`alt`テキストに気をつけた方がよいです。
`source`には`alt`テキストが設定できず、`<img>`にも`alt`は1つしか設定できないので...
（この場合の最適解がわからない）

### 画面解像度での出し分け
MacやiPhoneに搭載されているRetinaディスプレイなどの高解像度ディスプレイに対応する場合は`srcset`属性を使います。

```html
<picture>
  <source srcset="./sample@2x.jpg 2x, ./sample@3x.jpg 3x" />
  <img src="./sample.jpg" width="640" height="960" alt="" />
</picture>
```

`srcset`属性に画像へのURLと対応する解像度を記述します。これで一般的なディスプレイと高解像度ディスプレイでの画像の出し分けが可能になります。

### 画面幅（ビューポート）での出し分け
ブラウザの画面幅に応じて、画像を出し分けることも可能です。

```html
<picture>
  <source srcset="./sample-576w.jpg 576w, ./sample-992w.jpg 992w" />
  <img src="./sample.jpg" width="640" height="960" alt="" />
</picture>
```

この記述だと画面幅が576pxの場合にsample-576w.jpgが、992pxの場合にsample-992w.jpgが表示されるようになります。
また、`sizes`属性を利用することで場面に応じた画像サイズを指定する事が可能になります。

```html
<picture>
  <source srcset="./sample-576w.jpg 576w, ./sample-992w.jpg 992w" sizes="20vw"/>
  <img src="./sample.jpg" width="640" height="960" alt="" />
</picture>
```

例えば上記の指定だと`sizes`属性の指定値が`20vw`になっているため、画面幅の1/5サイズで足りる画像を選択して表示することができます。
（例：画面幅1920pxの場合、画像のサイズは1/5の384pxになり、表示される画像はsample-576w.jpgになる）

:::note info
`srcset` `sizes`属性ともに指定されている値が適切かどうかを検証してくれる[RespImageLint](https://ausi.github.io/respimagelint/)というサービスがあるみたいです。
（下記参考記事）
:::

https://www.tak-dcxi.com/article/my-blogs-responsive-coding#%E3%83%AC%E3%82%B9%E3%83%9D%E3%83%B3%E3%82%B7%E3%83%96%E3%82%A4%E3%83%A1%E3%83%BC%E3%82%B8%E3%81%AEsrcset%E5%B1%9E%E6%80%A7%E3%81%A8sizes%E5%B1%9E%E6%80%A7%E3%81%AE%E6%9C%80%E9%81%A9%E5%8C%96%E3%81%AFrespimagelint%E3%81%AB%E4%BB%BB%E3%81%9B%E3%82%8B


## 画像ファイル形式について
自分がよく使う拡張子については以下の4つです。
- JPEG
- PNG
- SVG
- WebP

ベクター形式（アイコンやイラストなど）のデータについては基本svgを、ラスター形式（写真など）のデータについては基本的にはWebPを扱うことが多いです。
今までWebPのフォールバックとしてjpg,pngを`<picture>`で出し分けて実装していましたが、Safariも14系から対応してるのでそのままWebPのみ利用する形で基本問題ないです。

https://caniuse.com/?search=webp

WebPについては基本的にjpgやpngよりは軽くなりますが、場合によってはWebPの方が重くなることもあるので一度圧縮してみて比較しどちらを利用するか決めた方がいいです。
（pngとかは内容によってはWebPより軽い事があります。）

また、高画質な上に圧縮率も高いAVIFという次世代のファイル形式もモダンブラウザで対応しているので、こちらも徐々に移行できたらいいなと考えています。
（safariが16系からなのでもう少し先かな...）

https://caniuse.com/?search=avif

## まとめ
画像はリソースのダウンロードでWebフォントに次いでネックなところなので、今後も知識をアップデートしつつ、適切な処理方法を行いたいです。

## 参考文献
https://developer.mozilla.org/ja/docs/Web/HTML/Element/img

https://developer.mozilla.org/ja/docs/Web/HTML/Element/picture

https://jis8341.net/jirei_sample/jirei_chapter_01.html

https://web.dev/articles/cls?hl=ja

https://zenn.dev/ixkaito/articles/deep-dive-into-decoding

https://b-risk.jp/blog/2022/09/picture-srcset/#sizes

https://www.tak-dcxi.com/article/my-blogs-responsive-coding#%E3%83%AC%E3%82%B9%E3%83%9D%E3%83%B3%E3%82%B7%E3%83%96%E3%82%A4%E3%83%A1%E3%83%BC%E3%82%B8%E3%81%AEsrcset%E5%B1%9E%E6%80%A7%E3%81%A8sizes%E5%B1%9E%E6%80%A7%E3%81%AE%E6%9C%80%E9%81%A9%E5%8C%96%E3%81%AFrespimagelint%E3%81%AB%E4%BB%BB%E3%81%9B%E3%82%8B