風柳メモ

ソフトウェア・プログラミング関連の覚書が中心

【覚書】viewport指定について

モバイルデバイス用のviewport設定についてまじめに調べようとしたら、深みにはまりそうだったので、とりあえず整理を兼ねて覚え書きを残しておく。
ついでに、viewportの簡易的な表示テストができるページも作ってみた




viewport の指定方法

viewportは、HTMLのhead内のmeta要素として指定する

<meta name="viewport" content="(viewportのパラメータ)" />

viewportのパラメータはカンマ(",")区切りで指定。

なお、これは主としてモバイルデバイス(スマートフォン・タブレットなど)用の設定であり、基本的にはデスクトップ用ブラウザの表示には影響を与えない。
ブラウザが常に全画面で表示されるために、基本、ウィンドウの幅=デバイスの表示幅となるモバイルデバイスにおいて(余白が出来たり見切れたりすることなく)適切なページを表示を行うための指定という認識。

viewportの主要なパラメータ

<meta name="viewport"> の content の値
設定可能な値 説明
width 正の整数または文字列 device-width ウェブサイトを描画したいビューポートの幅をピクセル数で定義します。
height 正の整数またはテキスト device-height ビューポートの高さを定義します。どのブラウザーでも使用されていません。
initial-scale 0.0 から 10.0 までの、正の数値 デバイスの幅 (ポートレートモードでの device-width またはランドスケープモードでの device-height) とビューポートの寸法との比率を定義します。
maximum-scale 0.0 から 10.0 までの、正の数値 ズームの最大値を定義します。この値は minimum-scale と同じまたはより大きくしなければなりません。そうではないときの動作は未定義です。ブラウザーの設定でこの規則を無視できます。また、iOS 10 以降は既定で無視します。
minimum-scale 0.0 から 10.0 までの、正の数値 ズームの最小値を定義します。この値は maximum-scale と同じまたはより小さくしなければなりません。そうではないときの動作は未定義です。ブラウザーの設定でこの規則を無視できます。また、iOS 10 以降は既定で無視します。
user-scalable yes または no no を設定すると、ユーザーはページのズームができなくなります。既定値は yes です。ブラウザーの設定でこの規則を無視できます。また、iOS 10 以降は既定で無視します。

<meta>: 文書レベルメタデータ要素 - HTML: HyperText Markup Language | MDN
viewportを指定しないとどうなる?

viewport指定を省略した場合、モバイルデバイスでは、ブラウザが暗黙的にデフォルトのwidth値(ブラウザ毎に異なる、例えば980といった値)を用いて、これをウィンドウ(≒スクリーン)内に収めるように表示される。
例えば、デスクトップ向けのページなどで、横幅がこれより大きい値を前提として設計されていたら見切れてしまう。

device-width とは?

device-widthとは、モバイルデバイスが持つ画面(スクリーン)の論理解像度(ポイント実質解像度CSSピクセル)の横幅のことで、これはカタログに載っている解像度(画素数)とは異なる。
device-heightは同じく縦幅のことだが、ほとんど使うことはないと思われる。
例えば、iPhone X のディスプレイは5.8インチ・1125x2436ピクセルの解像度を持つが、device-widthは375(device-heightは812)となる。
すなわち、ブラウザのウィンドウで375px×812pxの画像を等倍で表示した場合、画面にピッタリと収まるサイズで表示されるということになる。

initial-scale とは?

基本的には、ページを表示した際の、最初の表示倍率(拡大率)のこと。
例えば、initial-scale=1 の時は等倍で、initial-scale=0.5の時は幅が半分に縮小(矩形としては4分の1)、initial-scale=2の時には幅が倍に拡大(矩形としては4倍)となる。

これだけならばよいのだが、ややこしいのが『条件によっては viewport の大きさに影響してしまう』ということ(後述)。

viewport が影響する主な要素

JavaScript

viewportの指定によりブラウザのウィンドウの大きさが決まることになるため、window.innerWidth・innerHeightといったパラメータに影響する。
なお、window.screen.widthやwindow.screen.availWidthは、viewportの指定によらず一定で、デバイス固有の値(≒device-width)を持つ。

viewport 設定テスト用ページについて

簡易的な viewport 設定テスト用ページを作成した。

viewport setting tester

  • radioボタンに応じて、meta[name="viewport"]を書き換えるようになっている。
  • 条件によっては、値を変更しても表示が変わらない場合がある。この場合、[Reload]を押すことで反映される。
    Mobile 版 Firefox では、[Reload]を押しても変わらない場合あり。この場合、ページをいったん閉じて、開きなおす必要がある。
  • Google Chrome の開発者ツールのモバイルデバイスエミュレータ(device toolbar)で、画面の拡大縮小(ピンチアウト/イン)の操作は、[Shift]+ドラッグ(スワイプ)で行う([Ctrl]+[+]/[-]では変化しないことに注意)。リセットは[Ctrl]+[0]。

具体的な viewport 指定の例

"width=device-width, initial-scale=1" で、万全!?

viewportについて調べていると、典型的な設定例として出てくるのは、

<meta name="viewport" content="width=device-width, initial-scale=1" />

のようなもの。

  • viewport(≒ブラウザのウィンドウ)幅を device-width に
  • 最初の表示倍率を 1 に

という指定。
f:id:furyu-tei:20180520095805p:plain

適切なレスポンシブ対応(メディアクエリのブレイクポイントの設定)が行われている場合には、この設定で問題は無い。

レスポンシブ対応がなされていない場合

レスポンシブ対応がなされておらず、『とりあえず、デスクトップ用の画面を、モバイルデバイスでも横幅が綺麗に収まる形で表示したい』という場合の設定。
例は、横幅1000pxで設計されているページの例

<meta name="viewport" content="width=1000, user-scalable=no" />

f:id:furyu-tei:20180520095818p:plain
本来ならば、width さえ指定しておけば、ブラウザが拡大率を適切に設定して、スクリーン内に収まるように調整してくれそうなもの。
ではなぜ user-scalable=no を設定しているのかというと、状況によってはこの自動調整がうまくいかないケース(等倍で表示されるケース)があったため
うまく自動調整されない場合の原因は不明。
いずれにしても、user-scalable=no にするとユーザーが拡大縮小できなくなってしまい不便なので、早期にきちんとレスポンシブ対応すべきではある。

レスポンシブ対応が不完全な場合

スマートフォンには対応したのだけれど、ある範囲(例えば横幅768px~1199px)のデバイス(タブレット)だけ表示が見切れる(デスクトップ用に1200px前提でページ設計)という場合の設定。

<meta name="viewport" content="width=device-width, initial-scale=1">

<script type="text/javascript">
(function () {
'use strict';

var minimum_screen_width_for_tablet = 768, // タブレットとみなすデバイス画面の最小論理解像度
    // ※ CSSメディアクエリの指定(例: @media (min-width: 768px) { ... })に合わせること
    viewport_width_for_tablet = 1200, // viewportとして指定する横幅(例えばデスクトップ用として想定されている最小の横幅)
    use_initial_scale = false,
    
    vieport_element = document.querySelector('meta[name="viewport"]'),
    adjust_viewport = function () {
        // TODO: 一部のWindows系モバイルデバイス(IEや旧Edgeを使用している場合等)では meta[name="viewport"] による指定が効かないので別途対応要
        try {
            var device_width = window.screen.width;
            
            if (minimum_screen_width_for_tablet <= device_width) {
                if (use_initial_scale) {
                    vieport_element.setAttribute('content', 'width=' + viewport_width_for_tablet + ', initial-scale=' + (device_width / viewport_width_for_tablet));
                    // 問題点: "width" と "initial-scale" で指定した場合うまく表示されないケースあり(デバイスを回転させた場合等)
                }
                else {
                    vieport_element.setAttribute('content', 'width=' + viewport_width_for_tablet + ', user-scalable=no');
                    // 問題点: "user-scalable=no" によりユーザーによるズーム不可となってしまう
                }
            }
            else {
                vieport_element.setAttribute('content', 'width=device-width, initial-scale=1');
            }
        }
        catch (error) {
            console.error(error);
        }
    };

window.addEventListener('orientationchange', function (event) {
    adjust_viewport();
});

window.addEventListener('resize', function (event) {
    adjust_viewport();
});

adjust_viewport();

})();
</script>

デフォルトとして、meta[name="viewport"]には"width=device-width, initial-scale=1"を指定しておき、JavaScript で window.screen.width によりデバイスのスクリーン幅(≒device-width)を調べ、条件に当てはまる場合だけ書き換えを行う。

上記は簡易的なものだが、
github.com
を使えばもっと細かく調整できる、かも(未確認)。

注意点・問題点など

initial-scale について

上記のとおり、initial-scaleは初期の表示倍率を示すほかに、条件によっては viewport の大きさに影響してしまう。
具体的には、

X = device-width / initial-scale
とするとき、
width < X となる場合には、viewport の幅として X が適用される(widthの指定は無視される)。

模様。
例えばdevice-widthが360のモバイルデバイスの場合に、
"width=640, initial-scale=0.5"
のような指定をすると、実際の viewport の幅としては 720(=360/0.5)が適用されてしまう(width=640の方は無視される)

また、initial-scale に 1 未満の値を指定しても、状況によってはうまく縮小表示されないケースもある。
具体的な条件は不明。ただし、この場合でも、user-scalable=no と併用すれば正しく縮小表示される模様。

ブラウザによってウィンドウサイズ(innerWidth/Height)の決定方法が異なっている?

f:id:furyu-tei:20180520095827p:plain

Google Chromeの場合
innerWidth/innerHeightは、ユーザーがscale(ピンチインで縮小表示)して表示可能な最大領域のピクセル数を示す模様。

この最大領域の横幅は、document.body の大きさと viewport の minimum-scale により、以下のように決まっている模様。

  • document.body の横幅が viewport の以下の場合には、viewport の幅
  • document.body の横幅が viewport より大きい場合、device-width / minimum-scale と document.body の横幅のうち、小さい方

あくまで最大領域に応じて決まるため、実際のユーザーの拡大縮小操作には影響されない。

Firefoxの場合
innerWidth/innerHeightは、ドキュメントのうち、ブラウザの表示領域に実際に表示されているピクセル数を示す模様。
ユーザーの拡大縮小操作に伴って変動する。

【未確認】Safariの場合
実機が無いため、確認できていない。

【未確認】Windows Tablet / Phone について

Windows 系のモバイルデバイスについては、meta[name="viewport"] による指定は効かないため、別途、CSS で @-ms-viewport による指定が必要
実機が無いこともあり、確認が取れていない。