目次
1. デバイスピクセル比
スマートフォンのディスプレイや Apple 製品で採用されている Retinaディスプレイは、
- ディスプレイの物理的なピクセル数(物理ピクセル)
と
- 表示に使用する論理的なピクセル数(CSSピクセル)
が同じではありません。
例えば、物理的な 3ピクセル を 表示上は 1ピクセル として扱うことによって、より滑らかに見せることができます。
そして、(物理ピクセル / CSSピクセル)を計算した値が「デバイスピクセル比」(1つのCSSピクセルに対して、物理ピクセルをいくつ割り当てるか)で、これはデバイス毎に決まっています。
2. img 要素の srcset 属性 と sizes 属性
HTML の img
要素 には、srcset
属性と sizes
属性が用意されており、「1つの img
要素に対して、複数の画像ファイルの中から デバイスのデバイスピクセル比に最適なものを 1つだけダウンロードして表示する」という動作をさせることができます。
例えば、こんな感じで記述します。
<img src="/images/foo-600.png"
alt="" width="600"
sizes="600px"
srcset="
/images/foo-300.png 300w,
/images/foo-600.png 600w,
/images/foo-1200.png 1200w"
>
ざっと説明すると、以下となります。
sizes
属性に、表示したいサイズを指定する。srcset
属性に、候補となる画像ファイルのURLを指定する(複数可)
上記コードでは 3つの画像ファイルURLが指定されていますが、ブラウザが以下の処理を自動的にやってくれます。
- 一番最適な画像ファイルを選ぶ
- ダウンロードする
- 表示する
それぞれの属性についてもう少し詳しく説明します。
3. sizes
属性
- その画像をどのサイズで表示したいのか指定します。目的はこれだけです。
- 次で説明しますが、
srcset
属性で、w 単位を使った時のみ記述します。 - メディアクエリーの条件毎に、複数の値を指定することもできます(
max-width
やmin-width
を使って場合分けする)。- 通常はレイアウトが変化するブレークポイント(となるビューポートの幅)で条件を分けることが多いでしょう。
- ブラウザは 1つ目の条件から見ていって最初に合致したものを採用します。
- 例えば、
sizes="(max-width: 767px) 70vw, (max-width: 991px) 350px, 500px"
の場合、ビューポート幅が767px以下なら70vw
、991px以下なら350px
、それ以外なら500px
となります。 - メディアクエリーで場合分けする場合は、
width
属性を指定してはいけません。width
属性を指定すると、画像サイズが指定した値に固定されてしまい、場合分けされないからです。
- 指定するサイズの単位は、
px
,em
,vw
などいろいろ使えます。但し、%
は使えないです。
参照
4. srcset
属性
- Webブラウザに対して、実際に使える画像の候補を伝えます(サイズ違いの画像、正確にはピクセル密度記述子が異なる画像)。
- 「画像ファイルへのURL」と「そのファイルのサイズ(幅)」のペアを複数指定することができます。
- 「ファイルのサイズ(幅)」は、「x 単位」と「w 単位」のどちらかで指定します。
- Webブラウザは、
sizes
属性によって決定されたサイズで画像を表示するために、srcset
属性で指定された画像の中から最適なものを選んでダウンロード・表示してくれます。 - 基本的には、用意しているサイズ違いの画像を羅列するだけです。あとは勝手に Webブラウザが選んでくれます。
x 単位で画像ファイルのサイズを指定する場合
- 画像の「ピクセル密度記述子 (
1x
や2x
など)」により指定します。 - 例えば、実際は 1000px ある画像を 500px として表示したい場合は、1000 / 500 で、
2x
と記述することになります。 - ブラウザは、「1x」に該当するサイズで画像を表示しようとします。そのため、
sizes
属性でサイズを指定する必要がありません(その代り、メディアクエリーによる場合分けはできません)。 width
属性を指定する場合は、「1x」に該当するサイズをセットすることになります。
w 単位で画像ファイルのサイズを指定する場合
- 画像の「実際のピクセルでのサイズ(単位は
w
で指定します)」により指定します。 - 例えば、実際は 1000px ある画像を 500px として表示したい場合、元のサイズそのままの
1000w
と記述します。 w
単位で指定する場合は、sizes
属性も必要になります。sizes
属性によって決定されたサイズとw
単位の値の2つからピクセル密度記述子としての値に変換されます。(ピクセル密度記述子 =w
の値 /sizes
で決定されたサイズ)- Webブラウザがデバイスのデバイスピクセル比と比較して、最適な画像を選んでくれます。
参考
5. width
属性 と height
属性
2020年現在、「width
属性・ height
属性の値と、CSS での例えば width: 100%; height: auto;
といった記述により、そのときのウィンドウサイズに合った画像表示サイズが算出され、レンダリングが開始される時点で画面上にそのスペースを確保することによりレイアウト・シフトを防ぐ」というブラウザの動作を利用することが望ましいとされているようです。
つまり、width
属性、height
属性、CSSでの width
と height
を指定することがベストプラクティスであると言えます。
width
属性と height
属性を記述すると、sizes
属性で決定する幅が表示には使われなくなる(但しどの画像ファイルがダウンロードされるかはこれで決まります)という問題があるのですが、CSS で max-width を記述すれば、画像を表示する最大幅を指定できるため、sizes
属性で場合分けをする必要性はかなり低くなります。
以下は記述例です。
HTML
<img src="foo-500x377.png"
alt="" loading="lazy"
width="500" height="377"
srcset="foo-398x300.png 398w,
foo-500x377.png 500w,
foo.png 759w"
sizes="100vw">
sizes
属性の値は、CSS でwidth
プロパティに指定する値に合わせます(sizes
属性で % 単位は使えません)。srcset
属性で x 単位を使うのであれば、sizes
属性は必要ありません。loading="lazy"
を書いておくと、画面上の見える範囲に画像が現れる時点まで、画像ファイルの読み込みが行われません。指定することをお勧めします。
CSS
img {
max-width: 500px;
width: 100%;
height: auto;
}
こう書いておけば、ウィンドウサイズが大きい場合でも画像は500pxでしか表示されず、ウィンドウサイズが小さい場合は画面一杯のサイズ(実際は画像の親要素の幅に対して最大限のサイズ)で表示してくれます。
ブラウザの新仕様
2020年になったあたりの Firefox や Chrome のバージョンから、width,height を指定すると「画像が表示される前と後でレイアウトがずれる問題」が起きにくくなっていますので、なるべく指定しておいた方がよいです。
参考
6. デモページ
srcset
属性と sizes
属性を使ったデモページをいくつか作ってありますので参考にしてください。
- ? srcset の実験
- ? srcset と sizes 属性の実験(1)
- ? srcset と sizes 属性の実験(2)
- ? srcset と sizes 属性の実験(3)
- ? srcset と sizes 属性の実験(4)
7. おわりに
srcset
属性 と sizes
属性を使えば、デバイスに最適な画像を読み込ませることができるので、デバイスにとって必要以上となるデータをダウンロードしなくて済みます。
更に追加でやっておきたいこととしては、ページにとってクリティカル(すぐに表示したい)な画像に対しては、rel="preload"
というのを使って画像を先読みさせるという手法です。
逆に、画面の下の方にある画像であれば必要になった時に画像をダウンロードすればよいので、画像の遅延読み込みを導入するとよいでしょう。
参考にしようと思いましたが、下記の説明で分からなくなりました。
「上の例の場合、(ビューポート幅が767px以下なら 70vw、それ以外でビューポート幅が 991px以下なら 350px、それ以外なら 500px となります。)」
上の例とは??? 上に300 600 1200 という値はありますが
70 350 500 はありません。
全体も目を通してみましたが理解不能でした。私には基礎知識が足りないようです。
ご指摘ありがとうございます!
確かに意味不明でしたね。
修正致しました。