🐡

Webのジャイロ機能で遊んでみた

2024/10/29に公開

はじめに

大阪で学生をしているコウダイというものです。
普段は、Webのことを勉強していてフロントエンドエンジニアを目指して日々頑張っています。

この記事では、iPhoneのブラウザでジャイロセンサーを活用したWebアプリの開発に興味があり、自分なりにやってみたのでそれを紹介します。
https://github.com/koudaihirata

⚠️注意

ブラウザのジャイロセンサーを使用するために何点かの注意事項があります。

1. httpsでしか動かない理由

セキュリティプライバシーの保護

詳しく説明すると、デバイスのセンサー(ジャイロ、加速度、位置情報など)にアクセスする機能は、ユーザーのプライバシーに関連する非常に機密性の高い情報として扱われる

これにより、悪意のあるウェブサイトが無断でこれらのセンサー情報を利用されるリスクが生じる可能性があるので、ユーザーのプライバシーと安全を保護するために必要
https://www.w3.org/2020/09/web-roadmaps/mobile/sensors.html

2. ユーザーに許可を求めなければならない理由

セキュリティの強化

ios13以降では、ジャイロデータを取得するにはセキュリティの強化の面からユーザーからの明示的な許可が必要

実装

今回は、HTMLJavaScriptを使い、GitHub Pages上でスマホを傾けるだけでページが移動できるジャイロ機能を実装してみました。

こちらから実際のサイトに飛べます
※スマホでしか動作しません

まずは、HTML

HTMLのコード
index.html
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>ジャイロセンサー</title>
</head>
<body>
  <h1>ジャイロセンサーのデータを取得</h1>
  // ユーザーに許可を求めるボタン
  <button id="requestPermissionBtn">ジャイロアクセスを許可</button>
  // ジャイロセンサーの数値を表示する場所
  <div id="txt">ここにデータを表示</div>
 // ジャイロセンサーの数値を元に分かりやすいようにコンパスのようなものを表示する場所
  <canvas id="canvas" width="300" height="400"></canvas>


  <script src="./js/script.js"></script>
</body>
</html>

ジャイロで右左に振った時にページ移動する先のHTMLたち

left.html
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>左のページ</title>
</head>
<body>
    <h1>あなた左に傾けましたね</h1>
    
    <button><a href="index.html">戻る</a></button>
    
</html>
right.html
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>右のページ</title>
</head>
<body>
    <h1>あなた右に傾けましたね</h1>

    <button><a href="index.html">戻る</a></button>
    
</body>
</html>

次にジャイロ機能を実装したJavaScriptのコード

script.js
const alphaElement = document.getElementById('alpha');
const betaElement = document.getElementById('beta');
const gammaElement = document.getElementById('gamma');
const requestPermissionBtn = document.getElementById('requestPermissionBtn');
let alpha = 0, beta = 0, gamma = 0; 
let canvas = document.getElementById("canvas"); // ★canvas要素を取得 
let context = canvas.getContext("2d");          // ★絵を描く部品を取得



// ローカルストレージから許可状態を確認
const gyroPermissionGranted = localStorage.getItem('gyroPermissionGranted');

// ジャイロセンサーのデータを取得する関数
  window.addEventListener('deviceorientation', (event) => {
    alpha = event.alpha;
    beta = event.beta;
    gamma = event.gamma;

    // Alpha の値が 300 から 320 になったら右ページを移動
    if (alpha < 320 && alpha > 300) {
      window.location.href = 'right.html';
    }
    // Alpha の値が 40 から 60 になったら左ページを移動
    if (alpha > 40 && alpha < 60) {
      window.location.href = 'left.html';
    }
  });

let timer = window.setInterval(() => {
    displayData();      // displayData 関数を実行
    drawOrientation();  // 方向を描く
}, 33); 

  function displayData() {
    var txt = document.getElementById("txt");   // データを表示するdiv要素の取得
    txt.innerHTML = "alpha: " + alpha + "<br>"  // x軸の値
                + "beta:  " + beta  + "<br>"  // y軸の値
                + "gamma: " + gamma;          // z軸の値
}

// コンパスのような絵を描く drawOrientation 関数
function drawOrientation() {
    var centerX = canvas.width  / 2;            // canvasの中心のX座標
    var centerY = canvas.height / 2;	        // canvasの中心のY座標
    var radius  = 100;                          // 枠円の半径および針の長さ
    var radianAlpha = -alpha * Math.PI / 180;    // 角度をラジアンに変換 ( - で逆転させている)
    context.clearRect(0, 0, canvas.width, canvas.height);   // canvasの内容を消す clearRect(x, y, w, h)
    context.beginPath();                        // 描画開始
    context.arc(centerX, centerY, radius, 0, 2 * Math.PI);  // 枠円を描く
    context.strokeStyle = "rgb(0, 0, 0)";       // 枠円の線の色
    context.lineWidth = 2;                      // 線の太さ
    context.stroke();                           // 線を描画
    context.beginPath();                        // 描画開始
    context.moveTo(centerX, centerY);           // 中心に移動
    // 線を引く(cosでx座標、sinでy座標が得られる。長さradiusを掛ける。-90度すると真上に向く。)
    context.lineTo(centerX + Math.cos(radianAlpha - Math.PI / 2) * radius,
                centerY + Math.sin(radianAlpha - Math.PI / 2) * radius);
    context.strokeStyle = "rgb(255, 0, 0)";     // 針の線の色
    context.lineWidth = 5;                      // 線の太さ
    context.stroke();                           // 線を描画
}


// 許可済みならすぐにジャイロセンサーを開始
if (gyroPermissionGranted === 'true') {
//   startGyro();
} else {
  // 許可が未設定の場合はボタンを表示
  requestPermissionBtn.style.display = 'block';
}

// ボタンクリックでジャイロセンサーの許可をリクエスト
requestPermissionBtn.addEventListener('click', () => {
  if (typeof DeviceMotionEvent.requestPermission === 'function') {
    DeviceMotionEvent.requestPermission()
      .then(response => {
        if (response === 'granted') {
          // 許可されたらジャイロセンサーを開始
          startGyro();
        } else {
          alert('ジャイロデータへのアクセスが拒否されました。');
        }
      })
      .catch(console.error);
  }
});

最後に

ジャイロ機能を使用して、タップせずにページ遷移ができる仕組みを作ってみましたが、スマホを前後に傾けたときに左右に動かしたと認識されることが多く、誤作動が頻発しました。実際に実装してみた結果、やはりタップでの遷移が最も安定していると感じました。

https://github.com/koudaihirata/gyro

参考にさせてもらったサイト

https://kkblab.com/make/javascript/gyro.html
https://developer.apple.com/documentation/coremotion/getting_raw_gyroscope_events

Discussion