ブラウザから加速度センサーの値を見ることができるDeviceMotionEventを使う時に値が取得されなかったのでその解決方法です。
スマホサイトでdevicemotionイベントによって加速度をを表示しようとしたらイベントが発生しませんでした。
ページ自体はReactで作っていて下のような感じです。このまま開発PCと同一ネットワーク内のスマホで192.168.100.17:3000
にアクセスした時accelerationX, accelerationY, accelerationZ
の中身が初期値の0のまま更新されませんでした。
import React, { useEffect, useState } from 'react';
function App() {
const [accelerationX, setAccelerationX] = useState<number>(0);
const [accelerationY, setAccelerationY] = useState<number>(0);
const [accelerationZ, setAccelerationZ] = useState<number>(0);
useEffect(() => {
window.addEventListener("devicemotion", (event: any) => {
setAccelerationX(event.acceleration.x)
setAccelerationY(event.acceleration.y)
setAccelerationZ(event.acceleration.z)
})
})
return (
<div>
<span>{accelerationX}</span>
<span>{accelerationY}</span>
<span>{accelerationZ}</span>
</div>
)
}
export default App;
まず使っているスマホで加速度表示サイトにアクセスして値が動くことを確認します。恐らく最近のスマホでは動かないことは無いと思いますが、動かなかったらそのブラウザは対応していません。最新版のGoogle Chromeをインストールしましょう。
そして次にHTTPSを使いましょう。HTTPS=true npm start
してからhttps://192.168.100.17:3000
にアクセスすると無事に表示されました。
なおこのことはW3Cにも書いてあります。
fire events only on secure browsing contexts [SECURE-CONTEXTS] ref: https://www.w3.org/TR/orientation-event/#security-and-privacy
localhostくらい許してくれよとは思います。
iPhoneの場合は素直にいってくれません。iOSのバージョンや使用するブラウザによっていろいろ変わってきます。未だに仕様策定中というのもあって動くサンプルが全然無くてちょっと厳しいです。
iOS 13からブラウザからセンサーにアクセスする場合はDeviceMotionEvent.requestPermission()
関数を使って明示的に許可操作をしなければいけません。
また、罠としてDeviceMotionEvent.acceleration
がnullになって取得できないというものがありました。代わりに加速度を加味したDeviceMotionEvent.accelerationIncludingGravity
を使用します。
実際のコードは以下のような様子となります。
import React, { useState } from 'react';
function App() {
const [accelerationX, setAccelerationX] = useState<number>(0);
const [accelerationY, setAccelerationY] = useState<number>(0);
const [accelerationZ, setAccelerationZ] = useState<number>(0);
const deviceMotionRequest = () => {
if (DeviceMotionEvent.requestPermission) {
DeviceMotionEvent.requestPermission()
.then(permissionState => {
if (permissionState === 'granted') {
window.addEventListener("devicemotion", (event) => {
if (!event.accelerationIncludingGravity) {
alert('event.accelerationIncludingGravity is null');
return;
}
setAccelerationX(event.accelerationIncludingGravity.x)
setAccelerationY(event.accelerationIncludingGravity.y)
setAccelerationZ(event.accelerationIncludingGravity.z)
})
}
})
.catch(console.error);
} else {
alert('DeviceMotionEvent.requestPermission is not found')
}
}
return (
<div>
<button onClick={deviceMotionRequest} />
<span>{accelerationX}</span>
<span>{accelerationY}</span>
<span>{accelerationZ}</span>
</div>
)
}
export default App;
ボタンをクリックしたらアクセスしていいか尋ねるダイアログボックスが出てきて許可すると値が表示されるようになります。
上のコード片はReactですが需要ありそうなので<script>
タグに直接書くタイプのコードも置いておきます。動作未検証で雰囲気で書いてます。
<html>
<head>
<title>DeviceMotion Test</title>
</head>
<body>
<div>
<button onClick="deviceMotionRequest()">Click</button>
<span id="x">0</span>
<span id="y">0</span>
<span id="z">0</span>
</div>
<script>
function deviceMotionRequest () {
if (DeviceMotionEvent.requestPermission) {
DeviceMotionEvent.requestPermission()
.then(permissionState => {
if (permissionState === 'granted') {
window.addEventListener("devicemotion", function (event) {
if (!event.accelerationIncludingGravity) {
alert('event.accelerationIncludingGravity is null');
return;
}
document.getElementById('x').innerHTML = event.accelerationIncludingGravity.x;
document.getElementById('y').innerHTML = event.accelerationIncludingGravity.y;
document.getElementById('z').innerHTML = event.accelerationIncludingGravity.z;
})
}
})
.catch(console.error);
} else {
alert('DeviceMotionEvent.requestPermission is not found')
}
}
</script>
</body>
</html>