こんにちは、Webフロントエンドエンジニアの権守です。
弊社では200以上の提携ECサイトから集めた大量の商品写真を取り扱っています。そのサービスの性質上、画像配信の最適化は非常に重要な課題の1つです。今回は最適化の一環として画像のレスポンシブ対応を導入しましたので、その際に調査した内容やハマったポイントなどを紹介します。
はじめに
RetinaディスプレイなどのDPR(Device Pixel Ratio)の高いディスプレイの普及に伴い、Webサービス側でもその対応が必要となっています。基本的には高解像度の画像を用意すればよいですが、高解像度になればその分ファイルサイズは大きくなり、配信にかかる時間も長くなります。そのため、適切な画像を選んで配信する必要があります。
DPRについてはこちらのページがわかりやすくまとまっています。
画像の配信制御
ベクター画像
高DPRな環境に対応するにあたり、コーディングという観点から一番簡単な方法はSVGなどのベクター画像を使うことでしょう。
<img src="logo.svg">
ベクター画像であればいくら拡大しても画像が粗くなるようなことはないので、高DPRでも問題ありません。また、1つの画像をサイズ違いで複数用意し、コーディングするという手間も必要ありません(サイズ違いの画像を用いる場合のコーディングについては後述します)。
一方で、デメリットとして画像解像度が小さい場合にラスタ画像と比べるとファイルサイズが大きくなりやすい点や、写真への適用は難しいという点が挙げられます。
写真などの複雑な画像をベクター画像に変換することは難しく、変換時に画質の劣化やファイルサイズの増大が起こるので現実的に利用することは稀だと思います。ベクター画像への変換が何故難しいかはここでは割愛します。
VASILYでは、多少ファイルサイズが大きくなっても鮮明に表示したいサービスロゴなどの画像に対してSVGを使っています。
アイコンフォント
ベクター形式の利用という観点ではFont Awesomeなどのアイコンフォントを使うという手もあります。
アイコンフォントは便利ですが、いくつか欠点もありVASILYでは採用してません。アイコンフォントの欠点についてはこちらによくまとまっているので、参照するとよいと思います。
多様な画像フォーマットのサポート
高解像度の画像を用意しつつもファイル容量を抑えるのであれば、WebPやJPEG XRなどの新しい画像フォーマットの利用も検討してもよいかもしれません。しかし、これらのフォーマットのサポート状況はブラウザによって異なります。そのため、異なるブラウザのために複数のフォーマットで画像を用意する必要があります。
このような場合にはPicture要素とSource要素を使います。Source要素のtypeにMIMEタイプを指定することで、そのブラウザでサポートされているかどうかを上から順に判定し、最初に見つかった表示可能な画像を表示します。
<picture> <source srcset="photo1.webp" type="image/webp"> <source srcset="photo1.jxr" type="image/vnd.ms-photo"> <img src="photo1.jpg"> </picture>
複数の解像度のサポート
表示する環境毎に必要な大きさの画像を選択して表示することで、転送量を抑えつつユーザ体験も保つことが出来ます。
DPR毎に出し分け
サイズが固定のレイアウトでRetinaディスプレイなどの高DPRに対応する場合には、srcsetのxデスクリプションを使うとよいです。
<img src="photo-100.jpg" srcset="photo-200.jpg 2x, photo-300.jpg 3x" width="100" height="100">
上のようにsrcsetを記述することでDPRの大きさに応じて画像を選択してくれます。srcsetをサポートしていないブラウザではsrcに指定された画像を表示するのでsrcの記述も必須です。
DPRによる画像の切り替えを検証する際には各ブラウザの開発者ツールでDPRを切り替えて確認することができます。Chromeの場合は次のように検証できます。
Retinaディスプレイの端末であってもDPR1の動作を検証できます。
表示サイズ毎に出し分け
ウィンドウ幅の80%を横幅とするように画像を表示するといったレイアウトでは、予め画像が表示されるサイズはわかりません。こういった場合にはsrcsetのwデスクリプションを使うとよいです。
<img src="photo-100.jpg" sizes="80vw" srcset="photo-200.jpg 200w, photo-400.jpg 400w, photo-600.jpg 600w, photo-800.jpg 800w">
wデスクリプションを使うことで、ブラウザ側でDPRを考慮した上でsizesから必要な画像の大きさを計算し、srcsetから必要最低限な大きさの画像を選択してくれます。仮にウィンドウサイズが320px、DPRが2だったとした場合の計算例は下のようになります。
必要な解像度 = DPR * 80vw = 2 * 320 * 0.8 = 512
この場合、必要な解像度は512pxなのでそれ以上の解像度を持つ中で一番小さいphoto-600.jpgが表示されます。
* 注意
Chromeの開発者ツールでDPRに1より大きい値を設定した場合に必要な解像度の計算が正しくありません。Androidの実機やiPhoneの実機では問題なく動作しているので、PCのChrome特有のバグかもしれません。
また、sizesはその名の通り複数指定することが可能であり、次のように指定すればレイアウトを切り替えることもできます。
<img src="photo-100.jpg" sizes="(max-width: 300px) 50vw, 33.3w" srcset="photo-200.jpg 200w, photo-400.jpg 400w, photo-600.jpg 600w, photo-800.jpg 800w">
この例ではウィンドウ幅が300px以下の場合にはウィンドウ幅の半分のサイズで画像を表示し、そうでない場合にはウィンドウ幅の3分の1の大きさで画像を表示します。このような記述をすることで、横並びのレイアウトであればウィンドウ幅に応じて2カラムの表示を3カラムの表示に切り替えられます。
サンプルはDPRに1を設定した状態で確認することをお薦めします。
srcsetの注意点
sizes属性を指定されたimgはsizesから選ばれた大きさになりますが、CSSで明示的に大きさを指定されている場合はCSSで指定された大きさが優先されます。しかし、その場合であってもwデスクリプションの計算に用いられる大きさはsizesのものです。つまり、適切に必要な解像度を求めるためには、CSSでは大きさを指定しないかsizesとCSSで指定するimgの幅は統一する必要があります。
srcsetは同じ画像を複数の解像度で用意することを想定しているため、一度大きな画像をロードをした後にウィンドウを小さくしたとしても、小さい画像を改めて読み込むことはしません。また、同じsrcsetでsizesの小さいimgが同ページ内に存在する場合にもキャッシュされた大きな画像を利用します。
言い換えると、ウィンドウ幅に応じて画像のアスペクト比を切り替えるような指定をした場合に、ウィンドウサイズを小さくしたとしても意図した画像は表示されません。こういった画像の切り替えを行う場合には次に説明するアートディレクションを行う必要があります。
サンプルはDPRに1を設定した状態で確認することをお薦めします。
アートディレクション
ウィンドウサイズや画面の向きなどの表示している環境に応じて配信する画像の内容を変えることをアートディレクションといいます。例えば、ウィンドウサイズの横幅が大きくなった場合に背景全体も写った画像に切替えることが考えられます。
これをHTMLで表現するにはPicture要素とSource要素を使う必要があります。
<picture> <source media="(min-width: 45em)" srcset="large.jpg"> <source media="(min-width: 32em)" srcset="med.jpg"> <img src="small.jpg" alt=""> </picture>
これまで説明した画像配信は組み合わせることも可能です。各組み合わせのサンプルがこちらのページにまとまっているので、合わせて参照されるとよいと思います。
遅延読み込み
画像の配信容量を抑えるテクニックの1つとして遅延読み込みを採用されている方も多いと思います。VASILYでもLazy Loadを用いて遅延読み込みを行っていました。しかし、こちらのライブラリはsrcsetに対応していません。そのため、VASILYではsrcsetの導入に伴いlazysizesを採用しました。採用に至った理由としてはsrcsetに対応していること、jQueryに非依存であることなどが挙げられます。
まとめ
今回は、Webサイトのレスポンシブ対応の中でも特に画像に着目して紹介しました。レスポンシブ対応の導入を検討している方の参考になれば幸いです。
img要素のsizesとsrcsetはHTML5.1で策定されたものです。今回紹介した技術は比較的新しいものが多く、ブラウザによってはサポートされていないものもあるので、Can I useなどでサポート状況を確認した上での動作検証や導入をお薦めします。
最後に
VASILYでは、ユーザー体験をより良くしていけるエンジニアを募集しています。興味がある方は以下のリンクから是非ご応募ください。 https://www.wantedly.com/projects/61389www.wantedly.com