サイトを制作する際の画像周りの最適化は複雑で、ベストプラクティスがわからなかったり、疎かにしている人は多いのではないでしょうか?
- レスポンシブイメージ
- decoding="async"
- loading='lazy'
- レイアウトシフト
- webp等の次世代画像フォーマット
画像周りでは知っておかないといけないことが多くあります。
この記事では、その中でも特に重要な、srcset属性やpictureタグを使ったレスポンシブイメージについて解説します。
💡 コーディングの練習なら模写修行!
この記事の目次
画像の最適化が大切な理由
現代のwebサイトはファイルサイズの多くを画像が占めるケースも少なくありません。
Googleは1ページあたり1.6MB程度のファイルサイズを推奨していますが、画像を上手く扱わないと簡単にオーバーしてしまいます。
- 適切な画像フォーマットの使用
- 画像の圧縮
- レスポンシブイメージの使用
画像を多く使っているサイトであれば特に、これらをしっかりやるだけで、サイト全体のファイルサイズを大幅に減らすことが可能な場合もあります。
サイトのパフォーマンス(≒ 表示速度)は、ユーザービリティーやSEOにも関わる、重要な問題なので、出来る範囲で対応すべきです。
よくやりがちなNG例
- とりあえず大きめの画像を使っておく
- display: none;で画像を切り替える
これらはレスポンシブイメージ関連で、よくやりがちな代表的なNG例です。なぜ良くないのか、理由を紹介します。
とりあえず大きめの画像を使っておく
<div class="hoge">
<img width="2000" height="1000" src="img/hoge.jpg" alt="..." class="hoge-img" />
</div>
.hoge{
width: 90%;
margin: 0 auto;
max-width: 1000px;
}
.hoge-img{
width: 100%;
height: auto;
vertical-align: bottom;
}
上の例では、高解像度ディスプレイに対応するために、とりあえず表示される最大サイズの2倍の画像を使っています。この場合、特定の環境下では無駄に大きな画像を読み込むことになってしまいます。
今回のケースでは、スクリーンサイズ360pxのスマホであれば、360px × 90% = 324pxが実際の表示サイズになります。3 倍まで用意しても、972pxの画像を用意すれば十分なところ、PC基準の2000pxの画像を読み込むことになってしまいます。
display: none;で画像を切り替える
<div class="hoge">
<img width="2000" height="1000" src="img/hoge-pc.jpg" alt="..." class="u-pc hoge-img"/>
<img width="1000" height="800" src="img/hoge-sp.jpg" alt="..." class="u-sp hoge-img"/>
</div>
.u-pc { display: none; }
.u-sp { display: block; }
@media only screen and (min-width: 768px) {
.u-pc { display: block; }
.u-sp { display: none; }
}
SP時とPC時で"見た目の違う"画像を使いたい場合、上の例のようにdisplay: none;で切り替える方法もNGです。この書き方は、SPで見た時もPCで見た時もどちらの画像も読み込んでしまいます。
display で出し分けるをしているデモです。DevTools の Network を開いてリロードしてみると、画像が 2 つとも読み込まれているのがわかります。
サイト内で1,2箇所くらいであれば、問題ないかもしれませんが、SP時とPC時で"見た目の違う"画像を多用するサイトでは、パフォーマンスに影響が出ることもあります。
レスポンシブイメージとは?
前のセクションで、NG例を紹介しました。NG例の問題点を解決するのがレスポンシブイメージです。
レスポンシブイメージとは、レスポンシブwebデザインでの画像の扱いのことです。
- 適切なサイズの画像を出し分ける
- 異なる見た目の画像を出し分ける
これらのことをHTMLだけで実装出来ます。次のセクションからこの2点に関して、詳しく解説します。
srcset属性を使った画像の出しわけ - 適切なサイズの画像を出し分ける
昨今のデバイスは高解像度ディスプレイも多く、表示サイズと同じサイズの画像しか用意していないと、閲覧環境によっては画像が粗く見えます。
解像度 / Retinaディスプレイ(高解像度ディスプレイ) / デバイスピクセルとCSSピクセル / あたりの解説はわかりやすい記事を載せておきます。
どんな閲覧環境でもある程度、綺麗に見えて、パフォーマンスに影響がないようにする必要があります。そのために、srcset属性を使い、適切なサイズの画像を出し分けをします。
今閲覧している環境のデバイスピクセル比はこちらにアクセスするとわかります。
- デバイスの解像度のみで判断して出し分けするケース
- ビューポートサイズと解像度で判断して出し分けするケース
srcset属性を使った画像の出しわけは、この2パターンがあります。それぞれ解説します。
デバイスの解像度のみで判断して出し分けするケース
<img
width="200"
height="100"
srcset="img/200.png 1x, img/400.png 2x"
src="img/400.png"
alt="..."
/>
画像を可変ではなく固定幅で表示させたい時は、デバイスの解像度で判断して適切な画像を出し分けます。
上の例は2倍まで対応した例です。srcset属性で2つの画像を指定しています。
ユーザーの環境 | 画像 |
---|---|
デバイスピクセル比が1 | 200.pngを表示 |
デバイスピクセル比が2 | 400.pngを表示 |
ユーザーの環境に応じて、この表の出し分けをしていることになります。この時、必要な画像しか読み込まれないので、パフォーマンスに悪影響はありません。
デバイスピクセル比1~4まで対応したデモを作りました。
デバイスピクセル比 | 表示される画像 |
---|---|
1 | 横幅 200pxの画像 |
2 | 横幅 400pxの画像 |
3 | 横幅 600pxの画像 |
4 | 横幅 800pxの画像 |
上の表のように表示されるはずです。是非デバイスを変更して確認してみてください。
💡 その他注意点
- src属性はsrcset属性が非対応のブラウザ向けの記述
- widthとheightはレイアウトシフト防止のために必要
srcset属性はIEでは使えないので、src属性も必要です。レイアウトシフに関しては、下の記事をご覧ください。
CLS(Cumulative Layout Shift)とは?対策・改善方法も紹介!ビューポートサイズと解像度で判断して出し分けするケース
<div class="hoge">
<img
width="400"
height="200"
srcset="img/400.png 400w, img/800.png 800w, img/1200.png 1200w"
sizes="(max-width: 1200px) 100vw, 1200px"
src="img/1200.png"
alt="..."
class="hoge-img"
/>
</div>
.hoge{
max-width: 1200px;
}
.hoge-img{
width: 100%;
height: auto;
vertical-align: bottom;
}
- ビューポートサイズに対して100%で表示
- 最大幅は1200px
例えば、このようなケースで使います。1200pxに達するまでは、常にビューポートサイズに対して100%の幅で表示しているので、画像自体の表示サイズは閲覧環境に依存します。
この方法も、解像度によって出し分けるケースと同じく、必要な画像しか読み込まれません。
srcset="img/400.png 400w, img/800.png 800w, img/1200.png 1200w"
srcset 属性の部分だけ切り取りました。このように書くと、ブラウザが勝手に閲覧環境のビューポートサイズと解像度に応じて最適な画像を表示してくれます。
この図だけ見ると、img/400.png 1x
のような指定で良いように思いますが、これは図の状態がたまたま横幅400pxなだけです。可変なので閲覧環境によっては768pxになったり、1366pxになったりする想定です。
ポイントは図のようにビューポート(横幅)サイズだけでなく、解像度も考慮してブラウザが表示する画像を決めてくれる点です。
srcset属性の書き方は、img/400.png 400w
のように【画像ファイル/スペース/画像の幅のピクセル数】の順で書きます。画像の幅の単位はpxではなく、wを使う点に注意してください。実際に何pxの画像を使っているかなので、PCでサイズを確認して指定します。
WordPressでは、メディアから投稿した画像は、自動で数パターンのサイズで保存されます。表示する際も自動でsrcset属性が付与されます。
<img
loading="lazy"
class="..."
src=".../hoge.jpg"
alt="..."
width="1600" height="1000"
srcset=".../hoge.jpg 1600w,
.../hoge.jpg-300x200.jpg 300w,
.../hoge.jpg-1024x682.jpg 1024w,
.../hoge.jpg-768x512.jpg 768w,
.../hoge.jpg-1536x1023.jpg 1536w"
sizes="(max-width: 1600px) 100vw, 1600px"
>
このような感じになっているはずです。
sizes="(max-width: 1200px) 100vw, 1200px"
sizes属性の部分だけを切り取りました。sizes属性では画像の表示サイズを指定できます。
上の例の指定では、1200pxまではビューポートサイズに対して100%で、それ以外は1200pxの固定で表示しています。
ややこしいのですが、sizes属性は表示サイズを指定するためは使いません。
- imgタグにwidthとheightを指定すると、表示サイズはそのサイズになる
- CSSでwidthとheightを指定するとimgタグのwidthとheightは無視される
sizes属性を指定しても、上のような挙動になります。
図にすると、下のような疑問がわきます。
こう見るとsizes属性は不必要な気がしてしまいますが、必要です。
sizes="100vw"
だけしか書かない場合や、sizes 属性を省略( = デフォルトの100vwが適応)した場合を例に解説します。
<!-- 例1 -->
srcset="..., img/1200.png 1200w, img/2400.png 2400w"
sizes="(max-width: 1200px) 100vw, 1200px"
<!-- 例2 -->
srcset="..., img/1200.png 1200w, img/2400.png 2400w"
sizes="100vw"
例1と例2は、共にデバイスピクセル比2まで対応するために、表示される最大のサイズ(=1200px)の2倍の画像まで用意しています。
これをデバイスピクセル比が1、ビューポートサイズが2400pxの環境で見ると結果は下記の表のようになります。
書き方 | 表示される画像 |
---|---|
例 1 | 1200.png |
例 2 | 2400.png |
表示される最大サイズが1200pxなので、デバイスピクセル比が1であれば、1200.pngが表示されてほしいのですが、例 2 ではそうなりません。
上のようにブラウザが判断しています。従ってsizes属性は必要です。
- sizes属性は画像の出しわけのために必要
- widthとheightはレイアウトシフト防止のために必要
- 実際の表示サイズはCSSで設定する
まとめると上のようになります。
200~2000pxの間で200px刻みで画像を用意して、出し分けたデモを作りました。
幅を変えると画像が変わるのがわかると思います。ただし、ブラウザによって挙動が異なります。
例えばGoogle Chromeではキャッシュの仕様で、大きいサイズから小さいサイズに変更した際、リロードしない限り画像は切り替わりません。大きいサイズの画像を既に読み込んでるので、わざわざそれより小さい画像を読み込んで表示させる必要はないという考え方だと思います。
Google Chromeで確認する際は、小さいサイズから大きいサイズに変更して、確認してみてください。
💡 その他注意点
- src属性はsrcset属性が非対応のブラウザ向けの記述
- sizes属性を使うときはsrcset属性は1xや2xではなく、200wや400wなどのサイズでの指定でしか使えない
何パターンの画像を用意すべき?
何パターンの画像を用意すべきは悩ましいところです。
アナリティクスを導入しているサイトであれば、ユーザーの環境を調べて判断しても良いと思います。
デバイスの種類はかなりの数があり、全てに最適化するのは無理なので、個人的にあまり神経質にならなくても良いとは思います。
💡 例えば...
- 画面に対して90%で中央配置
- 最大幅は1000px
このような仕様であれば、4パターンくらい用意すれば良いのではないでしょうか?
- 360px × 90% = 324px
- 324px × 2 = 648px
- 最大幅の 1000px
- 1000px × 2 = 2000px
上の4パターンです。
このメディアは80%以上がPCからの閲覧で、SPは70%以上がiPhoneなので324pxの画像はいらないといった判断も出来ます。(iPhoneはほぼ全てがデバイスピクセル比2以上なので)
4パターンの用意は、SPのデザインとPCのデザインでそれぞれ書き出すだけなので、そこまで工数はかかりません。
picture タグを使ったアートディレクション - 異なる見た目の画像を出し分ける
バナーは全て画像にする想定です。この例のようにSPとPCで"見た目の違う"画像を表示したい場合、pictureタグを使います。
<picture>
<source media="(max-width: 500px)" srcset="img/sp.png" />
<source media="(max-width: 700px)" srcset="img/tb.png" />
<img width="1600" height="800" src="img/pc.png" alt="" />
</picture>
- 500px以下はsp.pngを表示
- 700px以下はtb.pngを表示
- それ以外とpictureタグ非対応のブラウザではpc.pngを表示
このように異なる見た目の画像を出し分ける方法をアートディレクションといいます。バナーやメインビジュアルなどで使う機会があります。
pictureタグを使うと、displayで切り替える方法と違って、表示する画像しか読み込まれないので、パフォーマンス的に良いです。
sizes属性で使うメディアクエリも同じですが、上から順番にマッチするか判断して、マッチしたらそれ以降は無視されるので、順番に気をつける必要があります。
また、srcset属性を使った出しわけと違ってブラウザが勝手に判断することはなく、必ず制作者の意図通りビューポートサイズに応じて出しわけが出来ます。
出し分ける画像が、全て同じ縦横比であれば、最後のimgタグにwidthとheightを記載すればレイアウトシフトは起こりません。しかし、縦横比の違う画像を使う場合は、レイアウトシフトが起こってしまいます。
これは諦めるか、CSSで予め領域を確保しておくなどの対策をするしかないです。
<div class="hoge">
<picture>
<source media="(max-width: 500px)" srcset="img/sp.png" />
<source media="(max-width: 700px)" srcset="img/tb.png" />
<img src="img/pc.png" alt="" class="hoge-img"/>
</picture>
</div>
.hoge {
height: 0;
padding-top: calc((1000 / 800) * 100%);
position: relative;
}
@media screen and (min-width: 500px) {
.hoge{
padding-top: calc((800 / 800) * 100%);
}
}
@media screen and (min-width: 700px) {
.hoge{
padding-top: calc((800 / 1600) * 100%);
}
}
.hoge-img {
position: absolute;
left: 0;
top: 0;
width: 100%;
}
一応このようにすればレイアウトシフトは防げます。
2021年6月時点でSafariとIEでは使えませんが、aspect-ratioを使うともっとすっきり書けます。
CSSで縦横比(アスペクト比)を固定できるaspect-ratioについて!具体的な使い道も紹介!aspect-ratioについての記事です。
SP / TB / PCサイズで画像が切り替わるデモを作りました。(デモなのでサイズ設定はてきとうです。)
srcset属性とpictureタグの使い分け
srcset属性とpictureタグ、どちらを使って出し分けるかの判断は、上の図のようになります。
ただし、pictureタグを使ったアートディレクションで、画像の出し分けもできます。
<picture>
<source
media="(max-width: 500px)"
srcset="img/sp.png 400w, img/sp@2x.png 800w"
/>
<source
media="(max-width: 700px)"
srcset="img/tb.png 800w, img/tb@2x.png 1600w"
/>
<img
src="img/pc.png"
srcset="img/pc.png 1600w, img/pc@2x.png 3200w"
width="1600"
height="800"
alt="..."
/>
</picture>
このような感じです。
IEも対応するにはPolyfillが必要
今まで解説してきた、srcset属性やpictureタグはIE非対応です。
IEも対応したい場合は、Polyfillを使います。ただ、IEでは最適な画像の出しわけが出来ないだけで、画像自体は表示されるので、Polyfillを使ってまで対応すべきケースはあまりないのではないでしょうか?
どうしても対応したい場合は、ご自身で調べてみてください。
CSSの背景画像の最適化はimage-setを使おう!
今まで、HTMLで画像を用事する方法を紹介してきましたが、CSSの背景画像として画像を入れたい場合もあります。
CSSの背景画像の最適化(出し分け)にはimage-set
を使います。
使い方は上の記事で詳しく紹介しています。
超実務向け!コーディングの練習が出来るサービス作りました 🎉
模写修行は実戦に沿った形で、コーディングの"練習"ができるサービスです。
こんな方におすすめ!
- 実務のレベル感を知りたい
- プロの制作フローを学びたい
- 何を考えながらコードを書くのか知りたい
- 基礎学習中、もしくは終えたばかり
- CSS設計やSCSSを使った書き方を学びたい
模写修行ではデザインデータ(XD) / web上で見れる解説 / サンプルコードを提供しています。ご自身でデザインを見ながらコーディングに挑戦し、解説とサンプルコードで答え合わせ&さらに深く学ぶことができるサービスです。
他の書籍や教材との違いは、超初心者向け・学習用の書き方ではなく、実際の現場で通用する書き方や考え方にフォーカスしている点です。詳しくは下記の記事をご覧ください。
コーディング教材の【模写修行】は他の教材と何が違うのか?嬉しい声もいただいています!
自分のweb制作におけるコードの書き方、ファイル管理は模写修行さんから学ばせていただきました。独学の強い味方だと思いました。教材、メディアありがとうございました!
TwitterでDMいただきました!🙇
classの命名迷子・横並びレイアウト恐怖症だったけど、無料〜中級までいくつかやっていく中で、どんどん迷いが減っていって楽しかったです!解説記事もわかりやすい...!
Tweet
デザインきれいだし、解説とサンプルソースが付いて答え合わせしやすいところが良い。
Tweet
答え合わせ終わった。top-kvの実装方法に脱帽。そういうやり方があることを知れたことが大きい。。sass学びはじめたばかりなのでscssコードがとても参考になった。次からはscssでかく。
Tweet