画像の出し分けは何を使って実現するべき?レスポンシブイメージのベストプラクティス 2019年最新版

2019年11月6日

responsive-image

最近のWeb制作で特に重視されているのが、表示スピード。表示スピードをあげるために画像サイズの最適化は必須対応項目です。

画像最適化するための2019年現在のベストプラクティスを調べてみました。

解像度にはsrcset、アートディレクションにはpicture要素を使う

下記のサイトを参考にさせていただきました。

レスポンシブ画像(MDN)

なんでもかんでも要素を使えばいいわけじゃない!レスポンシブ・イメージ実装の際の注意点

面倒なレスポンシブイメージの画像作成を自動化してくれる神ツールとブレイクポイントの考え方

解像度の変更のみの場合にはsrcsetを使う

srcsetを使用したコードの例は下記になります。

<img srcset="
 img/320w.png 320w, 
 img/480w.png 480w, 
 img/800w.png 800w"
 sizes="(max-width: 320px) 280px,
        (max-width: 480px) 440px,
         800px"
src="img/800w.png" alt="幅に合わせた寸法での表示の例">

アートディレクションには<picture>要素を使う

スマホ用の画面にはトリミングを変更した縦横比の違う画像を指定、それ以外ではブラウザの幅に合わせて解像度のみを変更した画像を指定したい場合。

<picture>
  <source media="(min-width:768px)"
     srcset="img/1280w.png 1280w , img/960w.png 960w , img/768w.png 768w" alt="picture">
  <source media="(max-width:767px)" 
     srcset="img/767w.png 767w , img/640w.png 640w , img/320w.png 320w" alt="picture">
  <img src="img/960w.png" alt="画像の説明です。">
</picture>

 上記のコード意味は以下になります。

  • ブラウザ幅が768px以上の場合はPC用にトリミングした画像、767px以下のときはスマホ用にトリミングした画像を使用する
  • 画像幅が1280px付近のときは1280w.png、960px付近のときは960w.png、768px付近のときは768w.pngを使用する(これは見た目の大きさではなくて画像の本質的なpx数。例えばレティーナの場合は解像度が2倍なので、見た目の大きさが640px幅のときに1280pxの画像を選ぶ)
  • ブラウザ幅767px以下のとき、767px付近では767w.png、640px付近では640w.png、320px付近では320w.pngを使用する
  • <img src=””>で指定した画像は、メディア条件のどれも true を返さない場合や<picture>要素をサポートしないブラウザーの代替に適用される。(必須要素)

pictureとsrcsetの違い

レスポンシブ・イメージの2つの記述方法の違いは、srcsetではブラウザに対して状況に応じた選択の余地を与えているのに対し、pictureではブラウザに判断の余地がないところにあります。

https://parashuto.com/rriver/responsive-web/picture-srcset-use-case#difference

上のサンプルコードの場合、srcsetで400w、800w、1200wの幅で使ってほしい画像をブラウザに対して提案しているにすぎず、仕様上は最終的にはブラウザの判断でユーザの閲覧環境に応じて画像を表示する仕組みになっています。たとえば、スクリーン幅が320pxでもピクセル比が2の場合は、ブラウザが自動的に800wに指定した画像を選ぶといったこともブラウザの実装次第では可能になります。将来的に「3G回線では高解像度の画像をダウンロードしない」といった機能がブラウザに追加された場合でも、srcsetを使った記述方法であれば対応できることになります。

https://parashuto.com/rriver/responsive-web/picture-srcset-use-case#difference

srcsetの指定で済むところは、優先してsrcsetを使いましょう!

ブラウザの対応状況は?

https://caniuse.com/#search=srcset

srcsetは、IEとOpera Miniを除いて最新バージョン(Edge,Firefox,Chrome,Safari,Opera,iOS Safariでは最新2バージョン)で対応しています。(2019年11月現在)

https://caniuse.com/#search=picture

pictureタグも、IEとOpera Miniを除いて最新バージョン(Edge,Firefox,Chrome,Safari,Opera,iOS Safariでは最新2バージョン)で対応しています。(2019年11月現在)

1世代前のAndroid Browser、IE11をターゲットに入れる場合はPicturefillを使う

1世代前のAndroid Browser、IE 11も対応ブラウザに入れる場合は下記のPollyfillを使用します。

Picturefill

2019年11月現在の安定版はVERSION 3.0.2です。

公式サイトに注意点があります。JS無効環境で画像が二重に読まれてしまうのを防ぐためにsrc属性にはリンクを記述せずに、blank.gif画像をdata URI化したリンクを貼りましょうとのこと。(src属性が無いと、バリデーションチェックでエラーになってしまいます)

JS-Disabled Browsers only see alt text: When using the PICTURE element, non-PICTURE supporting browsers will only see alt attribute text as a fallback when JavaScript fails or is disabled. This is because any noscript-based workarounds (such as the one used in Picturefill version 1) will cause future browsers that support the picture element to show two images instead of one when JavaScript is off. Unfortunately, adding a src attribute with an external source to the img element in your picture element isn’t a good workaround either, as any browser that exists today will fetch that src url even if it is not going to be used (which is wasteful), and an empty src can result in unexpected requests. For valid markup, the shortest possible value for src (without firing an onerror event or a potential request) is
src=”data:image/gif;base64,R0lGODlhAQABAAAAADs=”

JSが無効なブラウザーではaltテキストのみが表示されます。picture要素を使用する場合、非pictureサポートブラウザーでは、JavaScriptが失敗または無効になった場合に代替としてalt属性テキストのみが表示されます。 これは、noscriptベースの回避策(Picturefillバージョン1で使用されているものなど)により、JavaScriptがオフの場合にpicture要素をサポートする将来のブラウザーで1つではなく2つの画像が表示されるためです。 残念ながら、外部ソースを含むsrc属性をpicture要素のimg要素に追加することは、現在存在するブラウザーは使用されなくてもそのsrc urlを取得するため、適切な回避策ではありません(それは無駄です)、srcが空の場合はまた、予期しないリクエストが発生する可能性があります。 有効なマークアップの場合、srcの最短の値(onerrorイベントまたは潜在的なリクエストを発生させない)は下記のコードです。
src = “data:image / gif; base64、R0lGODlhAQABAAAAADs =”

ただ、わたしの環境では画像が二重に読まれてしまう現象はありませんでした。下記の2017年10月の記事ではIE11やSafari 7(iOS7)で画像が重複して読み込まれたとありますが、このときからさらに仕様が変更されているかと思います。

レスポンシブイメージのポリフィル「Picturefill.js」でブレイクポイントによって画像を切り替える

これは判断になりますが、以下の3つのうちどれかかと思います。

(1)picturefillを使用しないで、src属性を記述する(対応ブラウザが増えてきたため、未対応バージョンを使っている人にはsrc属性に記述されている画像を表示するのみの対応にする)
(2)picturefillを使用してsrc属性にはdata URI化したblank.gifを埋め込む(未対応ブラウザでもレスポンシブ画像が適用され、未対応ブラウザかつJS無し環境ではAlt文字のみが表示される。picturefill公式発表のバグは起きない)
(3)picturefillを使用して、かつsrc属性も記述する(未対応ブラウザでもレスポンシブ画像が適用され、未対応ブラウザかつJS無し環境ではsrc属性の画像が表示される。ただし、picturefill公式発表のバグの懸念あり)

また、下記2点を踏まえると(1)を選択する場合はsrc属性に入れるのはIE11向けの画像がいいのではないかなと思います。

  • IE 11のデスクトップでの日本でのシェア(201909-101910)は11.49%(statcounter)なのでまだ切り捨てられない
  • Android browserの日本でのシェア(201909-101910)は0.63%(statcounter)その中でさらに下位バージョンを使っている人はそれよりさらに少ない

なぜJavaScriptやCSSよりもHTMLでの出し分けがいいのか?

CSSのdisplay:noneなどを使用する方法だとすべての画像を予め読み込むことになってしまいますし、JavaScriptでファイル名を書き換えるなどの処理をする場合でもJavaScriptを読み込んで解釈する前に画像のダウンロードが開始されてしまいます。

srcsetやpicture要素を使用すれば現在開いているページに必要な画像のみがダウンロードされるため、表示速度が大幅に改善されます。

ブラウザーがページの読み込みを開始すると、メインのパーサーがページの CSS と JavaScript を読み込んで解釈する前に、画像のダウンロード(先読み)を開始します。 これは便利なテクニックで、平均してページ読み込み時間の 20% を削減します。 しかし、レスポンシブ画像では役に立ちません。 そのため、srcset のような解決法を実装する必要があります。 たとえば、  要素は、文書に画像を埋め込みます。これは置換要素です。” style=”margin: 0px; padding: 0px; border: 0px; color: #3d7e9a; text-decoration: none; font-family: Arial, x-locale-body, sans-serif; font-size: 16px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: -0.04448px; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: #ffffff;”><img> 要素を読み込んでから JavaScript でビューポートの幅を検出し、必要に応じて元の画像をより小さなものに動的に変更することはできません。 それまでには、元の画像が既に読み込まれていて、さらに小さい画像も読み込むことになります。 これは、レスポンシブ画像の条件ではさらに悪化します。

MDN

ただし、背景画像についてはCSSのメディアクエリを使用しましょう。