模写修行メディア

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

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

  • レスポンシブイメージ
  • 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の画像を読み込むことになってしまいます。

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

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

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

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

前のセクションで、NG例を紹介しました。NG例の問題点を解決するのがレスポンシブイメージです。

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

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

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

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

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つの画像を指定しています。

ユーザーの環境画像
デバイスピクセル比が1200.pngを表示
デバイスピクセル比が2400.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属性を指定しても、上のような挙動になります。

図にすると、下のような疑問がわきます。

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

こう見るとsizes属性は不必要な気がしてしまいますが、必要です。

重要!
sizes属性はsrcset属性で指定した画像の中のどの画像を表示するかブラウザが決める際に役に立ちます。

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の環境で見ると結果は下記の表のようになります。

書き方表示される画像
例 11200.png
例 22400.png

表示される最大サイズが1200pxなので、デバイスピクセル比が1であれば、1200.pngが表示されてほしいのですが、例 2 ではそうなりません。

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

上のようにブラウザが判断しています。従って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 タグを使ったアートディレクション - 異なる見た目の画像を出し分ける

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タグの使い分け

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 の背景画像を最適化!Retina ディスプレイ(高解像度)対応する方法

使い方は上の記事で詳しく紹介しています。

コーディングの練習が出来るサービスを作りました 🎉

模写修行はこんな方におすすめ!

  • プロの制作フローを学びたい
  • どんなことを考えながら書くのか知りたい
  • 実践的な練習がしたい

【初級編】はこんな方におすすめ!

  • 基礎学習中、もしくは終えたばかり
  • サイト制作経験が5サイト未満 (実際の案件でなくてもOK)

【中級編】はこんな方におすすめ!

  • CSS設計を意識したコーディングを学びたい
  • SCSSを使った書き方を学びたい (解説はSCSSを使わない前提でします)

模写修行ではデザインデータ(XD) / web上で見れる解説 / サンプルコードを配布しています。ご自身でデザインを見ながらコーディングに挑戦し、解説とサンプルコードで深く学ぶことができるサービスです。

模写修行を見る

この記事を書いた人

Gakuのアイコン

Gaku / @gaku92014091

大学・大学院時代から個人で仕事をする。卒業後5年間のフリーランス期を経て、法人化。マーケティング、デザイン、コーディング、プログラミング(フロント)、幅広くやります。webサービス作るのが好き!

\Share/

模写修行のトップページのスクリーンショット
模写修行

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