2018-10-01Organizationもう逃げない。content="width=device-width,initial-scale=1">と呪文のように書いてきたが、いい加減ちゃんと整理して理解しよう。調査した結果、以下の考え方で理解できると思う。実際の液晶の解像度は一旦忘れろ。content="width=480">と指定したとする。するとそこに幅480pxの仮想的なウインドウが作られる。幅480pxの液晶モニターがあることをイメージして欲しい。これをviewportと呼ぶ。ブラウザはviewportにレンダリングする。viewportの中では、あたかも本当に480pxのモニターを使っているかのような環境になっている。queryの条件判定でも480pxの画面幅が使われる。次にviewportを実際の液晶画面に表示する。この時、viewportの幅が実際の液晶画面よりも大きければviewportがはみ出して、液晶画面にはviewportの一部しか表示されないだろう。逆に、viewportの幅が液晶画面よりも小さければviewportの内容は液晶画面に収まるが、液晶画面には余白ができてしまうだろう。これを調整するため、ブラウザはviewportのレンダリング結果を液晶の画面サイズに合うようにズームアップまたはダウンして表示する。content="width=device-width">と指定した場合はviewportの幅は端末やブラウザアプリ毎によって異なる。重要なのはここでも実際の液晶の解像度ではなく360とか580などのスマホらしい小さい値が使われるということだ。content="width=device-width,initial-scale=1">を使えばいい。(結局、上の呪文と同じ形に落ち着いた)どちらを使うべきかは状況によるが以下の一長一短があることを覚えておくといい。前者は数字を明確に指定しているので端末ごとの表示を統一できるが、逆に言うと大きな画面サイズの端末でも小さな画面サイズの端末と同じ表示になるため、スペースの有効活用ができないことが有る。後者は端末ごとのサイズに応じてフレキシブルな表示ができる一方、細かい表示の統一はやりづらい。initial-scale=0.5このようにviewport幅が画面幅と同じで、縮小表示する必要がないにも関わらず縮小表示を強要するような設定にした場合、viewportの幅が予期せず大きくなってしまうことが有る。縮小するためにはviewportの幅が液晶画面よりも大きく無ければならないからだ。この例だとviewportの幅はdevice-widthの2倍になってしまう。initial-scale=0.5これも上と同じ。320という、大抵の端末のdevice-widthより小さい値を指定した場合、やはり大抵の機種では縮小の必要がない。縮小を強要するとviewportの幅を無理やり大きくして対応しようとしてしまうのだ。initial-scale=1.0これは上の2つの逆パターン。viewportの幅は液晶画面より小さいのでそのまま表示したのでは液晶画面に余白ができてしまうため、デフォルトでviewportをズームアップするようになっている。暗黙のうちにinitial-scaleが1より大きな値となっているのである。そこに無理やり1.0というズーム率を指定するとどうなるかというと、液晶画面に余白が出来るのではなく、ブラウザはそれを防ぐために勝手にviewportの幅を大きくしてしまうのだ。width=1280,initial-scale=0.5これは一見問題無さそうに見えるが、機種やブラウザによってはviewportが予期せぬ値に書き換えられてしまう。viewportは大きくし、それを縮小して初期状態で液晶画面に収まるようにしたいという狙いで使いたくなるが非常に挙動が不安定。試した限り、リロードする度に$(window).width()の結果が0になったり異常に大きな値になったりと訳の分からないことが起こった。initial-scaleに1より小さな値は使わないのが無難。以下の様なJSを使うとviewportを動的に変更できる。手元のAndroidとiPhoneで試した限りではchromeでもsafariでもちゃんと動作する。レンダリングが終了した後にこれを実行してもちゃんと新しいwidthで再レンダリングしてくれる。'width=360,initial-scale=1');スマホとPCでのデザインの切替はこうする幅が1080pxのスマホが増えて、PCブラウザの画面幅と変わらないくらいの画面幅を持つようになった。なるとメディアクエリーで画面幅によってレイアウトを変更するという芸当だけではダメなのでは?心配いらない。スマホのブラウザはPCのブラウザと違って実際の液晶の解像度ではなく、viewportに設定した画面幅によって支配されている。jQueryで$(window).width()なんてやるとPCでは普通にブラウザウインドウの幅を返すが、スマホではviewportの設定によって返る値が変わる。content="width=480">としてあれば、$(window).width()は480が返す。queryでも使用される。従って、上のようにviewportのwidthを480pxに固定しているなら、480pxをブレークポイントにしたメディアクエリーで表示切り替えすればいい。viewportのwidthにdevice-widthを使用している場合はどうかというと、この場合も前述の様に**(大抵の)スマホではviewportのwidthが(実際の液晶の解像度とは無関係に)360とか580**というスマホらしいサイズになるので問題ない。スマホ判定にwindow.screen.widthを使う少々余談だが、これまで説明したように、スマホでは実際の液晶の解像度とは無関係に色々と事が運ぶ。device-widthの値もそうだし、window.screen.widthの値もそうだ。これを利用するとwindow.screen.widthだけでスマホかどうか判定できる(かもしれない)。window.screen.widthはPCではモニターの解像度(横方向のピクセル数)を返す。スマホでは実際の端末の液晶サイズとは無関係にスマホらしい320とか360とかいう数値を返す。この値は(おそらく)ほとんどの端末でviewportのdevice-widthと同じ値になる。viewportのwidth設定に関わらず常に同じ値を取得できる。これが使えたら、PC並みの解像度を持つ端末が増えても、WindowsPhoneやFirefoxPhoneが増えても、UserAgentで判定するより簡単に判定できて便利だろう。JSだけでスマホ、PCの表示モードを切り替えるこれは少々実験的なテクニックなので、全ての端末で期待通り動くとは限らないが、今までの説明の集大成として紹介する。