ネイティブサポートされたレスポンシブ・イメージ

はじまり

物語はその昔、WURFL(Wireless Universal Resource File、モバイル端末を検出するためのデータベース) が幅を利かせており、またモバイル専用 Web サイトが流行りだったときから始まります。この時代、開発者は「本物」の Web サイトのシンプルで低レベルななバージョンを作り、UA 判別をしてモバイルユーザーに提供していました。

みなさんも知っているように、ビューポートの大きさを始め異なる特徴を持つデバイスの急激な増加によって、より良いモバイルユーザー(もしくは非モバイルユーザー)の判別が急務とされました。レスポンシブ Web デザイン(「Responsive Web Design」「レスポンシブデザイン」とも)は、新しいブラウザに搭載された機能と CSS のテクニックを組み合わせデバイスに応じた表示を提供し、どのような環境でも理想的な見た目にするという Web サイトの作成手法です。レスポンシブ Web デザインによって、開発者はビューポートの大きさという点ににおいては、信頼性の乏しいデバイス検出の仕組みに悩む必要がなくなったのです。

しかしながら、たとえ RWD サイトがデバイスごとに違うように見えたところで、ほとんどのサイトがデバイスに関係なく同じリソースをダウンロードさせているという現状があります。そしてダウンロードされる Web サイトにおいて、その多くを占めるのが画像であるため、開発者コミュニティはそのムダを避けるための解決策の検討に乗り出したのです。

複数の案が様々なメーリングリストで提案され、ブログ記事での議論や、浮かび上がった様々なユースケースが検討されるという、(とても)長いプロセスを経ることとなりました。開発者は Responsive Images Community Group を組織し、後にブラウザベンダーも関わりコンセンサスに至りました。コミュニティとブラウザベンダーが協力して策定された picture 要素の仕様は HTML 仕様に盛り込まれ、Blink と Gecko の実装が今秋早くにリリースされる予定というとこまでに来たのです!

しかし、ちょっと待ってください。これはとてもいい話ですが、なぜ私がレスポンシブ・イメージを気にかけなければならないのでしょう?そしてレスポンシブ・イメージは私に何をしてくれるのでしょう?

固定幅の画像

作っている Web サイトの画像が、ビューポートの大きさに関わらず同じ大きさで表示されている、つまりレスポンシブではないとしましょう。この画像を、高いデバイスピクセル比(Device Pixel Ratio, DPR とも)を持つハイエンドのデバイスでは綺麗に見せたい、いっぽうで「Retina 画像」を必要としないデバイスには普通の画像を提供したいとします。

画像の幅が 500px だとしましょう。これを Retina ディスプレイなどにも同じ大きさで表示させ、そのうえで品質を上げたいとします。

それを実現するのが、次のようなコードです。

<img src="cat_500px.jpg"
	srcset="cat_750px.jpg 1.5x, cat_1000px.jpg 2x"
	width="500" alt="lolcat">

シンプルですよね。説明もそんなに必要ないと思います。ここではブラウザに、リソースと画面の DPR を表す x デスクリプタの組み合わせをカンマ区切りで指定しています。ブラウザはその中からもっとも適切な画像を表示するわけです。

ここで、src 属性に 1x の画像リソースが指定されていることに注目してください。これはフォールバックとしても機能します。同じリソースを2回も必要はありません!

可変幅の画像

もしサイトがすでに「伸縮する」画像のある「クラシックな」レスポンシブ Web サイトであれば、いま紹介したものでは充分ではありません。まったく使えないというわけではありませんが、同じピクセル密度でも幅 1920px のディスプレイと幅 360px のディスプレイに同じ画像が提供されてしまうからです。ぼやけるか遅いかという、UX に関して苦しい選択を迫られるわけです。

実現したいのは、ディスプレイの DPR とビューポートの大きさをふまえブラウザが適切な画像を選ぶように、画像リソースを指定することでしょう。では、どうやるのでしょうか?

理想は、私たちが画像リソースとその物理的な大きさ(画像のピクセル幅)を複数指定したものの中から、画像が表示される大きさをもとに、ブラウザが適切な画像をダウンロードさせるというものでしょう。

しかしこれには問題があります。ブラウザがどの画像をダウンロードするかを決めるタイミングでは、画像が表示される大きさがわからないのです。画像の表示される大きさはページのレイアウトに左右されますが、レイアウトは CSS に依存することが多く、またダウンロードするリソースを決めたい画像やページ内の他の画像の大きさにも依存することがあるのです。循環依存の闇がそこにはあるのです!

というわけで、適切な画像をブラウザにダウンロードさせるには、最終的に表示される画像の大きさをヒントとして伝えなければなりません。これをどうにかする方法は残念ながらありません。もちろん、デザインによって画像の表示される大きさはレイアウトごとにさまざまになるでしょう。

これはなかなかややこしい問題でした。そして一体なにが問題なのかを把握するまでに時間がかかった理由です。最終的に、Google の Tab AtkinsJohn Mellor によって、この「伸縮する画像」のユースケースに対応する構文の案がもたらされ、レスポンシブ・イメージの仕様にも喜んで取り込まれました(仕様の編集のほとんどが Tab や Opera の Simon Pieters によって行われました)。

では、様々なレイアウトのブレークポイントにあわせて異なる画像の表示サイズを実現する例をご覧ください。

<img sizes="(max-width: 30em) 100vw,
			(max-width: 50em) 50vw,
			calc(33vw - 100px)"
	srcset="swing-200.jpg 200w,
			swing-400.jpg 400w,
			swing-800.jpg 800w,
			swing-1600.jpg 1600w"
	src="swing-400.jpg" alt="ケトルベルスイング">

初めて見た方は「うっ…」と思うかもしれませんから、細かく見ていきましょう。srcsetw デスクリプタは、上で見た x とそう変わりありませんね。srcset 属性はブラウザに選択させたい画像リソースのリストを指定する属性で、w デスクリプタは提供する画像の物理的な大きさをブラウザに伝える役割があります。

でも、さっき「ブラウザは画像の表示される大きさが計算されるのを待てない。そうすると画像が表示されるまでとても時間がかかるし、リソースを二重にダウンロードしてしまう可能性がある」というような事を言いましたよね。では、ブラウザが画像の表示サイズがわかるのを待てなければ、どうやって画像の物理的な大きさをもとにダウンロードするリソースを決定できるのでしょうか?

ここで sizes 属性の出番です。この属性は値に、メディア条件(メディアクエリーからメディアタイプを省いたもの)と CSS length のペアをカンマ区切りで指定します。メディア条件は任意です。

ブラウザはメディア条件を読んでいき、現在の状況(ほとんどの場合において、「現在の状況」== 現在のビューポートとなります)に最初にマッチするものを探します。ペアの2番目の値(メディア条件が省かれた場合は1番目ですが)が、そのペアの「有効サイズ」となり、画像の想定表示サイズを指定します。このペアはブラウザがリソースを選ぶためのヒントとして、開発者が指定しなければならないものです。

ブラウザはマッチしたペアの有効サイズと、ディスプレイの DPR、また(あれば)他の情報をもとに、どのリソースをダウンロードし表示すればいいかを決定します。

先ほどの例に戻って説明します。もしブラウザがビューポート幅 20em でルートのフォントサイズが 16px(つまりビューポート幅 320px)の場合、ブラウザは sizes に指定されたペアを読み、最初の (max-width: 30em) 100vw を選びます。これにより、画像はビューポート幅いっぱいに表示されることになりそうです。DPR が 1 の場合、ブラウザは幅 320px よりも大きな画像のうち最初にマッチするものを選ぶため、この場合は swing-400.jpg をダウンロードするでしょう。もし DPR が 2 の場合、ディスプレイの密度にマッチさせるには、リソースの幅が2倍以上大きくないといけません。そのため、ブラウザは幅 640px よりも大きな画像のうち最初にマッチしたもの、つまり swing-800.jpg をダウンロードすることになるでしょう。

もしビューポート幅が 40em (640px) の場合、(max-width: 50em) 50vw のペアがマッチし、画像の表示サイズはビューポート幅の半分となりそうです。これは、ダウンロードされる画像が、1x ディスプレイの場合は 320px 以上、2x ディスプレイの場合は 640px 以上となるだろうということを意味します。なのでダウンロードされるだろう画像はひとつ前の条件と一緒です。

さて、少し前から私が「なりそうです」「なるでしょう」といった言葉を使っていたことにお気づきでしょうか。なぜそういった言い回しをしたかというと、srcset中のリソースについては、ブラウザが好きなものを選べるとされているからです。つまり、開発者は自身の求める画像がブラウザにダウンロードされることを保証できません。これは「いいこと」です。なぜならそうすることにより、ダウンロードする画像の決定を行うプロセスがブラウザに委ねられ、ブラウザがよりスマートな最適化を行えるからです。最適化にはたとえばユーザー設定を設けることや、ネットワークの状況を加味するなどが考えられます。

さて、sizes 属性が指定さてていない場合は規定値の 100vw が有効サイズとして使われます。これは、横スクロールバーが出ない最大の画像表示サイズだからです。

では、異なるレイアウトごとに少し違う画像を見せたい、たとえば縦横比の違う画像や、対象物がより見やすい画像を出し分けたいといった場合はどうしたらよいでしょうか。

それがアート・ディレクションのユースケースになります!

アート・ディレクション

レスポンシブ・イメージにおける「アート・ディレクション」という用語は、Jason Grigsby によって初めて使われ始めたもので、これはレスポンシブ・レイアウトの特定のブレークポイント向けに調整された画像を提供したい場合に使われます。アート・ディレクションは画像リソースの品質だけではなく、縦横比、トリミング領域、コピーテキストの場所、画角など様々な場面で利用できます。可能性は無限です!

アート・ディレクションの場合、特定のデザインブレークポイントで、指定した画像を表示させたいですよね。

アート・ディレクションにおける構文はこんな感じです。

<picture>
	<source media="(min-width: 45em)" srcset="large.jpg">
	<source media="(min-width: 32em)" srcset="med.jpg">
	<img src="small.jpg" alt="章を授ける大統領">
</picture>

ここでも他の例と同じように、ブラウザに画像リソースのリストを渡しています。前の例との違いは、<source> タグのリストと、仕様で詳細に定義された画像の選択アルゴリズムです。

ブラウザは仕様のアルゴリズムに沿って、常にあなたが意図した通りの <source> タグを選びます。

アルゴリズムは sizes とほとんど同じで、リストを探し最初にマッチしたものを選びます。media 属性と type 属性によってマッチングが行われることもあります(どうして type 属性が?と思う方もいるかもしれませんが、これはあとで分かります)。

もし media 属性と type 属性どちらもにマッチした場合もしくはそもそも属性が指定されていない場合、マッチする画像リソースが選ばれます。もしどの <source> もマッチしない場合は、<img> が選ばれます。要素が選択されたら、その要素の srcset 属性と sizes 属性に指定された値から、先ほど説明した仕組みで画像リソースが選ばれます。

いくつか注意したいポイントがあります。

  • <source src> と書いてもなにも起こりません。要素はリソース選択のプロセス途中で無視されます。ちゃんと <source srcset> と書いているかチェックしましょう。
  • <picture> が親要素ではありますが、実際に作業するのは <img> です。<img> は親の <picture> と兄の <source> から読み込むリソースを決めますが、その画像を表示するのは結果的に <img> なのです。つまり、<img><picture> 内に書かれてないといけません。でなければ画像は表示されません。フォールバックという観点からも理由があります。<img> は古いブラウザに対してフォールバックを提供する役割があります。なのでどちらにせよ必要なのですが、もし <img> が書かれていない場合、なにも表示されません。
  • 最後に、画像にスタイルを当てたい場合は、いつもやっていたように <img> に対して行います。Tab Atkins の言葉を借りると、<picture><img> を囲む「魔法の span」と考えましょう。alt テキストも同じように考えましょう。代替テキストはいつもやっていたように、<img> に書きます。

sizessrcset でアート・ディレクションができない理由

sizessrcset の構文は、ビューポートの幅と画面の DPR の2つを扱います。そこにアート・ディレクションが加わると、各画像リソースに対してDPR、ビューポート幅の組み合わせすべてを明示的に指定しなければならなくなります。

これは Web 開発者に大変な手間となり、また構文もぱっと見でわからないごちゃごちゃしたものになります。これがアート・ディレクションを別の構文で行う理由です。

画像フォーマットのフォールバック

画像に関して他にしたいことは、ブラウザのサポート状況にあわせて異なるファイルフォーマットを提供することでしょうか(べつにレスポンシブに限りませんが)。ブラウザベンダーは、これまでサポートしてきた画像フォーマット(PNG、JPEG、GIF など)に加えて、新しいフォーマットを導入しようとしています。これらの新しいフォーマットにはアルゴリズムの改良などが施されており、画像をより効率的に圧縮するなど従来のフォーマットよりも優れています。具体的には、Google が WebP を、そして Microsoft が JPEG-XR を推進しています。

問題は、これらの新しいフォーマットが、従来のフォーマットのようには広くサポートされていないことです。つまり <img src> の値に新しいフォーマットの画像を使ってしまうと、サポートするブラウザではよりよいエクスペリエンスを提供できますが、サポートしないブラウザでは画像がなにも表示されません。これはよくありません。

非対応ブラウザのことも考えながら新しいフォーマットを提供する方法にはこれまで、HTTP の Accept ヘッダもしくは UA 判別によるコンテント・ネゴシエーションしかありませんでした。多くの場合これで大丈夫なのですが、いくつか問題もあります。まずサーバーサイドでの対応が必要ですが、これができない場合があります。また、これらの画像をキャッシュさせることにおいても難しさがあります。

しかし <picture> によってついに、クライアントサイドでフォールバックを指定する仕組みが導入されました。<source> 要素の type 属性を使えば、複数のリソース URL を提供しつつ、その中からブラウザのサポートする画像を表示させるようにできます。この仕組みはフォントやビデオなど、他のリソースにはすでに提供されていたものです。フォールバックの仕組みが、画像にも導入されたのです。

クライアントサイドのフォールバックを実現するコードはこんな感じです。

<picture>
	<source type="image/webp" srcset="president.webp">
	<source type="image/vnd.ms-photo" srcset="president.jpxr">
	<img src="president.jpg" alt="誰かと拳をつきあわせる大統領">
</picture>

いますぐ使える?

紹介してきた機能のネイティブサポートは、ブラウザの安定版に届くまではまだすこし時間がかかります。しかし、使うこと自体はもちろん今からできます。

srcsetx デスクリプタは Chrome 34、Opera 21、Safari 8 からサポートされています。

<picture> 要素のサポートは Chrome 38、Opera 25、Firefox 33 からになります。WebKit では sizessrcset のサポートが完了したものの、残念ながら Safari 8 には盛り込まれませんでした。また、WebKit での <picture> 完全実装までには、まだ少しやることがあります(私がするんですが)。

訳注:Firefox での <picture> はおおまかな実装は完了したものの、まだいくつかの修正が必要なことから Firefox 33 では有効にされませんでした。詳細は Mozilla の Bug 1017875 をご覧ください。

IE については、公式に「検討中(“under consideration”)」という状態です。しかし IE の開発チームは IRC の #respimg チャンネルに頻繁に顔を出し、良い質問をしてくれます。早く検討中から「開発中(“in development”)」に移ることを期待しましょう。

<picture> のサポートは W3C の validator でも行われているので、何かつまづいた際にマークアップをチェックできます。

<picture> には picturefill という標準準拠の Polyfill もあります。しかしこれがなくても、もとからある <img> へのフォールバックによって古いブラウザでもフォールバック画像がダウンロードされます。なので単に <img> を使った場合と同じエクスペリエンスとなるのです。

そのほかの提案

レスポンシブ・イメージの動向に詳しい人なら、「Client-Hints」という提案を覚えているのではないでしょうか。これは HTTP リクエストヘッダを使ってサーバにブラウザとその周辺状況を伝え、サーバに画像を選択させるというもので、いくつかのユースケースに対応できるものです。こういった解決方法は「コンテント・ネゴシエーション」と呼ばれています。

残念ながら、いくつかのブラウザベンダーがコンテント・ネゴシエーションを基とする解決法に消極的な反応を示しました。これは過去にあったコンテント・ネゴシエーションに関する問題を憂慮してのことです。これらのベンダーの賛同が得られなかったこともあり、Client-Hints の策定は止まったままです。

コミュニティ

レスポンシブ・イメージへの取り組みは、他の Web プラットフォームの策定プロセスとは異なり、コミュニティ主導で進みました。中心になったのは RICG で、開発者コミュニティによって支えられ、ブラウザベンダーの参加もあって標準化団体に取り込まれました。

Blink(Chrome と Opera のレンダリングエンジン)への実装においても、コミュニティ主導という姿勢は変わらず、重要な先例を作りました。

Blink プロジェクトから寄せられた実装観点の懸念(12)を払拭するため、私は Blink チームと共に実装計画をたて、関係する基盤の整備に取り組み始めました。

はじめ、私は自分の時間を使って作業していました。しかし実装までにどれくらい長くかかるかを知ってから、実装が完了するまで私はそれを仕事にしました。仕事の資金はコミュニティによりクラウドファンディングが実施され調達されました。このキャンペーンは RICG の人たち(特に Mat MarquisGeri Coady)の助けにより実現したものです。

Web 開発者、エージェンシー、それに Google や Opera がキャンペーンに寄付してくれ、実装の仕事をフルタイムで請け負えたことで、今日の成果があります。

まとめ

レスポンシブ・イメージは、レスポンシブな Web サイトの制作にあたって面倒な問題でした。しかしついに、ネイティブ実装という解決策を使える手前まできたのです。

<picture> は今日から使えます。Picturefill を使ってもいいですし、使わなくても問題ありません。使ってユーザーの時間とお金を減らしましょう!