Try .NET Core

.NET Coreを動かした、試した記録を書き残します。

音楽サーバ"Mopidy"のフロントエンドを作る:10 デバイス別の表示調整

音楽サーバ"Mopidy"のフロントエンド「Mopidy.Finder」が出来るまで、第10回です。

今回はBootstrap4をベースにした、デバイスごとに表示を最適化するフローを追います。

どんなふうにする?

まず、PCブラウザサイズ。
私が最も使うはずのサイズです。
なるべく全機能を表示しておき、マウスで即座に各機能にアクセスしたいです。
f:id:try_dot_net_core:20190811095629p:plain
Icons made by Freepik from www.flaticon.com is licensed by CC 3.0 BY

次に、タブレットサイズ。
それなりのモニタサイズがありますが、常時メニューを表示しておくには小さすぎます。
サイドバーは、ハンバーガーメニューの開閉式にしておきましょう。

そして、タブレットを横にしたときは、カラムが並ぶように。
縦にしたときは、各カラムがフルスクリーン表示されるようにしたいです。
f:id:try_dot_net_core:20190811101001p:plain Icons made by Dave Gandy www.flaticon.com is licensed by CC 3.0 BY

そして、スマートフォンサイズ。
モニタサイズは小さいため、縦横ともにフルスクリーンで表示しておきたいです。
f:id:try_dot_net_core:20190811095705p:plain Icons made by Freepik from www.flaticon.com is licensed by CC 3.0 BY

モニタサイズを判定する

Bootstrapではマークアップ時に`lg'や'md'のようなサイズ閾値を織り交ぜることで、自動的にカラム表示/フルスクリーン表示を切り替えて貰えますよね。

しかし、「今現在、どのサイズ閾値を使っているのか」を取得する方法が、プレーンなBootstrapの機能の中では見つけることが出来ませんでした。

そこで、前回記事に挙げたResponsive Bootstrap Toolkitを導入して、モニタサイズがBootstrap上のどれに当たるのか、を取得するようにしました。

しかし前回記事で少し触れたように、Responsive Bootstrap ToolkitはまだBootstrap4には対応していません。

ぐぐってみると、ご本家GitHubのissueで対応方法が議論されていました。
github.com

ほうほう、useメソッドで閾値定義を渡せばよい、と。

ということで、このissueに記載があった定義を、そのままコピペで持ってきました。
src/ts/Libraries.ts:

Libraries.ResponsiveBootstrapToolkit.use('bs4', {
    'xs': Libraries.$('<div class="d-xs-block d-sm-none d-md-none d-lg-none d-xl-none"></div>'),
    'sm': Libraries.$('<div class="d-none d-sm-block d-md-none d-lg-none d-xl-none"></div>'),
    'md': Libraries.$('<div class="d-none d-md-block d-sm-none d-lg-none d-xl-none"></div>'),
    'lg': Libraries.$('<div class="d-none d-lg-block d-sm-none d-md-none d-xl-none"></div>'),
    'xl': Libraries.$('<div class="d-none d-xl-block d-sm-none d-md-none d-lg-none"></div>')
});

カラム/フルスクリーン切替の閾値をきめる

Bootstrap4のサイズは、公式ドキュメントでは下記のとおりです。

  • xs: 幅576px未満
  • sm: 幅576px以上 - 768px未満
  • md: 幅768px以上 - 992px未満
  • lg: 幅992px以上 - 1200px未満
  • xl: 幅1200px以上

当初はmdサイズを基準に切り替えていたのですが、これではタブレットを水平にしてもカラム表示になりませんでした。

htmlのmetaタグでviewportを指定しているため、デバイス本来の解像度よりも小さめに判定されるためです。

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

ということで、切替閾値lgに変更しました。

閾値に合わせた表示切替ロジック

Mopidy.Finderでは、ディスプレイサイズに合わせた表示を、NavigationControllerで制御するようにしました。
コンストラクタから呼び出すInitialNavigationメソッドの中で、表示制御メソッドを実行しています。
src/ts/Controllers/NavigationController.ts: L63

private async InitialNavigation(): Promise<boolean> {
    await this._store.TryConnect();
    const updateProgress = await this._store.GetDbUpdateProgress();

    // どうも、ResponsiveToolkitの初期化後から反応が正しくなるまで
    // すこし時間がかかるっぽい。
    // Settingsクエリが終わるまで待ってからAdjustする。
    this.AdjustScreen();
    // -- 中略 --
}

Responsive Bootstrap Toolkitは、各閾値ごとに定義した要素の表示状態を見ることで現在のサイズを取得しているようです。
なので、初回描画が終わるまで、API呼び出しを挟むことで少し待たせています。

そして、表示制御メソッドの中身はこんなかんじ。 src/ts/Controllers/NavigationController.ts: L95

private AdjustScreen(): void {
    // コンテンツは、mdサイズを基点にカラム<-->フルスクリーンを切り替える。
    if (this._viewport.is('<=md')) {
        this._content.ContentToFullscreen();
    } else {
        this._content.ContentToColumn();
    }

    // サイドバーは、lgサイズを基点に常時表示<-->操作終了で非表示化を切り替える。
    if (this._viewport.is('<=lg')) {
        this._headerBar.SetSideBarClose();
    } else {
        this._headerBar.SetSideBarOpen();
    }
    // -- 中略 --
}

this._viewportは、Responsive Bootstrap Toolkitインスタンスをセットしています。
isメソッドで比較演算子が使えるのは、便利ですね!

やれやれ一安心...と思いきや

閾値lgにセットして、手持ちのiPad mini4 と iPhone6+と で表示を確認しました。
まず、iPad mini4 f:id:try_dot_net_core:20190811112909p:plain よしよし、と。

次に、iPhone6+。
f:id:try_dot_net_core:20190811113517p:plain ボケボケでなんですが、これも表示は想定どおりです。

最後に、いま私が普段使いしている、MediaPad M5で試します。
f:id:try_dot_net_core:20190811114030p:plain
あかーん!
横にしたとき、カラム表示になっていません...。

MediaPadのディスプレイサイズをみてみる

MediaPad M5のChromeにデバッガツールをアタッチしてみると。
f:id:try_dot_net_core:20190811114821p:plain スクリーンサイズの幅は、「963.137px」と出ています。
なるほど、lgサイズの992pxより、小さいわけですね...。

ここで少し悩みました。
ある程度は汎用的に環境を作り込んでくれているであろうBootstrap様のサイズ規定に、はたして手を入れるべきか?

しかし。
このタブレットは、決して珍しい種類のものではありません。
他のAndroidタブレットでも、同じ現象が出るかもしれません。

なにより。
このアプリは、主に私が使うもの。
オレオレ環境に合わせて、何が悪い!

と、3秒くらい葛藤した上で、BootstrapのCSSに手を入れることに、決定しました。

閾値サイズに手を入れる

BootstrapのCSSは、それはそれは巨大な記述の塊です。
しかし、サイズを規定している箇所の記述は、だいたいこんな感じです。

@media (min-width: 768px) { ...

この、@mediaで始まっているもののうち、lgサイズに該当する 992px の値を変えてしまえば、終わりです。

992pxを示す@media (min-width: 992px)が、11箇所。
そして992px未満を示す@media (max-width: 991.98px)の記述が、2箇所ありました。

これをそれぞれ、下のように書き換えました。

  • @media (min-width: 992px)@media (min-width: 930px)
  • @media (max-width: 991.98px)@media (max-width: 929.98px)

すこしマージンを取り、閾値を930pxとしました。

そして、MediaPad M5で表示を見てみます。
f:id:try_dot_net_core:20190811121209p:plain

おっしゃ!
狙いどおりです。

iPad mini4でも、表示は変わっていませんでした。
f:id:try_dot_net_core:20190811122138p:plain

まあ、当然ちゃ当然です。

その他、iOS機と各種Android機で表示を試し、狙い通りかを確認します。
f:id:try_dot_net_core:20190811123921p:plain

うんうん、ちゃんと見えてますね。

以上、デバイスに合わせた表示調整のおはなし、でした!

追記

ちょうどこの記事をかいた8月11日、こんなエントリがはてブに挙がってました。
hashimotosan.hatenablog.jp なるほど、560px/960pxの二つ、か。
これはシンプルでいいですね。