ASP.NET SignalR でデバイスの回転状態を同期する

前回の transform と deviceorientation における回転の表現 (HTML) では、
そのデバイスのブラウザー上で回転の状態を表示していましたが、
今回は他のデバイスのブラウザーにネットワーク経由で同期するようにしました。

WebSocket で同期するためのフレームワークとして ASP.NET SignalR を利用し、
Azure Web App に GitHub からの継続的デプロイを設定しています。

HTML 3D Device Orientation on Web Browsers

 

これを実装する方法を以下に示します。

Visual Studio で空の ASP.NET Web プロジェクトを作成し、NuGet で Microsoft.AspNet.SignalR をインストールします。
まずサーバー側の C# コードとして、次のクラスを実装します。

using System;
using Microsoft.AspNet.SignalR;
namespace DeviceSyncWeb
{
public class SensorHub : Hub
{
public void UpdateOrientation(double alpha, double beta, double gamma)
{
Clients.Others.NotifyOrientation(alpha, beta, gamma);
}
}
}
view raw SensorHub.cs hosted with ❤ by GitHub
using System;
using Owin;
namespace DeviceSyncWeb
{
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.MapSignalR();
}
}
}
view raw Startup.cs hosted with ❤ by GitHub

Startup.Configuration メソッドの中で ASP.NET SignalR を有効にします。
そして、送受信をするためのハブとして SensorHub クラスを作成しています。
今回は、JavaScript の deviceorientation イベントで取得できる alpha, beta, gamma の値を引数で受け取って
クライアントに通知するだけです。なお、Clients.Others は送信者以外のクライアントを表します。

次に、クライアントとなる HTML を実装します。

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width" />
<title>DeviceSync - Sensor</title>
<script type="text/javascript" src="/Scripts/jquery-1.6.4.min.js"></script>
<script type="text/javascript" src="/Scripts/jquery.signalR-2.4.0.min.js"></script>
<script type="text/javascript" src="/signalr/hubs"></script>
</head>
<body>
<script type="text/javascript">
var sensorHub = $.connection.sensorHub;
$.connection.hub.start();
if (window.DeviceOrientationEvent) {
window.addEventListener("deviceorientation", function (e) {
sensorHub.server.updateOrientation(e.alpha, e.beta, e.gamma);
});
}
</script>
</body>
</html>
view raw sensor.html hosted with ❤ by GitHub
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width" />
<title>DeviceSync - Viewer</title>
<script type="text/javascript" src="/Scripts/jquery-1.6.4.min.js"></script>
<script type="text/javascript" src="/Scripts/jquery.signalR-2.4.0.min.js"></script>
<script type="text/javascript" src="/signalr/hubs"></script>
<!-- style の記述を省略 -->
</head>
<body>
<div id="log"></div>
<div class="container">
<div id="cube">
<div class="face front">1</div>
<div class="face back">6</div>
<div class="face right">2</div>
<div class="face left">5</div>
<div class="face top">3</div>
<div class="face bottom">4</div>
</div>
</div>
<img id="image" src="website_news.png" style="margin: 80px;" />
<script type="text/javascript">
var sensorHub = $.connection.sensorHub;
sensorHub.client.notifyOrientation = updateOrientation;
$.connection.hub.start();
var logEl = document.querySelector("#log");
var cubeEl = document.querySelector("#cube");
var imageEl = document.querySelector("#image");
function updateOrientation(alpha, beta, gamma) {
logEl.innerHTML = `alpha: ${alpha}<br />beta: ${beta}<br />gamma: ${gamma}`;
var rotation = `rotateZ(${-alpha}deg) rotateX(${-beta}deg) rotateY(${gamma}deg)`;
cubeEl.style.transform = rotation;
imageEl.style.transform = rotation;
}
</script>
</body>
</html>
view raw viewer.html hosted with ❤ by GitHub

センサーのデータを送信する sensor.html と、それを受信して表示する viewer.html に分かれています。
各 JS ファイルを <script> で読み込み、$.connection から各機能にアクセスします。

 

前回: transform と deviceorientation における回転の表現 (HTML)

作成したサンプル

バージョン情報

  • .NET Framework 4.7
  • ASP.NET SignalR 2.4.0

transform と deviceorientation における回転の表現 (HTML)

CSS の transform プロパティと JavaScript の deviceorientation イベントではともに 3D の回転状態 (姿勢、傾き) が登場しますが、
その扱い方に差があるため検証しました。
deviceorientation は、デバイスのジャイロ センサーが回転状態を通知することで発生するイベントです。

HTML の 3 次元座標系では、2 次元スクリーン座標系の x 軸および y 軸に加えて、スクリーンに垂直な z 軸が存在します。
デバイス (スマートフォンなど) を水平に持ち、北を向いた状態を基準に考えます。

このとき、CSS の transform プロパティと JavaScript の deviceorientation イベントにおける、
回転に関する性質の違いを下の表にまとめました。例えば z 軸を中心とする回転の角度は、現在は北を向いていたとしたら、
transform では東側 (時計回り) を向くと正、deviceorientation では西側を向くと正になります。

  transform deviceorientation
座標系 左手系 右手系
x 軸 右が正 右が正
y 軸 下 (手前) が正 上 (奥) が正
z 軸 (画面が水平のとき) 鉛直の上が正 鉛直の上が正
回転角度 回転軸の正方向に左ねじを進める場合が正 回転軸の正方向に右ねじを進める場合が正

 

検証のため、CSS の transform プロパティと JavaScript の deviceorientation イベントを利用して、
デバイスの回転状態を画面内の立方体オブジェクトに同期させるサンプルを作成しました。

DeviceOrientation

ジャイロ センサーを搭載した端末であれば、こちらのテストページで確認できます。
HTML のソースは以下の通りです。

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width" />
<title>Device Orientation</title>
<style type="text/css">
.container {
width: 2px;
height: 2px;
margin: 100px auto;
perspective: 300px;
}
#cube {
transform-style: preserve-3d;
}
.face {
width: 100px;
height: 100px;
position: absolute;
left: -50px;
top: -50px;
background: rgba(255, 102, 0, 0.5);
border: 2px solid gray;
text-align: center;
font-size: 60px;
line-height: 100px;
}
.front {
transform: translateZ(50px);
}
.back {
transform: rotateY(180deg) translateZ(50px);
}
.right {
transform: rotateY(90deg) translateZ(50px);
}
.left {
transform: rotateY(-90deg) translateZ(50px);
}
.top {
transform: rotateX(90deg) translateZ(50px);
}
.bottom {
transform: rotateX(-90deg) translateZ(50px);
}
</style>
</head>
<body>
<div id="log"></div>
<div class="container">
<div id="cube">
<div class="face front">1</div>
<div class="face back">6</div>
<div class="face right">2</div>
<div class="face left">5</div>
<div class="face top">3</div>
<div class="face bottom">4</div>
</div>
</div>
<script type="text/javascript">
var logEl = document.querySelector("#log");
var cubeEl = document.querySelector("#cube");
if (window.DeviceOrientationEvent) {
window.addEventListener("deviceorientation", function (e) {
logEl.innerHTML = `alpha: ${e.alpha}<br />beta: ${e.beta}<br />gamma: ${e.gamma}`;
cubeEl.style.transform = `rotateZ(${-e.alpha}deg) rotateX(${-e.beta}deg) rotateY(${e.gamma}deg)`;
});
}
</script>
</body>
</html>
view raw cube-sync.html hosted with ❤ by GitHub

以下は、各技術についての説明です。

transform プロパティ

transform プロパティで rotateX などを利用して回転状態を指定する場合、次に示すように複数の回転を重ね合わせることができます。

CSS:
transform: rotateX(45deg) rotateY(30deg) rotateZ(60deg);

JavaScript:
element.style.transform = "rotateX(45deg) rotateY(30deg) rotateZ(60deg)";

ただし、座標系ごと回転させながら左から順に適用します (オイラー角)。
これは、以前に 3D における回転の表現と相互変換で書いた通り、元の座標系のまま右から順に適用する、と考えても同じです。

以下に rotateX(45deg)rotateY(45deg) を組み合わせた例を載せておきます。

初期状態

rotateX(45deg) (左)        rotateX(45deg) rotateY(45deg) (右)

rotateY(45deg) (左)        rotateY(45deg) rotateX(45deg) (右)

deviceorientation イベント

window.addEventListenerdeviceorientation に対するイベントリスナーを登録します。
デバイスの回転状態が変化すると、イベントリスナーが呼び出されます。

引数の alpha, beta, gamma はそれぞれ z 軸、x 軸、y 軸を中心とした回転の角度を表し、
座標系ごと回転させながらこの順に重ね合わせたものが回転状態を表します。
それぞれの値の範囲は次の通りです。

  • z 軸: 0 ≦ alpha < 360
    • 北を向いたとき、alpha = 0
  • x 軸: -180 ≦ beta < 180
  • y 軸: -90 ≦ gamma < 90

 

回転状態の同期

以上から、デバイスの回転状態を画面内のオブジェクトに同期させるには次のようにします。

cubeEl.style.transform = `rotateZ(${-e.alpha}deg) rotateX(${-e.beta}deg) rotateY(${e.gamma}deg)`;

正負の符号に注意します。
結果として、z 軸および x 軸における回転角度の正負は異なり、y 軸では同じになります。

 

次回は、回転状態をネットワーク経由で同期させます。

次回: ASP.NET SignalR でデバイスの回転状態を同期する

作成したサンプル
参照
transform
deviceorientation