【WebAR/VR】端末・バージョンごとのデバイスモーション対応


概要

こんにちは、株式会社palanのxR事業部でWebAR/VRの開発などしていますdamiと申します。

この記事ではスマホ向けWebAR/VRを開発する上で対応が必須となる 【端末のデバイスモーションセンサーの使用の許可をとる】 処理を、デバイスやバージョンに合わせて用意する方法についてまとめます。

こちらの処理、Androidはデフォルトで取得できるため特別な処理は要りませんが、iOSはバージョンによって処理を変える必要があったり…とかなり複雑です。
バージョンごとに丁寧に対応を変えたい場合はぜひ参考にしてみてください。

状況の整理

  • Android: 初期設定で取得できるため特に処理必要なし
  • iOS13以上: タップイベントを検知→「動作と方向を取得するシステムモーダル」を表示→「許可」をしてもらうと取得できる
  • iOS12.2~12.5.5: 設定アプリを開き、設定を変更してもらうことで取得できる
  • iOS12.1以下: Androidと基本同じく初期設定で取得できる

※こちらは2022年4月現在の状況です。
新しいバージョンが登場している場合は、最新情報を別途ご確認ください。

これらを振り分ける処理を書いていきます。

1. 共通処理

Androidを弾く

まずはAndroidは判定必要ないため弾きます
(iOSかどうかを判別します)

ua = navigator.userAgent;

isIos = () => {
  return (
    this.ua.indexOf("iPhone") >= 0 ||
    this.ua.indexOf("iPad") >= 0 ||
    this.ua.indexOf("iPod") >= 0
  );
};

// 必要なところで判定
if (!this.isIos) return;

既にデバイスモーションを許可しているかどうかを判定する

iOSであっても、すでに許可済み or iOS12.1以下(初期設定でモーションの値が取得できる) はこの段階で弾くようにします

// 判定の変数を用意
isDeviceMotionAllowed = false;

// イベント定義 これが発火すれば許可済みである
// ちょっと発火までに時間がかかる場合もあるので時間差に注意
window.addEventListener("deviceorientation", this.deviceMotionEvent, false);

// イベントの中身
deviceMotionEvent = () => {
  // ここまで来れば イベント発火済み、許可済み状態ということになる
  this.isDeviceMotionAllowed = true;
  // イベントもremoveしておく
  window.removeEventListener(
    "deviceorientation",
    this.deviceMotionEvent,
    false
  );
};

2. iOS13以上対応

1の共通処理が終わったら、後はiOSのバージョンごとの振り分け処理をやっていきます。

バージョンの挙動

iOS13では 「端末の『動作と方向』へのアクセス」を求めるシステムモーダルを呼び出し、同意してもらったときのみ取得できます。

振り分けなどの実装

isIos13Over = () => {
  return !/iPhone OS ([1-9]_|1[0-2]_)/.test(this.ua);
}

もしこれがtrue(iOS13以上)であれば、下のようなモーダルを用意して表示させるようにしましょう。


(画像は自社サービスpalanARで使っているモーダルで、A-Frameのdevice-orientation-permission-uiをカスタムしています。)

このモーダルの「許可」をタップしてもらうというタップイベントを取得※ し、その後デバイスモーション&オリエンテーション許可のためのシステムモーダルを呼び出します。
(※iOS13以上では、ユーザーのタップイベントなしにシステムアラートを表示したり、デバイスモーション&オリエンテーションを解放することができないため)

こちらがシステムモーダル。
これの呼び出し&デバイスモーション・オリエン解放方法に関してはこちらの記事が詳しいです。

https://webar-lab.palanar.com/developer/ios13-webar-webvr/

3. iOS12.2~12.5.5対応

バージョンの挙動

ユーザーに、以下のように設定を変更してもらう必要がある。
設定アプリ>Safari>「モーションと画面の向きのアクセス」をONに

振り分けなどの実装

isIos12 = () => {
  return /iPhone OS (12_[2-5])/.test(this.ua);
}

これがtrue(iOS12.2~12.5.5)であれば、次の操作を促すメッセージを表示するなどしましょう。

1. 設定アプリを開く
2. 「Safari」を開く
3. 「モーションと画面の向きのアクセス」をON
4. Safariブラウザに戻りページを再読み込みする

iOS12ではなぜこんなに面倒なのか…などについては、いっこうさんのこちらの記事が詳しいです。


一旦ここまでで振り分けが完了するはずですが、まれにその他の理由でモーションセンサーの値が取得できない場合があります。

その場合は以下が当てはまらないか確認します。

番外編1: iOSでモーションセンサーの値が取得できない場合

iOSのバージョンの問題とはまた別に、モーションセンサーの値が取得できない場合は、Safariが 「デスクトップ用Webサイトを表示」 モードになっている可能性が考えられます。

この「デスクトップ用Webサイトを表示」がオン/オフどちらになっているかは、以下の方法で確認ができます。

設定アプリ>safari>デスクトップ用Webサイトを表示>オフに

(オンになっているとモーションセンサーの値が取得できない)

この状態の場合、UserAgentがiPadではなくMacintoshと出るので、
Macintoshと出るか + touch可能デバイスかの判定で振り分けて注意書きを表示することができます。

ua = navigator.userAgent.toLowerCase();
if (ua.indexOf("macintosh") >= 0 && "ontouchend" in document) {
  alert(
    "【iOS端末をご利用の方へ】\nAR機能をお楽しみいただくには設定を変更する必要があります。\n\n設定アプリ>Safari>デスクトップ用Webサイトを表示>「すべてのWebサイト」のチェックボックスをオフに"
  );
}

番外編2: Androidでモーションセンサーの値が取得できない場合

Androidでモーションセンサーが取得できない場合は以下の2通りの可能性が考えられます。

  1. PC版サイトを表示する設定になっている
  2. モーションセンサーの使用が拒否されている

1. PC版サイトを表示する設定になっている

iOSと同様、PC版サイト表示になっているとモーションセンサーの値が取得できません。
Chromeブラウザを使用している場合は以下の方法で端末の設定を変更する必要があります。

Chromeブラウザ>アドレスバー右の3点アイコン>「PC版サイト」のチェックを外す

2. モーションセンサーの使用が拒否されている

Chromeの設定次第ではモーションセンサーの使用を拒否することもできます。
ここまでで解決しない場合はこちらも確認しましょう。

Chromeブラウザ>アドレスバー右の3点アイコン>設定>サイトの設定>モーションセンサー>許可に

共通処理〜iOSバージョン分け処理を経てもdeviceorientationのイベントが発火しない場合は、この辺りをユーザーに確認してもらうメッセージを表示するのがいいかもしれません。

また、その他の条件をご存知の方がいらっしゃったらぜひ教えてください!