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と言えるのではないだろうか。

コメント
コメントを投稿