htmlcss レスポンシブイメージ

2013年9月18日

スクリーンサイズに適した画像を読み込む方法のメモ。

1つの大きな画像をフルードイメージ(下記)で表示する場合、スマートフォンでもデスクトップ用の大きな画像を読み込むことになり表示が遅くなる。

フルードイメージによる画像の調整

フルードイメージ(Fluid Image)
ブラウザのウィンドウ幅に合わせて画像のサイズをフィットさせる方法。
ウィンドウサイズより大きい画像でも、ウィンドウサイズ(または親要素の幅)に応じて、縦横比を保持したまま自動的に画像が拡大・縮小するようにする手法

具体的には、img 要素に対して以下のスタイルを適用する。

img { width : auto}   /* IE8 */
img { max-width : 100%}

HTML の記述では、width と height の入力はしない。

<div id="main">
    <img src="images/main.jpg" alt="">
</div>

画像だけではなく動画などのメディアも含める場合の例

img, video, object {
  max-width: 100%;
  height: auto;
  width: 100%\9; /* IE8のみ適用 */
}
フルードイメージ対応ブラウザ
max-width プロパティは主要なブラウザでサポートされているが、IE6~IE7 ではサポートされていない。但し、IE6~IE7 を搭載したスマートフォンはない。

スクリーンサイズに応じて適切なサイズの画像を読み込むには、以下のような方法がある。

メディアクエリーと background プロパティを使っての切り替え

ベースのスマートフォン(320px)用の画像と、メディアクエリーで設定している「480px」「768px」などのブレークポイントごとの画像を用意して切り替える。

メディアクエリーの記述は小さなスクリーンから大きなスクリーンへと順番に CSS を記述する。

幅 320px~479px の場合の

メディアクエリーを使用せず、HTML に img 要素で幅 320px 用の画像を配置。

<div class="resp_image">
    <img src="images/w320.jpg" alt="">
</div>
/* CSS(デフォルト) */
img { width : auto}   /* IE8 */
img { max-width : 100%}

幅 480px 以上の場合

メディアクエリー内に background プロパティで背景画像として幅 480px 用の画像を配置。(必要であれば margin 等も指定)
この時、320px の場合表示していた img 要素は「visibility: hidden」を指定して非表示にする。

@media only screen and (min-width: 480px){
 .resp_image{
    height: 250px;
    background: url(images/w480.jpg) top center no-repeat;
  }

  .key-visual img{ visibility: hidden}
  
}

幅 768px 以上の場合

幅 480px 以上の場合と同様に、メディアクエリー内に background プロパティで背景画像として幅 768px 用の画像を配置。

@media only screen and (min-width: 768px){
 .resp_image{
    height: 220px;
    background: url(images/w768.jpg) top center no-repeat;
  }

  .key-visual img{ visibility: hidden}
  
}

background-size プロパティ

480px~767px のスクリーンでは 480px の背景画像を指定しているため、720px のスクリーン幅で表示すると画像の左右に大きな余白ができる。この余白は background-size プロパティを使って画像を伸縮させて調整することが可能。

background-size プロパティの値
意味
auto 自動的に算出される(初期値)
cover(覆う) 縦横比は保持して、背景領域を完全に覆う(カバーする)最小サイズになるように背景画像を拡大縮小
contain(全体的に含む、包含する) 縦横比は保持して、背景領域に収まる最大サイズになるように背景画像を拡大縮小。背景画像の縦横比を保持したまま、常に背景画像の全体を表示させる。
数値で指定 背景画像の幅・高さを数値(px, %, em)指定する
画像の幅のみを指定した場合:高さは暗黙的に auto になる
background-size: 50%
1つ目の値で画像の幅、2 つ目の値で高さを指定
background-size: 3em 25%
100% 100%(縦横比を保持しない)
100% auto(背景画像の縦横比を保持し、高さは自動設定。)
auto 100%(背景画像の縦横比を保持し、幅は自動設定。)

以下のように指定すると、画像は画面幅いっぱいにフィットして表示される。

.resp_image{
    height: 250px;
    background: url(images/w480.jpg) top center no-repeat;
    -webkit-background-size: cover;
    background-size: cover;
  }

この方法の他に JavaScritp ライブラリー「Response.js」を使った img 要素の置換などもある。但し、Response.js は、ブレイクポイントを px 単位でしか指定できない。

高解像度デバイスへの対応

可能であれば、ロゴは解像度に依存しない SVG などのベクター形式の画像を利用し、見出しはテキストで記述して CSS で装飾するのがいいが、ビットマップ画像を使わなければならない場合は、以下のような方法がある。

  • device-pixel-ratio による背景画像の切り替え
  • image-set による背景画像の切り替え(background-image プロパティの値に指定できる関数で一部のブラウザで試験的に実装されている)
  • img 要素の読み込みを JavaScript で制御(Response.js などを利用)
device-pixel-ratio
デバイスピクセル比を表すプロパティ。1つの CSS ピクセルをいくつの物理ピクセルで表示するかを数値で表す(画像の1ピクセルをデバイスで何ピクセルとして描画するかを表した値)。この比率を利用することで、高解像度のディスプレイでも通常の解像度のディスプレイと見た目のサイズが同じように表示される。
iPhone 3GS:CSS ピクセルと物理ピクセルが一致しているので値は「1」
iPhone 4:CSS ピクセルを縦横2倍の物理ピクセルで拡大しているので値は「2」
Android 端末などは「1.5」などが多い

device-pixel-ratio をメディアクエリーの条件に記述することで、解像度ごとに異なるスタイルを適用できる。

/*iPhone3~3GS、低解像度 Android 端末と devicePixelRatio 未対応ブラウザ */

.someClass {
     background: url(images/1x.png); /* 等倍サイズの画像 */
}

/* devicePixelRatio=1.5 (Android端末など)*/
@media only screen and (-webkit-min-device-pixel-ratio: 1.5),
only screen and (min-resolution: 1.5dppx) {

     .someClass {
          background: url(images/1.5x.png); /* 1.5倍サイズの画像 */
     }
}

/* devicePixelRatio=2 (iPhone4~4S、高解像度Android端末)*/
@media only screen and (-webkit-min-device-pixel-ratio: 2),
only screen and (min-resolution: 2dppx) {

     .someClass {
          background: url(images/2x.png); /* 2倍サイズの画像 */
     }
}

「-webkit-min-device-pixel-ratio」は Webkit の独自実装なので W3C が定義している「min-resolution」による指定も合わせて記述。「min-resolution」は単位が「dppx」になっているが、値は「device-pixel-ratio」と同じで、CSS ピクセルと物理ピクセルの比率をあらわす。

また、ボタンやアイコンなどに背景画像を使用している場合は background-size プロパティを使ってサイズを指定するとよいとのこと。

background-size: 幅 高さ

/* devicePixelRatio=1.5 (Android端末など)*/
@media only screen and (-webkit-min-device-pixel-ratio: 1.5),
only screen and (min-resolution: 1.5dppx) {

     .someClass {
          background: #001c33 url(images/2x.png) no-repeat 0 0; /* 2倍サイズの画像と背景色の指定 */
          -webkit-background-size: 20px 20px; /* 表示サイズの指定 */
          background-size: 20px 20px; /* 表示サイズの指定 */
     }
}

/* devicePixelRatio=2 (iPhone4~4S、高解像度Android端末)*/
@media only screen and (-webkit-min-device-pixel-ratio: 2),
only screen and (min-resolution: 2dppx) {

     .someClass {
          background: #001c33 url(images/2x.png) no-repeat 0 0; /* 2倍サイズの画像と背景色の指定 */
          -webkit-background-size: 20px 20px; /* 表示サイズの指定 */
          background-size: 20px 20px; /* 表示サイズの指定 */
     }
}

以下は CSS-Tricks「Retina Display Media Query」から

Retina ディスプレイ用

@media
only screen and (-webkit-min-device-pixel-ratio: 2),
only screen and (   min--moz-device-pixel-ratio: 2),
only screen and (     -o-min-device-pixel-ratio: 2/1),
only screen and (        min-device-pixel-ratio: 2),
only screen and (                min-resolution: 192dpi),
only screen and (                min-resolution: 2dppx) { 
  
  /* Retina-specific stuff here */
   .class {
          background: url(images/2x.png); /* 2倍サイズの画像 */
     }

}

上記の「min–moz-device-pixel-ratio」はおそらくバグ。

The super weird min–moz-device-pixel-ratio is probably a bug, might wanna put in -moz-min-device-pixel-ratio also in case they fix it but leave it prefixed.


以下は3つのブレークポイントがある場合の例。6つのメディアクエリーをそれぞれのブレークポイント(高解像度と低解像度)に記述する。

CSS-Tricks「Retina Display Media Query」から

@media only screen and (min-width: 320px) {

  /* Small screen, non-retina */

}

@media
only screen and (-webkit-min-device-pixel-ratio: 2)      and (min-width: 320px),
only screen and (   min--moz-device-pixel-ratio: 2)      and (min-width: 320px),
only screen and (     -o-min-device-pixel-ratio: 2/1)    and (min-width: 320px),
only screen and (        min-device-pixel-ratio: 2)      and (min-width: 320px),
only screen and (                min-resolution: 192dpi) and (min-width: 320px),
only screen and (                min-resolution: 2dppx)  and (min-width: 320px) { 

  /* Small screen, retina, stuff to override above media query */

}

@media only screen and (min-width: 700px) {

  /* Medium screen, non-retina */

}

@media
only screen and (-webkit-min-device-pixel-ratio: 2)      and (min-width: 700px),
only screen and (   min--moz-device-pixel-ratio: 2)      and (min-width: 700px),
only screen and (     -o-min-device-pixel-ratio: 2/1)    and (min-width: 700px),
only screen and (        min-device-pixel-ratio: 2)      and (min-width: 700px),
only screen and (                min-resolution: 192dpi) and (min-width: 700px),
only screen and (                min-resolution: 2dppx)  and (min-width: 700px) { 

  /* Medium screen, retina, stuff to override above media query */

}

@media only screen and (min-width: 1300px) {

  /* Large screen, non-retina */

}

@media
only screen and (-webkit-min-device-pixel-ratio: 2)      and (min-width: 1300px),
only screen and (   min--moz-device-pixel-ratio: 2)      and (min-width: 1300px),
only screen and (     -o-min-device-pixel-ratio: 2/1)    and (min-width: 1300px),
only screen and (        min-device-pixel-ratio: 2)      and (min-width: 1300px),
only screen and (                min-resolution: 192dpi) and (min-width: 1300px),
only screen and (                min-resolution: 2dppx)  and (min-width: 1300px) { 

  /* Large screen, retina, stuff to override above media query */

}

画面サイズにより読み込む画像を変更する

サンプル

画面サイズの小さいデバイスでは小さめの画像を、画面サイズの大きいデバイスでは大きい画像を読み込む方法。但し、この方法の場合、画面サイズの大きいデバイスでは最初に小さい画像を読み込み、その後大きい画像を読み込むことになるので両方の画像を読み込むことになる。果たしてそれが望む方法かは、検討する必要がある。

  • 小さい画像は「images/small/」に配置
  • 大きい画像は「images/full/」に配置
  • HTML には小さい画像のパスを記述
  • jQuery で画面サイズが大きい場合は画像のパスを書き換える
<div id="foo">
<p><img src="images/small/snow.jpg" alt=""></p>
</div>
  • navigator.userAgent でユーザーエージェントを検出(Android 2.x ではウィンドウの横幅を正確に取得できないため)
  • indexOf(‘Android 2’)で「Android 2」が含まれているかを判定し、変数を設定
  • window.devicePixelRatio:デバイスピクセル比
  • on を使って、ページがロードされたとき、またはウィンドウがリサイズされた場合に以下の処理を実行
  • 変数 device の値や、「document.all(IE のプロパティ)」を調べて画面の幅(sw)を設定
  • 画面の幅が 600px 以上の場合は、画像のパスを「images/small/」から「images/full/」に変更

jQuery

<script src="http://code.jquery.com/jquery-1.10.1.min.js"></script>
<script>
jQuery(function($){
  if(navigator.userAgent.indexOf('Android 2') != -1){
    var device = "android2x";
    var ratio = window.devicePixelRatio;
  }
  $(window).on('load resize', function(){
    if(device == "android2x"){  //android2x の場合
      var sw = window.outerWidth/ratio;
    }else{
      if(document.all){  //ie の場合
        var sw = document.body.clientWidth;
      }else{  //それ以外の場合
        var sw = window.innerWidth;
      }
    }
    if(sw >= 600){
      $("#foo img").attr("src",$("img").attr("src").replace(/small/, "full"));
      //以下は複数の画像を変更する場合
      /* $("#foo img").each(function() {
          $(this).attr("src",$(this).attr("src").replace(/small/, "full"));
      }); */
    }
  });
});
</script>

画面サイズ(またはユーザーエージェント)により画像を読み込むかどうかを判定する

画像などを非表示(display: none;)にしても、画像自体は読み込まれてしまうので、画面サイズやユーザーエージェントを検出して、大きな画面サイズのデバイスの場合のみ画像を読み込むようにする方法。

HTML には div 要素のみを記述。

<div id="thumbnails"></div>

画像のリストなどのマークアップをテキストファイルとして保存

thumbnails_list.txt

<ul>
<li><img src="images/thumbnails/1.jpg" alt=""></li>
<li><img src="images/thumbnails/2.jpg" alt=""></li>
<li><img src="images/thumbnails/3.jpg" alt=""></li>
<li><img src="images/thumbnails/4.jpg" alt=""></li>
</ul>

jQuery で画面幅(またはユーザーエージェント)を検出して、大きな画面のデバイスであれば「thumbnails_list.txt」をロードする。

<script src="http://code.jquery.com/jquery-1.10.1.min.js"></script>
<script>
jQuery(function($){
  if(navigator.userAgent.indexOf('Android 2') != -1){
    var device = "android2x";
    var ratio = window.devicePixelRatio;
  }
  $(window).on('load resize', function(){
    if(device == "android2x"){
      var sw = window.outerWidth/ratio;
    }else{
      if(document.all){
        var sw = document.body.clientWidth;
      }else{
        var sw = window.innerWidth;
      }
    }
    if(sw >= 600){
        $("#thumbnails").load("thumbnails_list.txt");
    }
  });  
});
</script>

ユーザーエージェントを検出する場合

iPhone, iPad, Android の場合以外は画像を読み込む

<script src="http://code.jquery.com/jquery-1.10.1.min.js"></script>
<script>
jQuery(function($){  
  if(navigator.userAgent.indexOf("iPhone") == -1 &&
      navigator.userAgent.indexOf("iPad") == -1 &&
      navigator.userAgent.indexOf("Android") == -1){
    $("#thumbnails").load("thumbnails_list.txt");
  }
});
</script>