目次

レスポンシブ画像 - srcset/sizes入門編

異なる解像度のデバイスへの対応問題を解決する為のimg要素のsrcset/sizesを活用する方法を学びましょう。IEもサポートが終了します。いよいよ本格実装です。


PREPARATION


想定する読者と前提条件

パソコンの基本操作ができること。HTML、CSSの基礎がわかっていると良い。


START


ビューポートとピクセル密度

HTML入門記事でも触れているようにウェブページを表示できる領域であるビューポート、ピクセル密度についておさらいしていきましょう。

ビューポートとは?

ブラウザでWebページを表示できる領域のことをビューポートと呼んでいます。もちろん、パソコンの表示領域は可変であり、みなさんが自由自在に変更することができます。

そして、ブラウザの表示領域とは別にWebサイト自身にも幅と高さがあります。もちろん、ブラウザは、表示領域に収まらない部分を非表示にしたりせず、スクロールバーを使ってWebページ全体を閲覧できるようしています。

では、実際に体験していただくために、ビューポート、ドキュメントサイズ等を確認できるページを準備いたしました。

パソコンで閲覧する場合は、ブラウザのウィンドウサイズを変更しながら値を確認してみましょう。

ビューポートサイズ確認

そもそも画像ってなに?

コンピューター上で、写真やイラスト、図形などの総称を画像と呼んでいます。

一般的に画像は、ラスタ形式とベクタ形式の2種類に分類されます。

画素とは、画像の色情報を入れるための入れ物のことで、その最小単位を指します。

この画素が集まって、1つの画像を作り出す形式をラスタ形式と呼んでいます。

これに対し、ベクタ形式は画素の代わりに図形を描くための情報が文字として書かれています。たとえば、「半径50pxの円を赤で書く」のように指定することができます。ベクタ形式は拡大縮小しても劣化しないのが特徴で、最近利用率が高まっています。

画素(ピクセル)とは?

ピクセル(pixel)とは、画像を表現するときの色情報を表す最小の単位(単位pxで表現します)のことです。パソコン上で画像を拡大していくと四角い色の集合体であることが分かります。

この画像を表現するための四角1つ1つのことをピクセルまたは画素と呼んでいます。

モニタ解像度とピクセルの密度

モニタの仕様には、モニタ解像度とピクセルの密度を表すピクセル(画素)密度ppi(pixels per inch)があります。モニタ解像度がモニタ全体のピクセルの数を表すのに対して、画素密度は1インチ(25.4mm)の当たりのピクセル数を表すものです。

当然ながら、数字が大きいほど1ピクセルのサイズが小さくなり、より高精細なモニタということが言えます。一般的なパソコンモニタでは、100ppi程度ですが、iPhone等の高精細なディスプレイでは300ppi以上です。

この高精細の小さなモニタに従来のWebページを表示することを考えてみましょう。1ピクセルのサイズが通常のモニタよりも小さいため、文字サイズは半分以下になり、画像も半分以下で表示されることになってしまいます。

それを解決する為、Retina Display等の高精細なディスプレイを採用している端末では、物理的なデバイス解像度(モニタ解像度)とは別に、Webページを閲覧しやすいように仮想の表示領域(CSSピクセル解像度・ビューポート)を作り出します。

このような仮想表示領域の計算は、devicePixelRatio(デバイスピクセル比)から求めることができます。

実際の物理解像度÷devicePixelRatio(デバイスピクセル比) = CSSピクセル解像度となります。 iPhone 11の場合、 物理解像度(828×1792)÷devicePixelRatio(2) = CSSピクセル解像度(414×896)となります。

様々な環境での画像表示

ここでは、デバイスピクセル比が2,3等の高精細なモニタで画像を表示する場合の違いを見ていきましょう。

2-1表示の違いを確認

では、まずは低解像度の画像と高解像度の画像を表示させて比較してみましょう。この例では、3種類の画像(幅300px、600px、900px)の画像を 幅300pxの固定幅で画面上に表示させてみます。

当然、300pxで表示しますので、幅300pxの画像が最適なサイズとなります。

しかし、デバイスピクセル比が2以上のディスプレイで表示する場合には、適切な画像サイズも異なってきます。

2-2何が問題なのか?

画像の品質を維持しつつ、適切な解像度の画像を選択することが重要です。例えば、デバイスピクセル比2のデバイスで、300pxの幅で画像を表示する場合、

実際には幅600pxの画像が最適な画像幅(表示幅300px×デバイスピクセル比2)になります。仮に幅300pxの画像を表示した場合、ブラウザはその画像を2倍に拡大して表示しようとします。

これは、明らかに画像の品質を維持できません。逆に、大きすぎる画像の場合(上記の場合、幅900px)、今度は縮小して表示することになります。

本来は、600pxが適切な画像(25KB)にも関わらず、900pxの画像(41KB)を読み込むことによって、下記画像の例では約16KB無駄に消費することになります。

画像サイズとファイルサイズの比較例
ファイル名 大きさ サイズ
g404_300_188.webp W300_H188 11KB
g404_400_250.webp W400_H250 16KB
g404_600_375.webp W600_H375 25KB
g404_800_500.webp W800_H500 35KB
g404_900_563.webp W900_H563 41KB
g404_1000_625.webp W1000_H625 46KB

2-3どうやって画像を選べばよいか?

一般的に画像を表示するにためには、img要素を使用しますが、当初実装されたimg要素には、モニタのサイズやデバイスピクセル比に合わせて画像を選択するという機能は有りませんでした。

<img src="./g404_300_188.webp" alt="404 not foundを持つ女性">

Webページがすべて固定幅でパソコンのような端末しか意識せずコーディングできていた時代には何も問題がなかったのです。

しかし、iPhoneのような高精細なモニタが登場したことにより、ディスプレイの解像度 = CSSピクセル解像度とは限らなくなり、適切な画像サイズを割り出す必要がでてきてしまいました。

その割り出す計算を行うためには、次の3つの値が必要となります。

画像が描画される幅

CSSピクセル解像度で表示される画像の表示領域のサイズ(幅)。

デバイスピクセル比

物理解像度とCSSピクセル解像度の比率。

表示させる画像の幅

画面上に表示させたい画像のサイズ(幅)。

Webサイトがパソコンで見られることが主流だった時代には、固定幅ですべてのレイアウトが完結し、画像はレイアウトにあわせて決まったサイズで表示すれば何も問題ありませんでした。

しかし、最近ではスマートフォンやiPhoneでWebサイト見られることが主流になり、様々な端末で表示が最適になるように画像のサイズを調整しなければなりません。

そのため、表示される画像のサイズも見られる端末によって異なります。

例えばパソコンのような端末では画面の30%のサイズで表示し、タブレット端末では画面の45%、スマートフォンのような端末では画面の100%の幅で表示するようなケースです。

その為、先程示した3つの必要な要素の内の1つ[画像が描画される幅]は、表示端末のサイズによって変化してしまうのです。 つまり、表示端末のWebサイトの表示領域のサイズ = ビューポートの幅も表示する画像を決定するための要素として必要になります。

もちろん、画像をビューポートの幅いっぱいに表示するだけの場合には、この情報だけで事足りますが、例えば画面の半分の幅で表示したいと考えた場合、更にもう1つ情報が必要になります。

それはビューポートの幅に対する画像を表示する割合です。整理すると以下のようになります。

画像が描画される幅

CSSピクセル解像度で表示される画像の表示領域のサイズ(幅)。
ビューポートの幅×ビューポートの幅に対する画像を表示する割合で算出されるCSSピクセル解像度で表示される画像の表示領域のサイズ(幅)。

デバイスピクセル比

物理解像度とCSSピクセル解像度の比率。

表示させる画像の幅

画面上に表示させたい画像のサイズ(幅)。

これらの値をもとに、適切な画像サイズを選択する必要があります。画像の表示領域が、固定な場合と可変な場合、2つのパターンでどのような値が必要か見てみましょう。

2-4そこで、srcsetとsizes属性の登場

この問題を解決するため、img要素にsrcset属性sizes属性が追加されました。

srcset属性

表示させる画像の幅デバイスピクセル比に応じて使用可能な画像をブラウザに示す機能を提供します。

sizes属性

ビューポートの幅によって、表示する画像の幅(ビューポートの幅に対する画像を表示する割合)幅をブラウザに示す機能を提供します。

これらの属性が追加になったことで、表示する画像を決定するために必要な情報すべてを補うことができるようになったのです。

2-5srcset属性について

まずは、srcset属性について詳しく見ていきましょう。

この属性は、ブラウザに対して使用することができる画像をカンマ区切りで示すことができます。記述方法は以下のとおりです。

例:srcset="画像のURL [幅記述子|画素密度記述子],画像のURL [幅記述子|画素密度記述子],画像のURL [幅記述子|画素密度記述子]..." 

幅記述子は、画像の幅をピクセル数で指定します。この時使用する単位は、pxではなくwを使用します。

この記述式は、ブラウザに対して表示させる画像の幅を伝える役割を担っています。

以下の例では、幅が320px、480px、640pxの3種類の幅の画像を指定しています。

例:srcset="girl_320.jpg 320w, girl_480.jpg 480w, girl_640.jpg 640w" 

画素密度記述子は、デバイスピクセル比を指定します。この時使用する単位は、xを使用します。(小数点数も可)

この記述式は、ブラウザに対してデバイスピクセル比に対応した適切な画像を伝える役割を担っています。

以下の例では、デバイスピクセル比が2倍、3倍の場合の画像指定をsrcsetで行っています。ブラウザは、src属性に指定されている画像を等倍(1x)の画像として使用します。

例:src="girl_320.jpg" srcset="girl_640.jpg 2x, girl_960.jpg 3x" 

幅記述子と画素密度記述子を混在させたり、同じ値の記述子を2つ記述すると設定が無効になりますので注意してください。 幅記述子を指定した場合、src属性は無視され、画素密度記述子を指定した場合には、src属性は1xの画像として扱われます。
後方互換性の為、幅記述子を指定した場合でもsrcを書くことをおすすめします。

2-6sizes属性について

次に、sizes属性について詳しく見ていきましょう。この属性は、ブラウザに対してメディア条件に応じて画像を表示させるサイズ(幅)をカンマ区切りで設定することができます。 また、srcset属性に幅記述子を利用している場合、sizes属性は必須の属性になります。

ビューポートのサイズに対しての画像を表示するサイズ(幅)のリストを記述し、ブラウザに伝える役割を担います。それぞれの画像の寸法、ビューポートの条件と長さの組をカンマ区切りで指定します。

これらの情報は、 ページのレイアウトを行う前(画像を読み込む前)にブラウザーが使用します。

例:sizes="[メディアクエリー] 画像サイズ,[メディアクエリー] 画像サイズ...." 

以下の例では、ビューポートの幅いっぱいに画像を出力する場合の記載例です。 この記述では、画像は、ビューポート幅全体を使用して表示することになります。

例:sizes="100vw"

vwとは、表示域(ビューポート)に相対的な単位です。百分率単位で、100vwは100%、1vwは1%を表します。
ビューポートの幅が320pxの場合、100vwと記載すると320pxとして解釈され、50vwだと半分の160pxとして解釈されます。

次に、以下の例ではビューポートの幅が480px以下の時に100vwで表示、800px以下の時に50vw、それ以外の時にcalc関数を用いて算出された幅での値を用いて画像を選択することになります。 (最後のメディアクエリが設定されていない値は、初期値として扱われます。)

例:sizes="(max-width: 480px) 100vw, (max-width: 800px) 45vw, calc(33vw - 50px)"

(max-width: 480px)は、表示域(ビューポート)の幅によって条件(max-width: 480px)がテストされます。
min-widthは、以上(">=") と同じ意味です。 例えば (min-width: 480px) は、 (width >= 480px) と同じ意味になります。
max-widthは、以下("<=") と同じ意味です。 例えば (max-width: 480px) は、 (width <= 480px) と同じ意味になります。
widthは、あらゆる比較(=、<、>、>=,<=)が行え、より柔軟な対応が可能です。(対応待ちですね。)

calc(33vw - 50px)のcalcはCSSの関数で、この場合画像の描画する幅をビューポートに応じて計算を行うことができます。 ビューポートの幅が1000pxの場合、33vw = 330pxと解釈され、そこから50px減算するので、画像の描画幅は280pxとなります。

サイズは、px、emやビューポートの幅に対して相対的な大きさvw単位を使用することができます。(ただし、パーセンテージは使用できません。)

srcset、sizes属性の利用方法

ここでは、画像の表示方法の違いによるsrcset、sizes属性の利用方法を見ていきましょう。

3-1画像を表示する幅が固定な場合

まずは、画像を表示する幅が固定な場合に必要となる情報を整理してみよう。

つまりこの場合デバイスピクセル比によって選択されるべき画像をブラウザに伝える必要があります。

ビューポートのサイズも画像のサイズも不要です。固定だからです。Web製作者が、画像の表示領域も決定し、そのサイズに従って画像を制作するからです。

ブラウザの仕事は、Web製作者が決めたサイズの場所にWeb製作者がデバイスピクセル比によって指定した画像を、閲覧端末のデバイスピクセル比を元に選択するだけです。

つまり、srcsetの画素密度記述子を用いた記述だけすれば、ブラウザに必要な情報を伝えることができます。

もちろん、画像を表示する幅も固定ですから、widthも、heightも必須です。

<img
    src="./g404_300_188.webp" 
    srcset="./g404_600_375.webp 2x, 
            ./g404_900_563.webp 3x"
    width="300"
    height="188"
    alt="404 not foundを持つ女性">

画素密度記述子を用いた記述は、画像を表示させる幅が固定の場合に利用してください。
表示幅が固定のため、widthおよびheight属性は必須です。
srcset="g404_300_188.webp"等、画素密度記述子を記述せずに記載した場合1xの画像として扱われます。
また、画素密度記述子を指定した場合には、src属性は1xの画像として扱われます。

固定幅の場合の表示テスト

3-2画像を表示する幅がビューポートの幅と同じ場合

まずは、画像を表示する幅がビューポートの幅と等しい場合に場合に必要となる情報を整理してみよう。

つまりこの場合、ビューポートの幅によって選択されるべき画像を選ぶための情報をブラウザに伝える必要があります。

閲覧端末によってビューポートのサイズが異なるため、適切な画像を選択する作業を完全にブラウザに委ねる必要があります。

そのため、ブラウザが計算に必要となるすべての情報を伝えなければなりません。

Web製作者は、sizes属性でビューポートの幅いっぱいに表示(100vw)することを伝え、srcset属性で画像の幅を伝えます。デバイスピクセル比は、閲覧端末によって異なりブラウザが予め知っている情報になります。

注意事項としては、画像を表示する幅は固定ではないので、widthも、heightも不要です。

<img
    src="./g404_300_188.webp" 
    srcset="./g404_300_188.webp 300w
            ./g404_600_375.webp 600w, 
            ./g404_900_563.webp 900w"
    sizes="100vw"
    alt="404 not foundを持つ女性">

例えば、300pxの端末でこの画像を表示する場合を考えてみましょう。デバイスピクセル比1の場合には、当然300wの画像が適切で、2の場合には600wの画像が適切です。

ブラウザがデバイスピクセル比に応じた適切な画像を選択するためには、ビューポートの幅で画像の幅を割ります。

つまり上記記述300pxで各画像の幅を割ることで、
"./g404_300_188.webp 1x,./g404_600_375.webp 2x,./g404_900_563.webp 3x"
と記述したことと等価になます。

ここまでくれば、後は画像の幅を固定で表示する場合と同じように、ブラウザはデバイスピクセル比を元に画像を選択するだけです。

後方互換性の為、幅記述子を指定した場合もsrcを書くことをおすすめします。

ビューポート幅の場合の表示テスト

画像の高さと幅を指定しない場合、画像の描画領域は画像が読み込まれるまで確定しません。そのため、画像の読み込みが完了するまで、 画像の描画領域が確保されないレイアウトシフトが発生します。この辺は対策が流動的で今後どのように推移していくか見守る必要があります。 現状の対策としては、CSS側で画像の幅と高さを設定することです。ただし、高さはビューポートによって異なるため、画像のアスペクト比を元に算出できるようにaspect-ratioを設定する必要があります。


img{
        width:100%;
        height:auto;       /* autoとし、aspect-ratioで算出されるようにする */
        aspect-ratio: 4/3; /* width / height で指定された比率 */
    }

レイアウトシフト対策テスト

3-3画像の表示される幅がビューポートの幅によって変化する場合

ここまでくれば後は簡単ですね。先程の例に、画像を表示する割合を加味すればいいだけです。

Web製作者は、sizes属性でビューポート条件に応じて画像を表示する幅をブラウザに伝え、srcset属性で画像の幅を伝えます。

<img
    src="./g404_300_188.webp" 
    srcset="./g404_300_188.webp 300w,
            ./g404_600_375.webp 600w, 
            ./g404_900_563.webp 900w"
            sizes="(max-width: 480px) 100vw,
                   (max-width: 800px) 45vw,
                   calc(33vw - 50px)"
    alt="404 not foundを持つ女性">

例えば、600pxの端末でこの画像を表示する場合を考えてみましょう。600pxの場合には45vwで表示を行うため、270pxが画像の表示幅になります。

ブラウザがデバイスピクセル比に応じた適切な画像を選択するためには、ビューポートの幅で画像の幅を割ります。

つまり上記記述270pxで各画像の幅を割ることで、
"./g404_300_188.webp 1.11,./g404_600_375.webp 2.22,./g404_900_563.webp 3.33"
と記述したことと等価になます。

ここまでくれば、後は画像の幅を固定で表示する場合と同じように、ブラウザはデバイスピクセル比を元に画像を選択するだけです。

後方互換性の為、幅記述子を指定した場合もsrcを書くことをおすすめします。

画像の幅が可変な表示テスト

レイアウトシフト対策としては先程とほぼ一緒ですね。


img{
        height:auto;       /* autoとし、aspect-ratioで算出されるようにする */
        aspect-ratio: 4/3; /* width / height で指定された比率 */
    }


COMPLETE


  • avatar
    お疲れ様でした!

    レスポンシブ画像の基礎としてimg要素のsrcset/sizesを学びました。

    IEのサポートも終了するということもありますので、積極的に取り入れていきましょう。 はじめは難しいと感じるかもしれませんが、少し手を動かせばすぐになれます。ぜひ、マスターしましょう。 アートディレクション問題は次の記事で。


SKILL


習得スキルボード

  • レスポンシブ画像の基礎
  • img要素のsrcset属性・sizes属性の基礎

コメント