フロントエンド・コーダーが知っておきたい画像の知識
目次
目次
- 拡張子による違い
- png
- jpg
- gif
- svg
- webp
- 画像圧縮の仕組み
- Retina対応とは
- HTMLでの画像の取り扱い
- picture / srcset / sizes
- 書き方いろいろ
- 画像は何パターン用意したらいいか / どの指定がベストか(結論見たい人はここ)
- 有名技術ではどう指定されてる?
- レイアウトシフト/ CLS
- loading属性
- decoding属性
1. 拡張子による違い
png
- 256色を扱うPNG8、1677万7216色を扱うPNG24、1677万7216色+256色を扱うPNG32がある(規格が3種類あるが、拡張子は全てpngなので、書き出す時以外は特に気にしなくていい)
- 透過可能
- 可逆圧縮の画像形式なので、低画質で保存しても元の画質に戻すことができる
- jpgやgifに比べてファイルサイズが大きくなりがち
- ロゴやアイコン、イラストなどで使われることが多い
jpeg(jpg)
- 1677万7216色を扱うことができ、色数が多い写真や、グラデーションがかかった画像に向いている
- 目に見えない差異をカットし、情報量を圧縮するため、大きい画像でも小さいファイルサイズにできる
- 写真に向いている一方で、文字やシャープなラインを含む画像はぼやけやすく、色数の少ないロゴやアイコンを保存すると他のファイル形式よりもファイルサイズが大きくなってしまうこともある
- 非可逆圧縮の画像形式となるため、低解像度で保存すると元の画質に戻すことはできない
- 高圧縮にすると画像がジャギジャギするので、圧縮率には注意
- 透過はできない
gif
- 最大256色で表現されるので、ロゴやアイコンなど色数が少ない画像の保存に適している
- 色数が少ないためグラデーションがある場合、色の段差が発生する
- 透過できる
- 可逆圧縮の画像形式なので、低画質で保存しても元の画質に戻すことができる
- アニメーション画像を作成できる
- gifに変わる技術としてpngが開発されたため、制作の現場ではほとんど使われなくなっている
SVG
- ベクター形式の画像。拡大しても滑らかな曲線が表現できる(jpgなどはビットマップ形式と呼ばれるいわば点画のようなもの。)
- 拡大縮小しても、容量が変わらないが、複雑なイラストはpngの方が軽くなる場合もあるので、見極めが必要
- ロゴやアイコンなどシンプルなもので、サイト内の配置場所によって大きさが変わる場合などに便利
- svgタグでマークアップすることで、CSSで色を変えることもできる
- アニメーションの表現にも使える
webp
- ウェッピーと読む
- Googleが開発した画像形式でjpgやpngよりも2,3割ファイルサイズを小さくできると言われている
- 1677万7216色扱う
- 透過可能
- アニメーションも可能
- 使えるブラウザが限られているため、表示できないブラウザに対してpngやjpgを表示する対応も必要 can i use
- pictureタグを使うか、htaccessで非対応ブラウザの制御をする
2. 画像圧縮の仕組み
専門的な話になるので、ざっくりイメージだけまとめます
各拡張子ごとにいろいろ方法はあるそうですが、主な方法だけピックアップしました。
png
- フルカラー(1677万色)からインデックスカラー(256色)に変更する
- イラストやロゴなど、色数が多すぎない場合にはインデックスカラーに減色しても見た目はほとんど変わらない
jpg
- 大きく影響がない部分を削ることで圧縮する
- 画像のビットマップの一つのマスを明るさ情報と色情報に変換し、高周波成分と低周波成分に分けて、高周波成分の情報を削るらしい(何言ってるかよく分からない)
- おそらくですが、一つのマスに複数の情報があるのを、平均したり、多数派の情報に統一するようなことが行われてるんじゃないかと思ってます。
gif
pngと同様、減色で圧縮する。もともと色数が少ないので、そんなに圧縮されなさそう
Retina対応とは
Retinaディスプレイに対応することです。
Retinaディスプレイは従来の倍の解像度のディスプレイのため、画面上の物理的な大きさが300 × 200 pxの画像でも、実際は倍サイズの600 × 400 pxで表示されることになります。
デザイン上横幅300 × 200pxの画像だからといって、そのサイズのままコーディングしてしまうと、Retinaディスプレイでは画像が引き伸ばされて荒く見えてしまいます。そのため、倍サイズの画像を使います。
ただし、倍サイズというのはデバイスピクセル比が2なので倍サイズと言ってますが、デバイスピクセル比が3や4の端末も出てきてるそうで、そういった端末に対応する際には注意が必要です。
Retina対応のために必要以上のサイズの画像を指定すると、サイトが重くなるんじゃないかと懸念してしまいますよね。
そこで次に紹介する imgタグの srcset
です
HTMLでの画像の取り扱い
- srcset / size属性
- pictureタグ
- レイアウトシフト
- decoding="async"
- loading="lazy"
srcset属性
ブラウザのスクリーン要件(ウィンドウサイズ、デバイスピクセル比)に応じて、異なる画像を読み込むことができる。Retinaディスプレイの時はこのサイズの画像、そうじゃない時はこっちのサイズの画像と出し分けられます。
デバイスピクセル比の検証はChromeのデベロッパーツールで変更できます。
変更後にリロードが必要
sizes属性
画像をどのサイズで表示したいのかを指定する。srcset属性でw単位を使った時のみ記述する
ややこしいですが、横幅や高さを指定するものではないです。横幅や高さwidth, heightで指定します。下に例を書いてます。
pictureタグ
画像を振り分ける要素。条件にあった画像を表示することができる
アートディレクション(PCとSPで違う比率やデザインの画像を表示する)
書き方いろいろ
推奨の方法は
- サイズ変更のみの場合は
img
タグにsrcset
を使う - アートディレクションを入れる場合は
picture
要素を使う
// デバイスピクセル比によって画像を切り替える
// デバイスピクセル比が1の時は500.png、2の時(Retina)は1000.pngを表示する
// src属性はsrcsetが非対応のブラウザのために記述
<img
srcset="500.png 1x, 1000.png 2x"
src="1000.png"
alt="..."
/>
// ビューポート(画面の横幅)と解像度によって画像を切り替える。単位はwを使う
// 以下の例だとデバイスピクセル比が1の場合、ビューポートが600pxだと800.jpが表示される
// デバイスピクセル比が2の場合、ビューポートが600pxだと1200.jpgが表示される
// またsizes属性で画像サイズを指定してます。800pxまでは100%、それ以降は1200pxで固定。
// つまり、1200.jpgに固定される。
<img
width="800"
height="533"
srcset="./images/1200.jpg 1200w, ./images/800.jpg 800w, ./images/400.jpg 400w"
src="./images/800.jpg"
sizes="(max-width: 800px) 100vw, 1200px"
alt="..."
/>
// SPとPCで画像を出し分ける(写真のトリミングが違う時や、バナーのデザインが違う時など)
// 上から順にマッチするかを判定し、マッチすればそれ以降は無視される
// 以下の例だと画面幅が450pxまではsp.png、700pxまではtb.png、それ以降はpc.pngが表示される
<picture>
<source media="(max-width: 450px)" srcset="img/sp.png" />
<source media="(max-width: 700px)" srcset="img/tb.png" />
<img width="1600" height="800" src="img/pc.png" alt="" />
</picture>
// SPとPCで出し分けに対応した上で、デバイスピクセル比も対応する
<picture>
<source
srcset="
img/sp.jpg 1x,
img/sp-2.jpg 2x"
media="(max-width:450px)">
<img srcset="img/pc.jpg 1x, img/pc-2.jpg 2x" src="img/pc-2.jpg" alt="">
</picture>
// webp対応
// webpに対応してればwebp、対応してないブラウザはxxx.jpg
<picture>
<source srcset="img/xxx.webp" type="image/webp" />
<img src="img/xxx.jpg" alt="" />
</picture>
画像は何パターン用意したらいいか / どの指定がベストか
デバイスの種類はかなり多く、全てに最適化するのは無理なので、そこまで神経質になる必要はない。
また、サイトによってはそもそも画像が少ない場合もあるので、そういうサイトで画像の最適化を頑張っても、大きな効果は得られません。対応パターンを増やせば増やすほど表示テストの手間も増えます。
案件ごとにサイトの特性や、要望、工数との兼ね合いでどこまで対応するか考えましょう。
必要であれば、アナリティクスでユーザー環境を調べたりして、どのサイズで対応するかを考えるのもあり。
Retina対応だけ考えたいなら以下の方法でいいんじゃないでしょうか。画像は2パターン。
<img
srcset="500.png 1x, 1000.png 2x"
src="1000.png"
alt="..."
/>
ちなみに有名な技術ではどう指定されてる?
// WordPress
// メディアから投稿した画像は自動で数パターンのサイズで保存され、以下のように表示される
<img
loading="lazy"
class="..."
src=".../hoge.jpg"
alt="..."
width="1600" height="1000"
srcset=".../xxx.jpg 1600w,
.../xxx.jpg-300x200.jpg 300w,
.../xxx.jpg-1024x682.jpg 1024w,
.../xxx.jpg-768x512.jpg 768w,
.../xxx.jpg-1536x1023.jpg 1536w"
sizes="(max-width: 1600px) 100vw, 1600px"
>
// Next.js
// next/imageを使うと、自動的に画像最適化を行ってくれる
<img
alt="..."
src="xxx.jpg"
decoding="async"
style=""
srcset="xxx.jpg 1x, xxx-2.jpg 2x"
>
レイアウトシフト / CLS(Cumulative Layout Shift)
レイアウトシフトは、サイトを開いた時に画像や動画が遅れて読み込まれ、レイアウトがずれる現象のこと
CLSはレイアウトシフトによるレイアウトのずれを定量化したもの。ページがどれぐらい安定しているかを表す。
画像だけでなく、webフォントや広告なども対策が必要
レイアウトシフトが起こるとどうなるか
- 画像が遅れて表示したことで、読んでいた箇所を見失う
- コンテンツがずれて、間違えてボタンを押してしまう
画像のレイアウトシフト対策方法
imgタグにwidthとheightを設定する。CSSで width: 100%; height: auto
など実際に表示したいサイズを設定する。CSSを指定することでimgタグのwidth/heightは上書きされる
// 画像の実際の横と縦のサイズを指定する
<img width="1000" height="500" src="img/xx.jpg" alt="" />
// 比率が重要なので、比率で指定してもOK
<img width="2" height="1" src="img/xx.jpg" alt="" />
ただし、上述の picture
による画像の出し分けをする場合には注意が必要です。
縦横比が同じなら問題ないが、縦横比が異なる画像を出し分ける場合は、sourceにもwidth, heightを指定する
<div class="wrap">
<picture>
<source media="(max-width: 450px)" srcset="img/sp.png" width="200" height="200" />
<source media="(max-width: 700px)" srcset="img/tb.png" width="1200" height="600" />
<img width="1600" height="800" src="img/pc.png" alt="" />
</picture>
</div>
レイアウトシフトの計測方法
- Lighthouse
- Chrome検証ツール
loading属性
画像の読み込みを遅延するかどうかの属性
必要な画像から読み込みするので速度改善につながります
全てのimgに指定するのではなく、ファーストビュー以下の画像に指定するといい
safariは未対応
lazyを指定するとスクロールして画像に近づいた時に読み込みされます。画像が表示される領域に到達した時にはすでに表示されてるので目で確認はできませんが、chromeデベロッパーツールでNetworkタブに切り替えて、imgに絞ってみるとスクロールすると徐々に画像が読み込まれるのが分かります。
// 遅延読み込みしない
<img src="xxx.jpg" loading="eager" alt="" height="150" width="360">
// 遅延読み込みする
<img src="xxx.jpg" loading="lazy" alt="" height="150" width="360">
decoding属性
loading属性はいつ読み込むかを指定するのに対して、decoding属性はいつデコードするかを指定する。デコードはデータから画像や映像を再生すること、要するに画像を描画することだと思います。
そのデコードを同期的にするか非同期的にするかを設定できます。
- 同期処理:順番に処理をしていくこと。Aという処理をしている最中に、Bが開始するとAは中断される
- 非同期処理:平行して処理をすること。Aという処理をしてる最中に、Bが開始してもAはそのまま処理が続く
autoもしくはsyncの場合、画像の描画が始まると他の処理が中断され、若干ページの表示速度に影響が出てくるかもしれません。
asyncにすることで、描画中も他の処理が平行して行われるため、表示速度改善が見込まれます。
// 同期
<img src="xxx.jpg" decoding="sync" alt="" height="150" width="360">
// 非同期
<img src="xxx.jpg" decoding="async" alt="" height="150" width="360">
// 明示的に指定しない(デフォルト)
<img src="xxx.jpg" decoding="auto" alt="" height="150" width="360">
loading / decording どう使うか
- 併用も可能(ただし、lazyによってデコードが遅れて、画像が表示されないということがあるらしいので注意が必要です)
- ファーストビューには
decoding="async"
以降の画像はloading属性と併用というパターン - 画像サイズが大きい場合lazyを指定すると表示が遅れる場合もあるらしいので、その場合は
loading="lazy"
は指定しない
さいごに
imgタグだけ指定してればよかったですが、いろいろと指定方法が増えてたので改めて勉強になりました。
Read next
- 2021年10月17日開発環境あれこれ(gulpやwebpackなど)
- 2021年10月17日CSSプリプロセッサについて(SassやPostCSS)
- 2021年10月18日フォントサイズの単位について