ウェブサイトのパフォーマンスを計測をすると、多くのケースでは画像の最適化についての問題が見つかりますが、効果的な改善策の 1 つとして考えられるのが、状況に応じて画像を出し分けるレスポンシブイメージの実装です。
すでに 10 年以上の歴史を持つレスポンシブイメージですが、大きく以下のパターンに分類できます。この記事では、このパターンごとに、実装方法や問題点、今後期待される機能を紹介します。
- ビューポートベース
- DPR ベース
- アートディレクション
- 画像フォーマットベース
1. ビューポートベース
見出し「1. ビューポートベース」ビューポートベースでは、srcset
属性と sizes
属性を使用して、候補となる画像を指定します。特徴としては以下が挙げられます。
srcset
属性には、候補となるサイズ違いの画像を列挙する(画角やアスペクト比は同じ)sizes
属性には、画像が表示されるレイアウト幅の条件を明示する- 画像選択の決定権はブラウザ側にある
レスポンシブウェブデザインにおける画像のファイルサイズの問題
見出し「レスポンシブウェブデザインにおける画像のファイルサイズの問題」レスポンシブウェブデザイン(RWD)では、まず、<img>
要素に以下の CSS を指定することで、レイアウト幅に合わせて画像が縮小します。
img {
max-width: 100%;
height: auto;
}
論理プロパティであれば、以下のように指定します。
img {
max-inline-size: 100%;
block-size: auto;
}
しかし、<img>
要素の src
属性には、1 つのファイルしか指定できないため、想定されるレイアウト幅の最大サイズの高解像度な画像を指定することになります。
<img src="photo.jpg" alt="代替テキスト" width="2400" height="1800">
この場合、モバイルの小さなスクリーンや、高解像度ディスプレイを搭載していないデスクトップ環境では、必要以上に大きなサイズの画像を読み込むことになります。
特にモバイルでは通信速度が十分ではない場面も多いため、ダウンロードするファイルサイズが大きいとパフォーマンス上のボトルネックになり、通信量の制限にも悪影響を及ぼします。
srcset
属性と sizes
属性
この見出しのリンクこの問題を解消するために、srcset
属性と sizes
属性を使用します。
srcset
属性で候補となる画像ファイルのパスをカンマ区切りで列挙します。ここで指定する各画像は、画角やアスペクト比が同じでなければなりません。なお、src
属性に指定した画像はフォールバックとして使われます。
ファイル名の後ろに指定されている 400w
や 800w
は、ディスクリプタ(Descriptor、記述子)と呼ばれ、w
は画像の横幅(width)を意味します。
sizes
属性は、画像が表示されるレイアウト幅を明示します。単位の vw
はビューポート幅(viewport width)の割合を意味します。ここでは 50vw
と指定しているので、画像がビューポート幅の 50% で表示されることを想定しています。
例えば、ビューポート幅が 1000px
のケースで、候補画像との比率を考えてみます。50vw
なのでレイアウト幅は 500px
です。
- 400 / 500 = 0.8
- 800 / 500 = 1.6
- 1200 / 500 = 2.4
多くの場合、DPR 以上のもっとも近い比率の画像が表示されます。例えば、デバイスの DPR が 1
であれば、1.6
である photo-medium.jpg
が選択される可能性が高く、DPR が 2
であれば、2.4
である photo-large.jpg
が選択される可能性が高いです。
ここで注意する点としては、srcset
属性に指定した値はブラウザに情報を提供しているだけで、指示ではないということです。DPR やズームレベル、通信状況などがブラウザ側で考慮され、候補リストから最適な画像が選択されます。
さて、この sizes
属性ですが、なぜ本来 CSS が扱うべき、見た目に関わる情報を HTML に書かなければならないのでしょうか。
ブラウザは、HTML ドキュメントの解析を開始してからレンダリングが完了する前に画像を読み込み始めますが、その段階では、画像がどのようなレイアウトで配置され、どれぐらいのサイズで収まるかがわかりません。sizes
属性で先にレイアウト情報を伝えて、ブラウザに適切な画像を選択してもらう必要があるのです。
メディアクエリの指定
見出し「メディアクエリの指定」sizes
属性には、以下のようにメディアクエリを使用して複数の条件を列挙できます。
この sizes
属性の指定は以下を意味しています。
- ビューポート幅が
1200px
以上であれば、画像の幅を33.33vw
で表示する(3 カラム) - ビューポート幅が
768px
以上であれば、画像の幅を50vw
で表示する(2 カラム) - いずれも一致しなければ、画像の幅を
100vw
で表示する(1 カラム)
上記の例では、レイアウト上の余白は考慮されていませんが、sizes
属性には calc()
も使用できるので、余白分を差し引いたサイズ指定も可能です。
非常に複雑なコードになりました 😵💫
実装コストとのトレードオフ
見出し「実装コストとのトレードオフ」ここまでで見てきたように、sizes
属性に指定するコードは複雑で理解しづらく、画像の選択は各ブラウザに委ねられるので、指定した値が正しいかどうかの検証が難しいのも事実です。
また、レイアウトが変更になると、場合によっては sizes
属性の値の変更や画像の書き出しが必要になり、メンテナンスコストも高くなります。
これらの点を踏まえ、ビューポートベースのレスポンシブイメージの実装については、パフォーマンス最適化と実装コストの、どちらに重点を置くかによって判断したほうがよいでしょう。
sizes=auto
見出し「sizes=auto」将来的には、loading="lazy"
で画像の遅延読み込みが指定されている場合には、sizes="auto"
を指定することで、ブラウザ側で自動的にレイアウト幅を計算してくれるように計画されています。
<img
src="photo-small.jpg"
srcset="
photo-small.jpg 400w,
photo-medium.jpg 800w,
photo-large.jpg 1200w
"
sizes="auto"
alt="代替テキスト"
width="400"
height="400"
>
すでに仕様には盛り込まれており1、Google Chrome では積極的に開発が進んでいますが2、安心して使えるようになるまではもう少し時間を要するでしょう。
なお、sizes="auto"
が使用できるのは、あくまでも loading="lazy"
が指定された画像のみなので、LCP に相当するヒーローイメージ(メインビジュアル)などには使えません。
コンテナクエリ
見出し「コンテナクエリ」<img>
要素の sizes
属性ではメディアクエリを指定できますが、コンテナクエリには対応していません。後述するアートディレクションで触れる <source>
要素でも同様です。
もし、実現できたとしても、基準となるコンテナ要素(親要素)とコンテナクエリを指定している子要素の両方に sizes
属性を指定する必要があり、非常に複雑でメンテナンス性が悪くなります。
この問題は、画像の読み込みをできるだけ早く開始するか、適切なサイズの画像のダウンロードを優先するかという点で競合するため、簡単に答えは出ないようです3。
なお、sizes="auto"
が安心して使えるようになれば解消されますが、依然としてファーストビュー(Above the fold)に配置される画像には使用できません。
代替案としては、<img>
要素ではなく CSS の background-image
を使用すれば、コンテナクエリに対応できますが、<img>
要素としての役割を失うため4、装飾的な画像に限定されます。加えて、ブラウザ側に画像の選択を任せる srcset
属性とは、意味合いが異なる点にも注意する必要があります。
2. DPR ベース
見出し「2. DPR ベース」DPR ベースでは、srcset
属性を使用して、候補となる画像を指定します。
前述のビューポートベースと比較するとシンプルですが、画像を出し分ける条件がデバイスのピクセル比(DPR)のみのため、さまざまなサイズに対応することはできません。
ビューポートベース同様に、srcset
属性で候補となる画像ファイルのパスをカンマ区切りで列挙します。ディスクリプタ x
は、デバイスピクセル比(DPR)を表します。
x
ディスクリプタと、w
ディスクリプタを混在させることはできません。
繰り返しになりますが、srcset
属性に指定した値はブラウザへのヒントに過ぎないので、2x
を指定したからといって、必ずしも DPR が 2
のディスプレイで表示されるとは限りません。
背景画像(background-image)
見出し「背景画像(background-image)」CSS の背景画像(background-image
)で同様のことを実現するには、image-set()
関数を使用します。
.bg {
background-image: url('photo.jpg');
background-image: image-set(
url('[email protected]') 1x,
url('[email protected]') 2x
);
}
なお、最初の background-image: url()
はフォールバック用の指定です。
3. アートディレクション
見出し「3. アートディレクション」レスポンシブイメージにおけるアートディレクションとは、<picture>
要素を使用して、メディアクエリに応じて画像を切り替える手法です。
例えば、ヒーローイメージ(メインビジュアル)の画角やアスペクト比が、モバイル向けとデスクトップ向けで異なる場合、以下のように <source>
要素と <img>
要素を <picture>
要素で囲うことで、ブレイクポイントを境界に画像を出し分けることができます。
注意点として、指定できる alt
属性は 1 つなので、内容の異なる画像を指定すべきではありません。
このとき、<source>
要素内の media
属性が参照され、条件に一致する場合には srceset
の属性の画像が表示され、一致しない場合には <img>
要素に指定した src
属性の画像が表示されます。
また、<source>
要素にも width
属性と height
属性を指定することができるので、画像のアスペクト比が異なる場合には、レイアウトシフトを防止するために指定します。
なお、前述した「1. ビューポートベース」や「2. DPR ベース」と大きく異なる点は、<source>
要素は指示的であるという点です。
前者の場合は、あくまで候補となる画像のリストを伝えるだけなので、画像選択の決定権はブラウザ側にあります。対して <source>
要素では、ブラウザは指定された内容に従うので、決定権は実装者にあります。
ダークモード対応
見出し「ダークモード対応」<source>
要素にはメディアクエリが使用できるので、prefers-color-scheme
を使用することで、ライトモードとダークモードで画像を出し分けることが可能です。
アニメーション対応
見出し「アニメーション対応」同様に、prefers-reduced-motion
を使用することで、アニメーションを好まないユーザ向けに静止画を表示することができます。
ただ、情報を伝えるためにアニメーションが必須で、静止画では表現できない場合には、何かしらの代替コンテンツを用意する必要があります。
4. 画像フォーマットベース
見出し「4. 画像フォーマットベース」前述のアートディレクションでは、<source>
属性にメデイアクエリを指定しましたが、type
属性を指定して、複数のフォーマットの画像を指定できます。
この例では、<source>
要素に type
属性を指定して、この条件に一致する画像を表示しています。指定した順番に、まず AVIF 形式に対応しているか、一致しなければ WebP 形式に対応しているかが評価されます。
そして、いずれの条件にも一致しない場合には、<img>
要素に指定した JPEG 形式の画像が表示されます。
背景画像(background-image)
見出し「背景画像(background-image)」DPR ベースと同じように、CSS の背景画像(background-image
)で同様のことを実現するには、image-set()
関数を使用します。
.bg {
background-image: url('photo.jpg');
background-image: image-set(
url('photo.avif') type('image/avif'),
url('photo.webp') type('image/webp')
);
}
おわりに
見出し「おわりに」この記事では、レスポンシブイメージの 4 つのパターンを説明しました。
特に srcset
属性と sizes
属性を組み合わせたレスポンシブイメージはわかりづらく、画像を生成するコストまで考えると、採用できるプロジェクトは限定されるかもしれません。
将来的には sizes="auto"
が使えるようになれば負担は軽減されますが、コンテナクエリを使用した場合にはどうするのかといった別の問題も生まれています。
このように、導入にあたって障壁の多いレスポンシブイメージですが、ウェブパフォーマンスにおいて効果的なメソッドの 1 つであることは間違いないでしょう。
ウェブサイト全体におけるモバイルのトラフィックの比率が、引き続き増加傾向にあることや5、ウェブサステナビリティへの対応が強く求められる時代において、パフォーマンス最適化は無視できない課題です。
まずは「必要以上に画像を使わない」「SVG 形式を積極的に使用する」といった戦略からスタートし、「LCP に影響しやすい要素から優先的にレスポンシブイメージを実装する」といったように、できるところから取り入れていくのがよいかもしれません。
参考文献
見出し「参考文献」本記事の作成にあたり、以下のウェブページを参考にしました。
- Learn Images | web.dev(外部リンクを開く)
- レスポンシブ画像 | MDN(外部リンクを開く)
- 4.8.4 Images | HTML Standard(外部リンクを開く)
- A Guide to the Responsive Images Syntax in HTML | CSS-Tricks(外部リンクを開く)
- srcsetとsizes(外部リンクを開く)
- Should responsive images work with container queries? | Stefan Judis(外部リンクを開く)
- On Container Queries, Responsive Images, and JPEG-XL | Cloud Four(外部リンクを開く)
- Component-level art direction with CSS Container Queries | Sara Soueidan(外部リンクを開く)
脚注
-
Auto Sizes for Lazy Loaded Images with Srcset | Chrome Platform Status ↩
-
[css-conditional] [css-contain]
srcset
andsizes
interaction with container queries | Issue #5889 | w3c/csswg-drafts ↩ -
role="img"
とaria-label
属性を使えば、画像として認識させることはできますが、ハック色が強すぎるため避けた方がよいでしょう。 ↩