pictureタグとsrcset属性の違い。超わかりやすく

結論からいうと、以下のように違います。

  • srcset属性・・・”異なるサイズの同じ画像”を表示したい場合に使う
  • pictureタグ・・・”異なる画像”を表示したい場合に使う

 

図解すると以下のような感じです🤗

▲この例では、例えばスマホ端末に対してはpictureタグで犬の画像を表示するように”強制”し、srcset属性でブラウザに対して「好きなサイズを選べ」と選ばせる。

srcset属性

例えば、スマホ端末に対して1920×1080の画像を表示するのはムダです。

もっと小さいサイズでいいですからね。

逆にデスクトップPCだと画面が大きいので、1920×1080の画像を表示しても良いですよね。

 

このように

  • デスクトップPC
    →1920×1080の画像を表示する
  • ノートパソコンPC
    →1280×720の画像を表示する
  • スマホ
    →640×360の画像を表示する

という風に、大きさの異なる画像を出し分けたいときに使うのがsrcset属性です。

srcset属性のデモページソース
※後述していますが、ChromeやSafariの場合はスーパーリロードしないと画像が切り替わりません

 

例えば、以下のように書くと

<img 
  srcset="small.png 720w, medium.png 1280w, large.png 1920w"
  src="medium.png"
/>

imgタグは、ブラウザに対して以下のように提案します。(後ろについているwについては後述します)

やぁよく来たね。

さっそくだけど君は以下の3つのサイズから選ぶことができるよ。

  • small.pngの横幅:720px
  • medium.pngの横幅:1280px
  • large.png:1920px

君の環境に合う画像だけを持っていくといいよ。

もし、srcset属性を理解できない場合はsrc属性を使ってね。(IE向け)

 

この場合、ブラウザは

  • ブラウザの横幅が720px以上なら720px以上の画像がないか探す
    small.pngを選ぶ
  • ブラウザの横幅が1280px以上なら1280px以上の画像がないか探す
    medium.pngを選ぶ
  • ブラウザの横幅が1920px以上なら1920px以上の画像がないか探す
    large.pngを選ぶ

という動きをします。

 

しかしながら、常にブラウザの横幅いっぱいいっぱい(=100%)の画像がほしいとは限りません。

そういうときに使えるのがsizes属性です。

sizes属性のデモページソース

 

例えば、以下のように指定すると

<img
  srcset="small.png 720w, medium.png 1280w, large.png 1920w"
  sizes="(max-width: 350px) 720px,
        (max-width: 600px) 1280px,
        100vw"
  src="medium.png"
/>
  • ブラウザの横幅が350px以下なら
    →imgタグは「この画像は720pxで表示すべきやと思うやで」とブラウザに伝える
    →結果的にブラウザは「ほなsmall.pngを使ったらええな。あと横幅は720pxで表示したらええな」と判断する
  • ブラウザの横幅が600px以下なら
    →imgタグは「この画像は1280pxで表示すべきやと思うやで」とブラウザに伝える
    →結果的にブラウザは「ほなmedium.pngを使ったらええな。あと横幅は1280pxで表示したらええな」と判断する
  • どれにも該当しないなら
    →imgタグは「この画像は100vwで表示すべきやと思うやで」とブラウザに伝える
    →結果的にブラウザは「ほなlarge.pngを使ったらええな。あと横幅は100vwで表示したらええな」と判断する

のような動きになります。

 

ただし、ここで注意しなければいけないのは、ブラウザによって以下のように挙動が異なることです。

  • Chrome : 大きなサイズの画像ファイルをキャッシュした場合、画面幅を狭めても小さい画像は読み込まれない
  • Firefox : 画面幅を変える度に、画面幅に適したサイズの画像を読み込む
  • Safari : 最初に開いた画面幅に応じた画像ファイルがキャッシュされ、画面幅を変えても画像は再読み込みされない

引用:https://ics.media/entry/13324/

これはつまりですね。

例えばChromeの場合、ブラウザの横幅が1920px以上の状態でアクセスすると「large.pngを取得したろ!」と判断しますが

そのあとにブラウザのサイズを小さくして、横幅が200px以下になったとしてもChromeは「大は小を兼ねるって言うし、キャッシュしたlarge.pngを使いまわしたらええやろ!」と判断して、small.pngは取得してくれません😂

先ほどのデモページで試してみてください。(ただし、ページをスーパーリロードしないとキャッシュが破棄されないので注意です)

 

Safaiも違った動きをします。

例えばSafariブラウザの横幅が200px以下の状態でアクセスすると「small.pngを取得したろ!」と判断しますが

そのあとにブラウザのサイズを大きくして、横幅が1920px以上になったとしてもSafaiは「画質汚いけどキャッシュしたsmall.png使っときゃええやろ!通信量を節約するほうが大事やで!」と判断して、large.pngを取得してくれません😂

 

また、将来的にブラウザに「ネットワーク使用量を極力抑えるモード」なるものが実装されたとして、そのモードをONにすると「srcset属性で提示された中から一番小さいサイズを必ず選ぶ」という動きになったりするかもしれません。

例えば、imgタグが「この画像は1920pxで表示すべきやと思うやで」とブラウザに伝えたとしても、「お前のいうことなぞ聞かぬ!ワイは一番サイズの小さいヤツを取得するやで!」みたいなことになるかもしれません。

 

こういう特性があるため、srcset属性だけでは

  • 大きい画面のユーザーには、犬の画像を表示させたろ!
  • 小さい画面のユーザーには、猫の画像を表示させたろ!

みたいな切り替えが”確実”に実装できるとは限りません。

これはsrcset属性で出し分ける画像は、大きさは違えど画像の中身は同じ」を前提にしているからです😎

pictureタグ

さきほどのように、

  • 大きい画面のユーザーには、犬の画像を表示させたろ!
  • 小さい画面のユーザーには、猫の画像を表示させたろ!

を”確実”に実装するために使うのがpictureタグです。

pictureタグのデモページソース

 

以下のようにpictureタグとsourceタグを組み合わせて使用します。

<picture>
  <source media="(max-width: 350px)" srcset="cat.png" />
  <source media="(max-width: 700px)" srcset="dog.png" />
  <img src="dog.png" />
</picture>

上のコードは

  • ブラウザの横幅が350px以下なら
    →「cat.pngを使え」とブラウザに強制する
  • ブラウザの横幅が700px以下なら
    →「dog.pngを使え」とブラウザに強制する
  • どれにも該当しないなら
    →「dog.pngを使え」とブラウザに強制する

のような意味になります。

 

srcset属性で候補を複数提示することもできます。

 <picture>
  <source media="(max-width: 350px)" srcset="cat-small.png 720w, cat-medium.png 1280w, cat-large.png 1920w"/>
  <source media="(max-width: 700px)" srcset="dog-small.png 720w, dog-medium.png 1280w, dog-large.png 1920w" />
  <img src="dog.png" />
</picture>

上のコードは

  • ブラウザの横幅が350px以下なら
    →「以下の3つから好きなものを選べ」とブラウザに強制する
    cat-small.png:横幅720px
    cat-medium.png:横幅1280px
    cat-large.png:横幅1920px
  • ブラウザの横幅が700px以下なら
    →「以下の3つから好きなものを選べ」とブラウザに強制する
    dog-small.png:横幅720px
    dog-medium.png:横幅1280px
    dog-large.png:横幅1920px
  • どれにも該当しないなら
    →imgタグは「dog.pngを使え」とブラウザに強制する

のような意味になります。

また、sourceタグは一番最初にマッチしたものが適用され、あとは無視されます。(最終的にどれにも該当しなかったら<img src="dog.png" />が適用されます)

なので順番が重要です。

 

ちなみに、type属性を使えば「webp非対応ならjpgを使う」という風にもできます。

<picture>
  <source type="image/webp" srcset="hoge.webp">
  <img src="hoge.jpg">
</picture>

デバイスピクセル比の話

話は変わりますが

最近のスマホは、超高解像度になってきており、下手するとノートパソコンより解像度が高かったりします。

例えば、「Galaxy S21 Ultra 5G」というスマホだと、約7インチの小さい画面のくせに解像度が「3200 x 1440」もあったりして、「誰がこんなもん求めてんだ」と突っ込みたくなる解像度です😅

それはさておき、この「3200 x 1440」という解像度をそのまま使ってしまうと、ブラウザで表示される文字などが極小サイズになってしまって、とても使い物にならなくなってしまいます。

 

そこで「ブラウザでは2 x 2ピクセルを1ピクセルとして扱うようにすればいいのでは?」と誰かが考えました。

おそらく最初にこれを実現したのはAppleだと思うのですが、Appleではこれを「Retinaディスプレイ」などと呼んでいます。
(他のAndroidスマホでは「高精細ディスプレイ」などと呼ぶみたいです)

それはさておき、ここでいう「複数のピクセルを1ピクセルとして扱うようにすればいいのでは?」という考えで設定された

  • 実際のデバイスのピクセル数(3200 x 1440)
    デバイスピクセルと呼ぶらしい
  • ブラウザ上での疑似ピクセル数(1600 x 720)
    CSSピクセルと呼ぶらしい

との比率のことを「デバイスピクセル比」と呼びます。(この場合のデバイスピクセル比は「2」です)

 

つまり

CSSピクセル × デバイスピクセル比 = デバイスピクセル

という式になります。

ちなみにパソコンでも、拡大率を変えることで、ピクセルアスペクト比を変更できます。

先ほどのデモページに移動してから、スクロール+Ctrl(macの場合は⌥キー)で拡大率を変更してみてください。

 

例えば、iPhoneXは

  • 解像度:1125x2436
  • デバイスピクセル比:3.5

と設定されているので、ブラウザでは375x812として認識されます。

 

では、ここで問題です。

iPhoneXで以下のコードを実行すると、どちらの画像が読み込まれるのでしょうか?

<img 
  srcset="small.png 375w, medium.png 1125w"
  src="medium.png"
/>

 

ブラウザでは375x812として認識されるんだから、small.pngの方が読み込まれるんじゃないの?

と思うかもしれませんが、実はmedium.pngの方が読み込まれます。

これは「普段はそのまま表示しちゃうと小さすぎて見にくいから2×2を1ピクセルとして表示してるけど、画像は1ピクセルを1ピクセルとして表示したほうが綺麗に見えるでしょ?」という理由からです。

つまり、画像を表示するときはCSSピクセルではなく、デバイスピクセルが使われます。(このへんがややこしい・・・)

ただ、先ほどのデモページでデバイスピクセル比を例えば2に変更してから横幅を変えてみると、FireFoxだと上で説明したとおり、CSSピクセルが200pxを超えると画像が切り替わるのですが、ChromeやEdgeだと253px付近で切り替わります。。

なぜこうなるのかは謎です・・・。(誰かおしえてください😥)

w、x、h

さきほど以下のようなコードを書きましたが

<img 
  srcset="small.png 720w, medium.png 1280w, large.png 1920w"
  src="medium.png"
/>

ここでいう

  • 720w
  • 1280w
  • 1920w

の後ろに付いているwは「画像の幅」を表しています。

 

他にも

  • w(画像の幅)
  • h(画像の高さ)
  • x(デバイスピクセル比)

も指定できます。

 

例えば、xを使って以下のような書き方もできます。

<img 
  srcset="small.png 1x, medium.png 2x, large.png 3x"
  src="medium.png"
/>
  • デバイスピクセル比が1なら
    small.pngを選ぶ
  • デバイスピクセル比が2なら
    medium.pngを選ぶ
  • デバイスピクセル比が3なら
    large.pngを選ぶ

 

例えば、hを使って以下のような書き方もできます。

<img 
  srcset="small.png 480h, medium.png 720h, large.png 1080h"
  src="medium.png"
/>
  • ブラウザの高さが480px以上なら
    small.pngを選ぶ
  • ブラウザの高さが720px以上なら
    medium.pngを選ぶ
  • ブラウザの高さが1080px以上なら
    large.pngを選ぶ

 

最初に720wという表記を見たときに「なんでpxじゃないんだ?」と疑問に思ったかもしれませんが、pxだと「画像の高さ」を指定できないからだと思われます。(おそらく・・)

おわりに

この記事のような感じで自分は理解したのですが

もし間違っている箇所があれば、コメントが指摘していただけると大変うれしいです!🙇

 

おわり

HTML/CSS/JavaScript
スポンサーリンク
この記事を書いた人
penpen

1991生まれ。
2019年くらいからフロントエンドエンジニアを目指している元アフィリエイターです💩

職を探しています😭
よろしくお願いします🙇

penpenをフォローする
penpenをフォローする
penpenメモ

コメント

タイトルとURLをコピーしました