ローカルサーバでスマートフォンブラウザから端末の向きを検出する


## 結論から

httpsサーバじゃないといけない

カメラを利用するのにも必要だったので、以前の記事を参考に

https://qiita.com/tkyko13/items/1871d906736ac88a1f35

```sh
$ openssl req -newkey rsa:2048 -new -nodes -x509 -days 3650 -keyout key.pem -out cert.pem
```

で、鍵作ったあと

```sh
$ http-server -S -C cert.pem
```

or

```js:server.js
const fs = require('fs')
const express = require('express');
const app = express();
const https = require('https');
const options = {
  key:  fs.readFileSync('./key.pem'),
  cert: fs.readFileSync('./cert.pem')
};
const server = https.createServer(options, app);
const PORT = process.env.PORT || 3000;

// ルート
app.get('/' , function(req, res){
    res.sendFile(__dirname+'/public/index.html');
});

// サーバの立ち上げ
server.listen(PORT, () => {
  console.log('listening on https://localhost:' + PORT);
});

```

## 動作した環境

* iOS15.6.1(iPhone8)
    * safari
    * chrome
* Android12(Pixel 6a)
    * chrome
* Node.js v16.15.1
    * express v4.18.1

## スマートフォンブラウザで向き取得するための参考サイト

https://developer.mozilla.org/ja/docs/Web/Events/Detecting_device_orientation

https://qiita.com/kob58im/items/17059ab51dc93503994b

https://openprocessing.org/sketch/1156927

ここらへんだけを見るとすぐ使えそうに思えたけども、コードコピペしてもローカルサーバだとこんなエラー

![IMG_3488.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1326419/5d70070f-6877-4133-254c-8a139b9b1634.png)

CodePenやOpenProcessingなんかだとうまくいく

あと、[Netlify](https://www.netlify.com/)にアップしたらうまくできたし、たぶん[GitHub pages](https://docs.github.com/ja/pages/getting-started-with-github-pages/creating-a-github-pages-site)でもできると思う

が、今回動作させたい環境でインターネット使えるか怪しかったので、ローカルサーバで動作させてみたかった

### 注意点

ローカルサーバ関係ないが、スマフォの向き情報を取得するための許可をもらうための処理の

```js
//. ユーザーに「許可」を求めるダイアログを表示
          DeviceOrientationEvent.requestPermission().then(function (response) {
            if (response === 'granted') {
              // 許可
            }
          }).catch(function (e) {
            alert(e);
            console.error(e);
          });
```

この処理は、ボタンクリックした後に処理します

## サンプルコード集

### p5jsを使って画面に傾きをテキスト表示

```html
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>スマフォ向き取得テスト</title>
</head>

<body>

  <button id="btn">クリック</button>

  <ul>
    <li id="rx"></li>
    <li id="ry"></li>
    <li id="rz"></li>
  </ul>

  <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.2/p5.min.js"></script>
  <script>
    function setup() {
      frameRate(10);

      select('#btn').mousePressed(() => {
        if (window.DeviceOrientationEvent) {
          //. ユーザーに「許可」を求めるダイアログを表示
          DeviceOrientationEvent.requestPermission().then(function (response) {
            if (response === 'granted') {
              // 許可
              select('#btn').hide();
            }
          }).catch(function (e) {
            alert(e);
            console.error(e);
          });
        }
      });
    }

    function draw() {
      const rx = round(rotationX * 100) / 100;
      const ry = round(rotationY * 100) / 100;
      const rz = round(rotationZ * 100) / 100;
      select('#rx').html(rx);
      select('#ry').html(ry);
      select('#rz').html(rz);
    }

  </script>
</body>

</html>
```

p5jsだと許可もらったあとは `rotationX` で扱えます

その他は、[こちら](https://p5js.org/reference/#group-Events)

### socket.ioでサーバに共有

#### サーバ側（Node.js）

```sh
npm i socket.io express
```

```js
const fs = require('fs')
const express = require('express');
const app = express();
const path = require('path');
const https = require('https');
const options = {
  key:  fs.readFileSync('./key.pem'),
  cert: fs.readFileSync('./cert.pem')
};
const server = https.createServer(options, app);
const io = require('socket.io')(server);
const PORT = process.env.PORT || 3000;

// app.get('/', express.static(path.join(__dirname, '/public/index.html')));
app.get('/' , function(req, res){
    res.sendFile(__dirname+'/public/index.html');
});
// app.use('/public', express.static(path.join(__dirname, '/public')));

io.on('connection',function(socket){
    socket.on('rotation',function(rot){
        console.log('rx:' + rot.x + ',  ry:'+rot.y+',  rz:'+rot.z);
    });
});

// サーバの立ち上げ
server.listen(PORT, () => {
  console.log('listening on https://localhost:' + PORT);
});
```

#### クライアント側（html,js）

```html:index.html
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <style>
    html, body {
      margin: 0;
      padding: 0;
    }
    canvas {
      display: block;
    }
  </style>
  <title>スマフォ向き取得テスト</title>
</head>

<body>
  <script src="/socket.io/socket.io.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.2/p5.min.js"></script>
  <script>
    const socket = io();

    let rots = {};
    let textArea;

    function setup() {
      if (DeviceOrientationEvent && DeviceOrientationEvent.requestPermission) {
        const btn = createButton('クリックして許可', () => {
          //. ユーザーに「許可」を求めるダイアログを表示
          DeviceOrientationEvent.requestPermission().then(function (response) {
            if (response === 'granted') {
              // 許可
              btn.hide();
            }
          }).catch(function (e) {
            alert(e);
            console.error(e);
          });
        });
      }

      // createCanvas(windowWidth, windowHeight);
      textArea = createElement('textArea');
      textArea.position(10, 10);
      textArea.style('width:95%;height:95vh');

      socket.on('rotation', (rot) => {
        // console.log(rot);
        rots[rot.id] = rot;
      });
    }

    function draw() {
      textArea.html(JSON.stringify(rots, null, ' '));
    }

    function deviceMoved() {
      const rx = round(rotationX * 100) / 100;
      const ry = round(rotationY * 100) / 100;
      const rz = round(rotationZ * 100) / 100;
      socket.emit('rotation', {
        x: rx,
        y: ry,
        z: rz
      });
    }

  </script>
</body>

</html>
```

p5jsだと、デバイス端末が動いたときだけ送るってこともできます