レスポンシブイメージでユーザーの閲覧環境に最適な画像を表示しよう

このハンズオンではa-blog cms で実装するときに、srcset属性やpicture要素を使ってレスポンシブイメージでユーザーの閲覧環境に最適な画像を表示する方法をご紹介します。

レスポンシブイメージとは?

まずはレスポンシブイメージについておさらいしましょう。

普段、Retinaディスプレイへの画像表示の対応はどうしていますか?スマートフォンでも大きなPCのRetinaディスプレイ用の画像を読み込んでいたりしないでしょうか。 レスポンシブイメージを使えば、画像の幅、高さ、ピクセル密度をブラウザが判断して、最適なサイズで読み込ませることができます。

レスポンシブイメージを使う際には大きく2つの方法があります。

  • 画像解像度の変更(resolution switching)
  • アートディレクション(art direction)

画像解像度の変更(resolution switching)

レスポンシブイメージを使用したい用途が「画像解像度の変更」だけなら、img要素とsrcset属性を組み合わせた方法がおすすめです。たとえば、全く同じ構図の写真を高解像度のディスプレイと通常のディスプレイどちらにも表示したい場合に有効です。


同じ縦横比だけれど、画像の大きさを変更できる

画像解像度の変更のイメージ図

HTMLの記入方法

<img srcset="画像のパス 記述子,
	     画像2のパス 記述子,
	    画像3のパス 記述子"
     src="フォールバック画像のパス"
     sizes="メディアクエリー(任意) 表示サイズ,
	    画像2のメディアクエリー(任意) 画像2の表示サイズ,
	    画像3のメディアクエリー(任意) 画像3の表示サイズ"
     alt="代替テキストを入力してください">

属性 説明
srcset 画像パスと記述子の間には半角スペースを、次の画像を指定するときには「,」を入力します
src 記入した画像がsrcset属性に対応していないブラウザで使用されます
sizes 記述子を「w」に設定したときのみ使用可能。表示サイズを指定します(※「%」は単位に使用できません)。任意でメディアクエリーを使用できます。

srcset属性に使用できる記述子には2種類あります。


記述子 記述方法 説明
幅デスクリプタ w 画像の固有サイズをブラウザに伝える。
ピクセル密度デスクリプタ x 高解像度のディスプレイをサポートする。(記述例:1x、1.5x、2x...など)

記述子がなかった場合は、1xが使用されます。

HTMLの記入例

<img srcset="img.png 400w,
	     img@2x.png 900w,
	     img@3x.png 1200w"
     src="img_fallback.png"
     sizes="(max-width: 479px) 100vw, 50vw"
     alt="代替テキストを入力してください">
<img srcset="img.png 1x,
	     img@2x.png 2x,
	     img@3x.png 3x"
     src="img_fallback.png"
     alt="代替テキストを入力してください">

アートディレクション(art direction)

レスポンシブイメージを使用したい用途が、「画角を変更したい」なら、picture要素+srcset属性を組み合わせた方法がおすすめです。たとえば、小さなスクリーンでは写真が全体的に小さくなって人物の顔がよく見えなかったり、文字が潰れてしまう場合に画角を変更して表示することが可能になります。


画角を変更する。たとえば、メイン部分にフォーカスするなど

アートディレクションのイメージ図

HTMLの記入方法


要素 説明
picture とくに特別な属性などは記述しない。source要素の親要素。
source video要素などにも使われている要素。srcset属性が使用可能。picture要素の子要素。
img srcset属性が使えないブラウザでフォールバック画像として使用される。picture要素の子要素。
<picture>
	<source media="メディアクエリー"
		srcset="画像1のパス 表示サイズ,
			画像2のパス 表示サイズ,
			画像3のパス 表示サイズ">
	<source media="メディアクエリー"
		srcset="画像4のパス">
	<img src="フォールバック画像のパス" alt="代替テキストを入力してください">
</picture>

img要素以降にsource要素を書いた場合、以降のsource要素は無視されてしまいます。img要素はpicture要素内で一番最後に記述します。

HTMLの記入例

<picture>
	<source media="(max-width: 479px)"
		srcset="img.png 400w,
			img@2x.png 900w,
			img@3x.png 1200w">
	<source media="(min-width: 480px)"
		srcset="img@3x.png">
	<img src="img.png" alt="代替テキストを入力してください">
</picture>

picture要素では、以下のようにtype属性を指定しSVGやWEBPなどの画像に対応している環境で読み込むことも可能です。

<picture>
	<source type="image/svg+xml" srcset="img.svg">
	<source type="image/webp" srcset="img.webp">
	<img src="img.png" alt="代替テキストを入力してください">
</picture>

ブラウザサポート

picture要素とsrcset属性はIE11では使用できませんが、そのほかのモダンブラウザでは使用できるようになっています(参照:Can I use... )。


スクリーンショット:Can I use ... によると、IE11ではsrcset属性は使用できない

Caniuse : Srcset and sizes attributes


スクリーンショット:Can I use ... によると、IE11ではpicture要素は使用できない

Caniuse : Picture element

ユニットでレスポンシブイメージを使ってみよう

それぞれどんな種類のレスポンシブイメージがあるかわかっていいただけたのではないでしょうか。そこで、よく運用しているサイトでありがちな問題を解決するために、レスポンシブイメージをユニットで実装してみましょう。

「画像解像度の変更(resolution switching)」で3カラムの画像が高解像度のスマホから表示したときに画像がボケている問題を解決する

以下の画像は画像ユニット、「画像解像度の変更」を実装するためにimg要素にsrcset属性を使った例です。テーマは公式テーマのsite2018を使用しています。どちらのユニットも1/3カラム幅を使用しています。デスクトップから表示した際は1/3幅になっていますが、スクリーンサイズが479px以下になったときは1カラムに変更します。そのときに、高解像度のRetinaディスプレイで閲覧すると画像ユニットの画像のほうがすこし文字がボケて読みにくくなっているのがわかります。


デスクトップから閲覧したとき


高解像度のRetinaディスプレイで閲覧したとき


しかし、srcset属性を使ったユニットでは画像解像度の変更が行われ、ユーザーの閲覧環境に最適な画像をブラウザが判断して提供しているためRetinaディスプレイでラスタ画像を表示しても綺麗に表示されています。

以前までは同じことをするために複雑なCSSやJavaScriptを使って解決していた方も多いかもしれませんが、srcset属性を使えばHTMLの変更だけで実装できるようになります。

ファイルを作成する

実装するためには画像の拡張ユニットを使用します。お使いのテーマの中に/include/unit/extend.htmlを作成します。

画像ユニットのソースコードをコピーする

作成したら、/themes/system/_layouts/unit.htmlより、画像ユニットのソースコードをコピーしましょう。Ver.2.9.7の時点では以下のようなソースコードです。

<!-- BEGIN unit#image -->
@section(image-unit)
<!-- 画像 -->
<div class="column-image-{align}{display_size_class}"{display_size}[raw]><!-- BEGIN link#front --><!-- BEGIN_IF [{url}/nem/] -->
	<a href="{url}"{viewer}[raw] data-caption="{caption}[nl2br|delnl]"><!-- END_IF --><!-- END link#front -->
		<img class="columnImage" src="%{HTTP_ROOT}{path}" alt="{alt}[delnl]" width="{x}" height="{y}"><!-- BEGIN link#rear -->
	</a><!-- END link#rear --><!-- BEGIN_IF [{caption}[delnl]/nem] -->
	<p class="caption">{caption}[nl2br]</p><!-- END_IF -->
</div>
@endsection
<!-- END unit#image -->

@section(image-unit)@endsectionは今回のカスタマイズには不要なので、削除します。

ブロック名を変更する

つぎは、ブロック名を変更します。<!-- BEGIN unit#image --> <!-- BEGIN unit#image_srcset -->へ、<!-- END unit#image --><!-- END unit#image_srcset -->へ変更します。

img要素にsrcset属性を追加する

img要素にsrcset属性を追加しましょう。src属性はそのままsrcset属性を対応していないブラウザへのフォールバックの記述となりますので、残しておいてください。以下のように書きます。

<img class="columnImage"
     srcset="〇〇(画像のパス) △△(画像のサイズをブラウザに伝える記述子),
	     〇〇(画像のパス・違うサイズのもの)"
     src="〇〇(フォールバック画像のパス)"
     alt="〇〇(代替テキスト)">
<img class="columnImage"
     srcset="%{HTTP_ROOT}{path}[resizeImg('878','')] 878w,
	     %{HTTP_ROOT}{path}"
     src="%{HTTP_ROOT}{path}"
     alt="{alt}[delnl]">

ここでは画像サイズを仮に878pxと設定しましたが、多くの画像サイズを記述すればそれだけ多くの閲覧環境に最適な画像をブラウザが選別してくれます。

479px以下から1カラムになり、479px幅のときはメインカラムが439pxです。その2倍の解像度を考えて、ここでは一例として画像サイズを878としました。

本来であれば最適な画像をブラウザが選んでくれるためこのような計算は不要ですが、a-blog cmsではresizeImg校正オプションを使えば好きなサイズの画像を読み込めます。

拡張ユニットの設定をする

これで、下準備ができたので、拡張ユニットを設定していきましょう。

管理ページに移動し、コンフィグ>編集設定の順にページを移動(⌘+K/Ctrl+Kで「編集設定」を検索でも移動可)してください。

編集設定のページでユニット追加ボタンの項目を1つ追加します。セレクトメニュー横の入力欄に先ほど作成した拡張ユニットのブロック名「image_srcset」を記入し、ラベル名にはわかりやすいように「srcset属性」と記入して保存してください。



次に、コンフィグ>ユニット設定の順にページを移動(⌘+K/Ctrl+Kで「ユニット設定」を検索でも移動可)します。ページの一番下まで移動すると、ユニットの設定が空になっている「srcset属性」という項目があるので、「srcset属性」をクリックしてユニットを追加してください。


空になっている「srcset属性」という項目


「srcset属性」をクリックしてユニットを追加した様子


確認する

エントリー作成画面に移動すると、srcset属性というユニット追加ボタンが追加されています。1つエントリーを作成して、画像ユニットと比べてみてください。比べてみるときは、どちらのユニットも画像の幅を1/3カラム幅にして登録してください。

サンプル画像はなんでもいいですが、文字が入っている画像のほうが画像がボケているか確認しやすいと思います。こちらで画像を用意したので、よければサンプル画像としてお使いください。


srcset属性用サンプル画像


デスクトップ幅のとき


Retinaディスプレイ、スクリーン幅が479px以下のとき


「アートディレクション(art direction)」でスクリーンサイズが小さい端末で文字が小さくて見えなくなってしまう問題を解決する

「アートディレクション(art direction)」は、ユーザーの環境によって異なった画角の画像を出し分けることができます。たとえば、スマートフォンからみたときに画像内の文字が潰れてしまって読みにくくなってしまったり、人物の顔が潰れてしまったことはありませんか?

そんな問題をpicture要素を使ってアートディレクションを行い、解決することができます。以下がpicture要素をユニットで使ったときのデモの様子です。


画像ユニットから挿入したバナーは文字が小さくて見えないが、picture要素で画角を変更した画像の文字は大きく表示され可読性が高くなる

スマートフォンからVer.2.9のバナーを見たとき


さらに、picture要素で表示している画像はサイズをリサイズしているため、画像ユニットで挿入した画像が51.3KBなのに対して、picture要素を使用したユニットの画像は12.6Kとなっており軽くなっています。Webサイトのパフォーマンスの表示を解決することにも役立ちます。


スクリーンショット:Chromeのインスペクターから調べた様子

Chromeのインスペクターから調べたところ、この例ではpicture要素を使用した画像のほうが38.7 KB軽くなっている

それでは実装していきましょう。srcset属性のユニットと同じく、拡張ユニットを作成します。/include/unit/extend.htmlを開いてください。

srcset属性で作成したソースコードをコピーしてブロック名を変更する

先ほどsrcset属性で作成したソースコードをコピーして、その直下にペーストしてください。

コピーしたソースコードのブロック名が現在image_srcsetになっているので、image_pictureへ変更します。

<!-- BEGIN unit#image_picture -->
...
<!-- END unit#image_picture -->

picture要素とsource要素を追加する

  • img要素のsrcset属性を削除
  • img要素をpicture要素で囲む
  • picture要素の中のimg要素の直前にsource要素を記述する
<source class="columnImage"
	media="(max-width: 479px)"
	srcset="%{HTTP_ROOT}{path}[resizeImg('480','320')] 480w,
		%{HTTP_ROOT}{path}[resizeImg('600','400')] 600w,
		 %{HTTP_ROOT}{path}">
<source class="columnImage"
	media="(min-width: 480px)"
	srcset=" %{HTTP_ROOT}{path}">

この記述では、スクリーンサイズが479px以下だったときに画角が変更された画像が読み込まれ、resizeImgを使っているため画像の中心が縦横比3:2でおさまる画像であれば適切に表示されます。480px以上のスクリーンサイズでは挿入された画像が元のサイズで表示されます。

最終的なソースコードは以下のようになります。

<!-- BEGIN unit#image_picture -->
<div class="column-image-{align}{display_size_class}"{display_size}[raw]><!-- BEGIN link#front --><!-- BEGIN_IF [{url}/nem/] -->
	<a href="{url}"{viewer}[raw] data-caption="{caption}[nl2br|delnl]"><!-- END_IF --><!-- END link#front -->
		<picture>
			<source class="columnImage"
				media="(max-width: 479px)"
				srcset="
				%{HTTP_ROOT}{path}[resizeImg('480','320')] 480w,
				%{HTTP_ROOT}{path}[resizeImg('600','400')] 600w,
				 %{HTTP_ROOT}{path}">
			<source class="columnImage"
				media="(min-width: 480px)"
				srcset=" %{HTTP_ROOT}{path}">

			<img class="columnImage" src="%{HTTP_ROOT}{path}" alt="{alt}[delnl]">
		</picture>
		<!-- BEGIN link#rear -->
	</a><!-- END link#rear --><!-- BEGIN_IF [{caption}[delnl]/nem] -->
	<p class="caption">{caption}[nl2br]</p><!-- END_IF -->
</div>
<!-- END unit#image_picture -->

拡張ユニットの設定をする

これで、下準備ができたので、srcset属性を追加したみたいに拡張ユニットを設定していきましょう。

管理ページに移動し、コンフィグ>編集設定の順にページを移動(⌘+K/Ctrl+Kで「編集設定」を検索でも移動可)してください。

編集設定のページでユニット追加ボタンの項目を1つ追加します。セレクトメニュー横の入力欄に先ほど作成した拡張ユニットのブロック名「image_picture」を記入し、ラベル名にはわかりやすいように「picture要素」と記入して保存してください。



次に、コンフィグ>ユニット設定の順にページを移動(⌘+K/Ctrl+Kで「ユニット設定」を検索でも移動可)します。ページの一番下まで移動すると、ユニットの設定が空になっている「picture要素」という項目があるので、「picture要素」ボタンをクリックしてユニットを追加してください。


空になっている「picture要素」という項目


「picture要素」をクリックしてユニットを追加した様子


確認する

エントリー作成画面に移動すると、picture要素というユニット追加ボタンが追加されています。1つエントリーを作成して、画像ユニットとpicture要素を挿入して2つのユニットを比べてみてください。

文字が入っている画像のほうが縦横比3:2の比率にトリミングされており、479px以下のスクリーンサイズからみたときでも文字が読みやすくなっているはずです。

サンプル画像はなんでもいいですが、文字が入っていて、文字が入っているエリアが3:2の縦横比に収まるものを推奨しています。こちらでも画像を用意して置いたので、よければサンプル画像としてお使いください。


picture要素用サンプル画像


今回は画像の拡張ユニットとして用意しましたが、3:2の縦横比で固定になってしまいます。もし縦横比を自由に選びたい場合はカスタムユニットで作成していただくとよりよいものが作成できるかと思います。

完成すると、以下のようにスクリーン幅が479px以下の場合は3:2に縦横比がトリミングされた画像が表示されるはずです。


スクリーン幅が479px以下のとき


デスクトップ幅のとき


以上でハンズオンは終了となります。画像解像度の変更とアートディレクションをうまく使って、ユーザーの閲覧環境によって画像の表示を分けることで、よりきれいな画像をよりよいパフォーマンスで提供していきましょう!

参考にした資料

余談:img要素+srcset属性でもアートディレクションはできる?という疑問について

実は、srcset属性のみでアートディレクションをすると危険があります。この方法ではブラウザがユーザーの閲覧環境に合わせて読み込むべき最適な画像を決めています。このとき、ブラウザごとに最適な画像と判断する基準が違うため予期せぬ表示を引き起こす可能性があります。

以下、HTML 5.1のsrcset・sizes属性とpicture要素の使い方 - レスポンシブイメージで画像表示を最適化 - ICS MEDIAの記事より引用します。

注意しなければならないのは、「環境に応じた最適な画像決定」の仕組みはブラウザによって異なることです。例えば次のような挙動があります。

Chrome : 大きなサイズの画像ファイルをキャッシュした場合、画面幅を狭めても小さい画像は読み込まれない
Firefox : 画面幅を変える度に、画面幅に適したサイズの画像を読み込む
Safari : 最初に開いた画面幅に応じた画像ファイルがキャッシュされ、画面幅を変えても画像は再読み込みされない

この手法では、単にサイズの異なる画像を場合は特に問題ありませんが、例えばスマートフォン向けにはトリミングをした異なる画像を出し分けたいといった場合に対応ができません。

同じタグ付けがされている記事