2016/09/02 15:30:02

javascriptでデバイスの物理画面サイズを推定する方法

目次(クリックするとジャンプします)
  • 1:物理画面サイズ
  • 1.1:解像度について
  • 1.1.1:ppiとdpiの違い
  • 1.2:物理画面サイズを推定することの難しさ
  • 1.2.1:ピクセルの絶対的な大きさ
  • 1.2.2:CSSを用いる方法では取得できない
  • 1.3:devicePixelRatioの活用と論理ピクセル96dpi
  • 1.3.1:devicePixelRatio
  • 1.3.2:論理ピクセルは96dpi
  • 2:なんとか物理サイズを計算してみる
  • 2.0.1:計算してみた
  • 3:まとめ

物理画面サイズ

昨今ではさまざまなサイズのデバイスがあり、アクセス解析において画面サイズは百花繚乱の体を示しています。 そういった状況もありサイト制作やアプリ制作ではデバイスの物理画面サイズを推定することが重要になってきています。

しかしデバイスの物理画面サイズを取得するのはじつに骨の折れることなのです。

今回の記事ではできるだけ正確にデバイスの物理画面サイズを推定する方法を説明したいと思います。

解像度について

まず単位について少し説明しておきたいです。

解像度を表す単位としてppi(Pixel Par inch)とdpi(Dot Par inch)という単位があります。

いずれも1インチ内にいくつピクセル/ドットがあるかを表します。

ppiとdpiの違い

ピクセルもドットも最小構成画素を意味するが、厳密に言うとドットとピクセルは異なるものです。

本来dpiはプリンタやスキャナで用いられる単位で、プリンタやスキャナは水玉模様の集まりで画像を表現するが、その水玉模様一つがドットということになります。

この場合ドットは■ではなく●なので当然、ドット同士には隙間が生じます。 だから隙間分は期待した解像度にならないということが起きます。 そのため印刷分野ではdpiとppiは別もので捉えないと大変です。

しかしディスプレイの場合、ピクセルとドットを同一視してもほとんど問題がないので混用されているのが現状のようです。

一応デバイス画面に言及する際はppiを使うのが正しいのだと思います。

物理画面サイズを推定することの難しさ

ピクセルの絶対的な大きさ

ピクセル大きさは絶対的ではなく相対的なものです。

競馬場の大きなスクリーンでは1ピクセルは何センチもあるでしょうし、カーナビのスクリーンでは0.1mm単位でしょう。1ピクセルが何ミリということはデバイスによって異なります。だからピクセルを元に物理サイズを推定することはできないです。

物理サイズを算定するにはどうしてもppi/dpiが必要です。1インチに何個ピクセルが入っているかでピクセルの絶対的な大きさが判ます。

しかし現状デバイスのppi/dpiを取得するjavascriptAPIは存在していないです(標準になっていない)。

つまり、ブラウザ上ではデバイスのppi/dpiを得る術は現状ないです。

事前にデバイスの物理サイズを調べておいてデータベース化する?増え続けるデバイスのすべてを網羅するなど気が遠くなる作業です。

CSSを用いる方法では取得できない

dpi/ppiを取得する方法として、1インチのdiv等の要素を用意して、ピクセル値を得ることでdpi/ppiを取得できるとする情報を散見するが、この方法ではデバイス本来のdpi/ppiは取得できないです。

なぜならCSS上では論理dpi/ppiが96dpiで定義されているからです。だからどのデバイスで1インチの要素のピクセルを取得しても「96」が返ってくるはずです。

devicePixelRatioの活用と論理ピクセル96dpi

devicePixelRatio

ピクセル比を表すdevicePixelRatioというプロパティが存在しています。

appleがRetinaDisplayを導入する際に、デバイスピクセルと論理ピクセルの対応を調整する為に提唱したプロパティだそうですが、他のブラウザベンダーも追従実装したのでモダンブラウザでは取得できるはずです。

このプロパティが物理サイズ推定に対して重要になってきます。

このプロパティの実装は最近なので、残念ながらIE9以下では取得ができないですが、問題はないとは思います。

というのも、devicePixelRatioが1以上の場合、そのディスプレイは最近のものであり(少なくともIE8やら9やらの時代じゃない)そのディスプレイを使っている人がレガシーブラウザを使っているとは考えにくいです。

さらにタブレットやスマートフォンではIE9以前が動いている心配をしなくていいです。

取得できなければ1にしてしまえばいいとも言えます。

論理ピクセルは96dpi

先ほどもCSS上では96dpiと定義されていると説明したが、この論理dpiは物理サイズ推定の大きなカギです。仮の基準としての役割を果たします。

なんとか物理サイズを計算してみる

いろいろあがいてみたが、やはり完全に正確に取得することはできないです。ただそれなりにいい線いっている感じの値は導くことができました。 javascriptだが計算式は以下です。

function windowWidthHeight(){
    var ratio;
    window.devicePixelRatio ? ratio = window.devicePixelRatio : ratio = 1 ;
    return{ 
        windowWidth : window.innerWidth , 
        windowHeight : window.innerHeight,
        devicePixelRatio : ratio
    };
}
function deviceDpi(){
    var POINTDPI = 96 ,
        ratio = windowWidthHeight().devicePixelRatio ,
        width = windowWidthHeight().windowWidth ,
        coefficient ,
        logicalDpi ,
        estimatedActualDpi = logicalDpi * ratio;
    ratio < 2  ? coefficient = -ratio : coefficient = ratio ,
    logicalDpi = ( devicePixelRatio === 1 ) ? 
        ( POINTDPI + Math.sqrt( Math.sqrt( windowWidthHeight().windowWidth ) ) * coefficient ) :
        ( POINTDPI + ( POINTDPI / ratio ) + Math.sqrt( Math.sqrt( width ) ) * coefficient ) ;
    return { 
        logicalDpi : logicalDpi,
        estimatedActualDpi : estimatedActualDpi   
    };
}
function deviceInchSize(){
    var dpi = deviceDpi().logicalDpi ,
        width = windowWidthHeight().windowWidth ,
        height = windowWidthHeight().windowHeight ;
        widthInch = width / dpi ,
        heightInch = height / dpi ,
        diagonalInch = Math.sqrt( Math.pow( widthInch , 2 ) + Math.pow( heightInch , 2 ) );
    return {
        widthInch : widthInch ,
        heightInch : heightInch ,
        diagonalInch : diagonalInch
    }
}

@MINOのコーディングの下手さには目を瞑ってもらうとして、ざっと読んでもらえれば何をやっているかはわかっていただけるかと思います。

計算根拠は希薄です。しいて言うならとにかくたくさん試行していたら一番近くなったのがこの計算方法だったからというのが計算根拠です。

 ratio < 2  ? coefficient = -ratio : coefficient = ratio ,

devicePixelRatioが2以下の場合、マイナスになるようにしているのは試行の結果であり、何か根拠があるわけではないです。そうした方がより正確になったからです。

logicalDpi = ( devicePixelRatio === 1 ) ? 
        ( POINTDPI + Math.sqrt( Math.sqrt( windowWidthHeight().windowWidth ) ) * coefficient ) :
        ( POINTDPI + ( POINTDPI / ratio ) + Math.sqrt( Math.sqrt( width ) ) * coefficient ) ;

この式で算出されるのは、デバイスのdpiではなく、devicePixelRatioが1だった場合のdpiであることに注意してほしいです。

iphone6だとしたら実際326ppiですが、この計算で大体半分の152ppiが算出されるはずです。iphone6がRetinaDisplayじゃなかったら約150ppiのデバイスなんだなということです。

 ( POINTDPI + Math.sqrt( Math.sqrt( windowWidthHeight().windowWidth ) ) * ratio ) :

widthの平方根の平方根を算出し、devicePixelRatioを掛けているあたりが苦し紛れもいいところですが、微調整にはなっています。

windowWidth : window.innerWidth , 
windowHeight : window.innerHeight,

window.innerWidthで取得できる幅はviewpointで指定されている論理上の解像度です。つまりさっき算出した「devicePixelRatioが1だった場合のdpi」だとしたらという仮定で取得できるwidthがこれになるのです。

iphone6だと、Width:375,height:667で取得できると思いますが、これは実際のデバイスピクセル数である750x1334をdevicePixelRatioで割った数字だということがお分かりいただけるでしょう。

iphone6は論理上のピクセル1つを4つのデバイスピクセルで描画しているということになります。(そりゃきれいな訳だ)

計算してみた

この計算式でいくつかのデバイスを計算してみましました。(もっと網羅するべきだろうが、ちょっと面倒になりました。実機無いしね)

デバイス ポイント レシオ 算出インチ 公式インチ
iphone5 320×568 2 4.28inch 4.5inch
iphone6 375×667 2 5.01inch 4.7inch
iphone6puls 414×768 3 6.16inch 5.5inch
XPERIA Z3 360×640 3 5.21inch 5.2inch
nexus7(2012) 601×962 1.325 6.94inch 7inch
@MINOのノートパソコン 1366×768 1 15.27inch 15inch

画面サイズを表すインチは対角線の長さです。もうアメリカしかヤードポンド法を使っていないのに、亡霊のようにインチが残ることに辟易しますが、しょうがないです。

誤差は10%くらいありますが、@MINOの頭ではこのくらいが限度なので勘弁してほしいです。汎用的にデバイスのサイズを推定する役目としては十分な精度かなとは思うがどうでしょう。

とりあえず実際の物理画面サイズに近い数値を取得できるようにはなると思います。

まとめ

物理画面サイズが推定できると、いろいろな判断に使えます。 もちろん目的の大半はレスポンシブなサイト制作の為です。冒頭でも述べたとおり、最近ではデバイスに最適化されたサイト表示が求められています。

@MINOはこれでデバイスの種類判別を行っています。もちろん画面サイズだけでは判別できないので、OSやユーザーエージェントなどを合わせて判断するが、物理サイズが判ると判別はより正確になります。

タブレットなのかPCなのか解像度を確認しただけで解からないことも多くなってきたし、面倒なことこの上ないですが、サイト制作の宿命か。