イナヅマTVログ

[JavaScript] スマホ・タブレットのportrait / landscapeを調べたい

| 0件のコメント

レスポンシブしてますか?
ネット上の「レスポンシブが簡単に実装できる」ネタでまともなものが一つもなく、ゴミみたいな情報をありがたがる奴が多すぎておじさんとしては違和感を覚える今日この頃です。
いや、言い過ぎた。
ネット上の記事を全て見てるわけではないので正しく導いてくれるステキな記事はあるはずです。

まぁこの記事もゴミの一つですけど…

今回は間接的にレスポンシブに関する話題 iOS, Android ブラウザでの portrait / landscape です。
以前に同じテーマの記事を投稿していて今読み返すと恥ずかしくて直視できない内容で本当に申し訳なく思います。

スマホ(タブレットもだけど)のportrait / landscapeを調べたい

コンテンツ全体が成り行き(%指定)で仕上がっていればJavaScriptをワザワザ登場させるまでもありません。
しかし実コンテンツではなかなかそうも行かないことがあります。
iframe, 動画(video)とかCanvas(canvas)などサイズ(px)を与えないといけないタグを使用すると「%指定だったら大丈夫計画」は破綻します。

向き(portrait / landscape)でサイズを指定し直す必要があります。
画面が小さい方(portrait)でサイズを固定しlandscapeの時は余白がでても「いいや」って諦めてもいいけどなかなかの無様な結果になりそう。

Media Queriesの限界

Media Queries max-widthを使うと対応できそう。

@media screen and (max-width: 480px) {
    iframe {
        width: 480px;
        height: 270px;
    }
}
@media screen and (max-width: 320px) {
    iframe {
        width: 320px;
        height: 180px;
    }
}

ただ、Android端末の解像度種類があまりに多く「全部設定できる?」問題が発生します。
がんばってコツコツ設定できたとしても、未知の解像度が登場する度にメンテナンスするのか、メンテナンス費用は誰が負担するのかと新たな問題も起きます。
iPhone,iPadも安心していられません。
新機種の解像度がどうなるのか予測できません。

発注元は無償対応が当たり前と考えることが多そうですが、制作側の1人として個人的にはタダ働きは御免被りたいものです。

JavaScriptで(portrait / landscape)判定

こんなことにJavaScriptを使うのは罪悪感もあるのですが未知の解像度端末にも対応できそうです。

Event

賢人の方々はすでにご承知のことですがJavaScriptでEventは重要です。
トリガーは常にEventになります。

portrait / landscape判定のEventにつかえそうなのは次の2つのようです。
onorientationchange
onresize

var event_type = "onorientationchange" in window ? "orientationchange" : "resize";
 
function onOrientationChange () {
    // 変わった
}
 
window.addEventListener( event_type, onOrientationChange, false );

onorientationchange を使えるのが一番なのですが使えない端末は onresize を使います。
onorientationchange が使えない古い端末は残念ながら存在します。
余談ですがIEのことを考慮しaddEventListener, attachEventを使い分けたりevent type の”on”有る無しを気にしなくてすむのが唯一の救いです。

portrait / landscape 判定

イベントハンドラ onOrientationChange の中で判定を行います。

【onresize イベント】
画面サイズを計測し縦横どちらが大きいかで判定します。

function isPortraitBySize () {
    var w = parseInt( 
            window.innerWidth || 
            document.documentElement.clientWidth || 
            document.body.clientWidth, 10 ),
        h = parseInt( 
            window.innerHeight || 
            document.documentElement.clientHeight || 
            document.body.clientHeight, 10 );
 
    return h > w;
}

【onorientationchange イベント】
window.matchMedia が使える場合はこちらを優先します。
使えない場合は window.orientation の角度情報を使用します。

// use matchMedia
function isPortraitByMatchMedia (argument) {
    return window.matchMedia( "(orientation: portrait)" ).matches;
}
 
// use orientation
function isPortraitByOrientation (argument) {
    return Math.abs( window.orientation ) !== 90;
}

*一部のAndroidタブレットではwindow.orientation角度情報を一般的に使用されているものと逆の情報を返すものが存在します。

Math.abs( window.orientation ) === 90
が portrait になります。
window.orientationでのチェックは危険が伴います。

【onOrientationChange】

function onOrientationChange () {
    if ( !"onorientationchange" in window ) {
        if ( isPortraitBySize() ) {
            // portrait
        } else {
            // landscape
        }
    } else {
        if ( typeof window.matchMedia !== "undefined") {
            if ( isPortraitByMatchMedia() ) {
                // portrait
            } else {
                // landscape
            }
        } else if ( "orientation" in window ) {
            if ( isPortraitByOrientation() ) {
                // portrait
            } else {
                // landscape
            }
        }
    }
}

最適化余地はありますがこんな感じでしょうか。

window.matchMedia でも Event をとれるのですが一部の Android 端末で期待通りに動作しなかったので割愛しました。
update 2015-04-13
MediaQueryList を使えば良かったようです。

MediaQueryList

MDN: Testing media queries
MDN: MediaQueryList

function onOrientationChange ( mql ) {
if ( mql.matches ) {
    // portrait
  } else {
    // landscape
  }
}
 
var mql = window.matchMedia( "(orientation: portrait)" );
mql.addListener( onOrientationChange );
onOrientationChange( mql );

コメントを残す