iPhoneXかどうかをJavaScriptで判断するには?
まずUserAgentで判別できないかと考えた。しかし、
という感じなので無理。
cordova-plugin-deviceでもダメ。
で、世間ではどのような議論が行われているかというと、
ios - Detect if the device is iPhone X - Stack Overflow
画面の幅高さで判別しろとか、ネイティブ側で
的なことをしろとか、safe-area-inset-*があるかどうかで判別しろとか、そういった議論が行われている。
今回はJavaScriptで判別する必要があるので、ネイティブ側から情報を取得する方法は使えない。
画面の幅高さで判別する方法は、現状では問題無いだろうが、端末ごとに幅高さが変更される可能性があることを考えると、あまり上手い方法とは言えない。
safe-area-inset-*があるかどうかで判別するのは有効そうだ。safe-area-inset-*を持つということは「ノッチ」がある端末ということなので、iPhoneXに限らずEssential Phoneなども検出できるだろう。
問題はJSからどのようにsafe-area-inset-*を取得するかだ。
WEBページをiPhoneXに対応させる方法として、CSSで「constant(safe-area-insets-*)」を使うということはわかっているが、これをJSで取得するにはどうするのか?
window.screenやdocument.documentElement.styleのプロパティーをくまなく見てみたが、safe-area-inset-*を取れそうなプロパティーは存在しなかった。
そこで、safe-area-inset-*のpaddingを持つダミーエレメントをテンポラリにDOMツリーに追加し、値を取得した後すぐに削除するというdirtyな実装をした。
もちろん、isSafeAreaDisplay()だけではEssential Phoneなどの可能性もあるので、iPhoneXかどうかを判別するにはやはり幅と高さも考慮する必要があるが、「ページをノッチがある端末に対応させる」という目的なら、isSafeAreaDisplay()だけでも事足りるだろう。
iPhoneXかどうかを判断するには、ここまでやる必要がある。
少々微妙な感じではあるけれど、ここまでチェックすれば、まぁiPhoneXと言えるのではないだろうか。
mozilla/5.0 (iphone; cpu iphone os 11_1 like mac os x) applewebkit/604.3.5 (khtml, like gecko) mobile/15b93
という感じなので無理。
cordova-plugin-deviceでもダメ。
で、世間ではどのような議論が行われているかというと、
ios - Detect if the device is iPhone X - Stack Overflow
画面の幅高さで判別しろとか、ネイティブ側で
struct utsname systemInfo; uname(&systemInfo);
的なことをしろとか、safe-area-inset-*があるかどうかで判別しろとか、そういった議論が行われている。
今回はJavaScriptで判別する必要があるので、ネイティブ側から情報を取得する方法は使えない。
画面の幅高さで判別する方法は、現状では問題無いだろうが、端末ごとに幅高さが変更される可能性があることを考えると、あまり上手い方法とは言えない。
safe-area-inset-*があるかどうかで判別するのは有効そうだ。safe-area-inset-*を持つということは「ノッチ」がある端末ということなので、iPhoneXに限らずEssential Phoneなども検出できるだろう。
問題はJSからどのようにsafe-area-inset-*を取得するかだ。
WEBページをiPhoneXに対応させる方法として、CSSで「constant(safe-area-insets-*)」を使うということはわかっているが、これをJSで取得するにはどうするのか?
window.screenやdocument.documentElement.styleのプロパティーをくまなく見てみたが、safe-area-inset-*を取れそうなプロパティーは存在しなかった。
そこで、safe-area-inset-*のpaddingを持つダミーエレメントをテンポラリにDOMツリーに追加し、値を取得した後すぐに削除するというdirtyな実装をした。
/** * セーフエリアがあるディスプレイかどうかを返す */ function isSafeAreaDisplay() { var flag = false; //constant()はiOS 11.2で削除されるのでenv()へのフォールバックが必要 //Designing Websites for iPhone X | WebKit //https://webkit.org/blog/7929/designing-websites-for-iphone-x/ var isSafeAreaConst = CSS.supports('padding-left: constant(safe-area-inset-left)'); var isSafeAreaEnv = CSS.supports('padding-left: env(safe-area-inset-left)'); //どちらかがサポートされているなら実際の値を計算 if (isSafeAreaConst || isSafeAreaEnv) { //safe-area-inset-*をダミーDIVのpaddingに設定する var elmDiv = document.createElement('div'); if (isSafeAreaConst) { //iOS 11.1 elmDiv.style.paddingTop = 'constant(safe-area-inset-top)'; elmDiv.style.paddingLeft = 'constant(safe-area-inset-left)'; elmDiv.style.paddingRight = 'constant(safe-area-inset-right)'; elmDiv.style.paddingBottom = 'constant(safe-area-inset-bottom)'; } else { //iOS 11.2 elmDiv.style.paddingTop = 'env(safe-area-inset-top)'; elmDiv.style.paddingLeft = 'env(safe-area-inset-left)'; elmDiv.style.paddingRight = 'env(safe-area-inset-right)'; elmDiv.style.paddingBottom = 'env(safe-area-inset-bottom)'; } //非表示にしてドキュメントに追加 elmDiv.style.display = 'none'; document.body.appendChild(elmDiv); //計算後のsafe-area-inset-*を取得 var elmDivStyle = window.getComputedStyle(elmDiv); var safeAreaInsetTop = parseInt(elmDivStyle.paddingTop); var safeAreaInsetLeft = parseInt(elmDivStyle.paddingLeft); var safeAreaInsetRight = parseInt(elmDivStyle.paddingRight); var safeAreaInsetBottom = parseInt(elmDivStyle.paddingBottom); console.log( 'safeAreaInsetTop=' + safeAreaInsetTop, 'safeAreaInsetLeft=' + safeAreaInsetLeft, 'safeAreaInsetRight=' + safeAreaInsetRight, 'safeAreaInsetBottom=' + safeAreaInsetBottom ); //セーフエリアディスプレイか判断 if (window.navigator.userAgent.indexOf('iPhone') != -1) { //iOSの場合、iPhoneXでない端末の場合でもステータスバーの20pxがinsetに設定されるので、このケースを除外する必要がある var statusBarH = 20; //デバイスの回転によってどこにinsetが付くのかわからないので、4辺のどこかにinsetが設定されればセーフエリアディスプレイだとみなす if (safeAreaInsetTop > statusBarH || safeAreaInsetLeft > statusBarH || safeAreaInsetRight > statusBarH || safeAreaInsetBottom > statusBarH) { flag = true; } } else { if (safeAreaInsetTop > 0 || safeAreaInsetLeft > 0 || safeAreaInsetRight > 0 || safeAreaInsetBottom > 0) { flag = true; } } //ドキュメントから削除 document.body.removeChild(elmDiv); } //デバッグログ console.log('isSafeAreaDisplay=' + flag, 'isSafeAreaConst=' + isSafeAreaConst, 'isSafeAreaEnv=' + isSafeAreaEnv); return flag; }
もちろん、isSafeAreaDisplay()だけではEssential Phoneなどの可能性もあるので、iPhoneXかどうかを判別するにはやはり幅と高さも考慮する必要があるが、「ページをノッチがある端末に対応させる」という目的なら、isSafeAreaDisplay()だけでも事足りるだろう。
iPhoneXかどうかを判断するには、ここまでやる必要がある。
/** * iPhoneXかどうかを返す */ function isIPhoneX() { //iOSかどうか? var isIOS = false; if (window.navigator.userAgent.indexOf('iPhone') != -1) { isIOS = true; } //幅と高さはiPhoneXサイズか? //デバイスのピクセルサイズは(1125,2436)だが、 //JSから見た幅と高さは3分の1の(375,812)であることに注意 //Adaptivity and Layout - Visual Design - iOS Human Interface Guidelines //https://developer.apple.com/ios/human-interface-guidelines/visual-design/adaptivity-and-layout/ var isDimensionOK = false; if (window.screen.width == 375 && window.screen.height == 812) { isDimensionOK = true; } //デバッグログ console.log('isIOS=' + isIOS, 'isDimensionOK=' + isDimensionOK, 'isSafeAreaDisplay=' + isSafeAreaDisplay()); return isIOS && isDimensionOK && isSafeAreaDisplay(); }
少々微妙な感じではあるけれど、ここまでチェックすれば、まぁiPhoneXと言えるのではないだろうか。
コメント
コメントを投稿