🎉 お知らせ:サイト名が『webkore』から『模写修行 media』に変更になりました。6月以降にドメインも『moshashugyo.com/media/』へ変更する予定です。

HTML/CSS

HTMLのsrcset属性やpictureタグを使ったレスポンシブイメージを解説

Author:Gaku(gaku92014091)

この記事の目次

サイトを制作する際の画像周りの最適化は複雑で、ベストプラクティスがわからなかったり、疎かにしている人は多いのではないでしょうか?

  • レスポンシブイメージ
  • decoding="async"
  • loading='lazy'
  • レイアウトシフト
  • webp等の次世代画像フォーマット

などなど。画像周りでは知っておかないといけないことが多くあります。

この記事では、その中でも特に重要な、srcset属性やpictureタグを使ったレスポンシブイメージについて解説します。

この記事を書くにあたり、検証はしましたが若干不安な点もあるので、もし何か間違っていることがあればTwitter(@gaku92014091)までリプやDMをいただければ嬉しいです。

画像の最適化が大切な理由

現代のwebサイトはファイルサイズの多くを画像が占めるケースも少なくありません。

Googleは1ページあたり1.6MB程度のファイルサイズを推奨していますが、画像を上手く扱わないと簡単にオーバーしてしまいます。

  • 適切な画像フォーマットの使用
  • 画像の圧縮
  • レスポンシブイメージの使用

画像を多く使っているサイトであれば特に、これらをしっかりやるだけで、サイト全体のファイルサイズを大幅に減らすことも可能かもしれません。

サイトのパフォーマンス(≒ 表示速度)は、ユーザービリティーやSEOにも関わる、重要な問題なので、出来る範囲で対応すべきです。

よくやりがちなNG例

  • とりあえず大きめの画像を使っておく
  • display: none;で画像を切り替える

これらはレスポンシブイメージ関連で、よくやりがちな代表的なNG例です。なぜ良くないのか、理由を紹介します。

とりあえず大きめの画像を使っておく

html
<div class="hoge">
    <img width="2000" height="1000" src="img/hoge.jpg" alt="..." class="hoge-img" />
</div>
css
.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の画像を読み込むことになってしまいます。

Point

この問題は後述する、srcset属性を使って、数パターンの画像を用意して出し分けることで解決します。

display: none;で画像を切り替える

html
<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>
css
.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時で"見た目の違う"画像を多用するサイトでは、パフォーマンスに影響が出ることもあります。

Point

この問題は後述する、pictureタグを使って解決します。

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

レスポンシブイメージとは、レスポンシブwebデザインでの画像の扱いのことです。

  • 適切なサイズの画像を出し分ける
  • 異なる見た目の画像を出し分ける

これらのことをHTMLだけで実装出来ます。次のセクションからこの2点に関して、解説します。

srcset属性を使った画像の出しわけ - 適切なサイズの画像を出し分ける

srcset属性を使った画像の出しわけ

昨今のデバイスは高解像度ディスプレイも多く、表示サイズと同じサイズの画像しか用意していないと、閲覧環境によっては画像が粗く見えます。

解像度 / Retinaディスプレイ(高解像度ディスプレイ) / デバイスピクセルとCSSピクセル / あたりの解説はわかりやすい記事を載せておきます。

どんな閲覧環境でもある程度、綺麗に見えて、パフォーマンスに影響がないようにするために、srcset属性を使い、適切なサイズの画像を出し分ける必要があります。

今閲覧している環境のデバイスピクセル比などはこちらにアクセスするとわかります。

  • デバイスの解像度のみで判断して出し分けするケース
  • ビューポートサイズと解像度で判断して出し分けするケース

srcset属性を使った画像の出しわけは、この2パターンです。それぞれ解説します。

デバイスの解像度のみで判断して出し分けするケース

html
<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 横幅200pxの画像
2 横幅400pxの画像
3 横幅600pxの画像
4 横幅800pxの画像

デモではデバイスピクセル比1~4まで対応したので、上の表のように表示されるはずです。是非デバイスを変更して確認してみてください。


その他注意点
  • src属性はsrcset属性が非対応のブラウザ向けの記述です。
  • widthとheightはレイアウトシフト防止のために入れましょう。

ビューポートサイズと解像度で判断して出し分けするケース

html
<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>
css
.hoge{
    max-width: 1200px;
}

.hoge-img{
    width: 100%;
    height: auto;
    vertical-align: bottom;
}
どんな時に使う?
  • 画像を可変幅で表示する場合
  • ビューポートサイズに対して100%で表示
  • 最大幅は1200px

例えば、このようなケースで使います。1200pxに達するまでは、常にビューポートサイズに対して100%の幅で表示しているので、画像自体の大きさは閲覧環境に依存します。

解像度によって出し分けるケースと同じく、必要な画像しか読み込まれません。


html
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属性が付与されます。

html
<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">

このような感じになっているはずです。


html
sizes="(max-width: 1200px) 100vw, 1200px"

sizes属性の部分だけを切り取りました。sizes属性では画像の表示サイズを指定できます。

上の例の指定では、1200pxまではビューポートサイズに対して100%で、それ以外は1200pxの固定で表示しています。ただし、imgタグにwidthとheightを指定すると、sizes属性での表示サイズの指定は無視されます。また、CSSでwidthとheightを指定するとimgタグのwidthとheightは無視されます。

imgタグのwidthとheightはレイアウトシフト防止のために書いておきたいので、少しややこしいことになります。

srcset属性を使った画像の出しわけ

図にするとこんな感じになります。

こう見るとsizes属性は不必要な気がしてしまいますが、sizes属性はsrcset属性で指定した画像の中のどの画像を表示するかブラウザが決める際に役に立ちます。

sizes="100vw"だけしか書かない場合や、sizes属性を省略(=規定値の100vwが適応)した場合を例に解説します。

html
<!-- 例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ではそうなりません。

例1の場合

sizes属性で1200px以上は1200pxで固定にしているので、ブラウザは1200pxで見た時に最適なサイズを計算して画像を選んでいる

例2の場合

sizes属性で常に100vwにしているので、ブラウザは100vw=ビューポートサイズ=2400pxで見た時に最適なサイズを計算して画像を選んでいる

このようにブラウザが判断するので、表のような結果になっています。従ってsizes属性は表示サイズを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

例えば、このメディアは80%以上がPCからの閲覧で、SPは70%以上がiPhoneなので324pxの画像はいらないといった判断も出来ます。(iPhoneはほぼ全てがデバイスピクセル比2以上なので)

4パターンの用意は、SPのデザインとPCのデザインでそれぞれ書き出すだけなので、そこまで工数はかかりません。

pictureタグを使ったアートディレクション - 異なる見た目の画像を出し分ける

pictureタグを使ったアートディレクション

このバナーは全て画像にする想定です。この例のようにSPとPCで"見た目の違う"画像を表示したい場合はpictureタグを使います。

html
<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属性を使った出しわけと違ってブラウザが勝手に判断することはなく、必ず制作者の意図通りビューポートサイズに応じて出しわけが出来ます。

pictureタグを使用する場合、全て同じ縦横比の画像であれば、最後のimgタグにwidthとheightを記載すればレイアウトシフトは起こりません。しかし、縦横比の違う画像を使う場合は、レイアウトシフトが起こってしまいます。

これは現状諦めるか、CSSで予め領域を確保しておくなどの対策をするしかないと思います。

html
<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>
css
.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%;
}

一応このようにすればレイアウトシフトは防げます。


SP/TB/PCサイズで画像が切り替わるようになっています。(デモなのでサイズ設定はてきとうです。)

srcset属性とpictureタグの使い分け

srcset属性とpictureタグの使い分け

srcset属性とpictureタグ、どちらを使って出し分けるかの判断はこの通りなので、とても簡単です。ただし、pictureタグを使ったアートディレクションをする際、sourceタグの中でもsrcset属性は使えます。

html
<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を使ってまで対応すべきケースはあまりないのではないでしょうか?

どうしても対応したい場合は、ご自身で調べてみてください。

駆け出しエンジニアのためのコーディング練習教材

Coming Soon
© 2021 模写修行 media.