ブラウザ上で高度なグラフィックを描画できるWebGL。また、HTMLには多彩なAPIが備わっています。今回はiPhoneでも動作する3Dの写真ビューアを作ってみました。本記事では次の4つの技術を中心に解説していきます。

  • ファイルの取り扱いAPI
  • ドラッグアンドドロップのAPI
  • デバイスの傾き検知処理
  • WebGL

※ Androidについては手元の環境では動作しませんでした。

iPhoneでの動作ビデオ

実際にiPhoneで動いているデモは以下の動画です。iPhone 5Sです。

STEP1. ファイルのアップロード方法

HTML5で登場したFile APIを使用すると、ローカルからファイルをアップロードすることができるようになります。ここでは、input要素を使う方法と、ドラッグアンドドロップを使う方法を紹介します。デモでは、demo.FileUploadクラスで実装しています。

input要素を使ってファイルをアップロード

input要素のtype属性でfileを指定すると、このinput要素からファイルをアップロードできるようになります。また、multiple属性を指定することで、複数ファイルのアップロードにも対応します。

<!--HTMLコード-->
<input type="file" multiple="multiple" />
// JavaScriptコード
// イベント設定
document.getElementById("fileInput").addEventListener("change", inputChangeHandler);
// イベント処理
function inputChangeHandler(event) {
    const files:FileList = event.target.files;
}

JavaScriptではchangeイベントが発生した時にevent.target.filesを参照します。すると、input要素に指定されたファイルを取得できます。

ドラッグアンドドロップでファイルをアップロード

Drag and Drop APIを使うと、HTMLの任意の要素にドラッグしたファイルを取り扱うことができます。ドロップを受け付けたい要素でdropイベントを監視し、event.dataTransfer.filesでファイルを取得できます。

// イベント設定
const fileZone = document.querySelector('.file-zone');
// ドラッグオーバー時のイベント処理
fileZone.addEventListener('dragover', (event) => {
  // デフォルトの挙動を停止
  event.preventDefault();
});
// ドロップ時のイベント処理
fileZone.addEventListener('drop', (event) => {
  // デフォルトの挙動を停止
  event.preventDefault();
  const files = event.dataTransfer.files;

  console.log(files)
});

STEP2. ブラウザで受け取ったファイルを読み込む

Step1でローカルからファイルを受け取ったら、今度はそれらのファイルを読み込んでJavaScript内で使えるようにする必要があります。ファイルの読み取りには、FileReaderクラスを使います。今回は、受け取った画像ファイルを、HTMLImageElementにしてWebGLで処理したいので、HTMLImageElementに変換することを目指して処理を進めていきます。

// FileReaderのインスタンスを生成
const fileReader = new FileReader();
// イベント設定
fileReader.addEventListener("load", fileReadHandler);
// File型のファイルをデータURLとして読み込み
fileReader.readAsDataURL(file);

ファイルの読み込みが完了した時に発行されるイベントオブジェクトeventから、event.target.resultを参照することで読み込みが完了したデータが取得できます。

Step3. WebGLで描画

画像の準備が完了したら、WebGLで描画していきます。今回は、WebGLを扱いやすくしたフレームワークであるAway3D TypeScriptを使用します。基本的な使用方法は弊社池田の記事が参考になりますのでそちらをご参照いただければと思います。

※ JavaScriptでの解説記事ですが、基本的にはTypeScriptでも同じです。

デモのWebGLでの描画は、demo.Mainクラスで実装しています。Mainクラスの初期化処理(init())で、Step1、Step2で作成した画像リストを受け取ります。写真のレイアウトについて行っている処理は大きく分けてふたつです。

  1. 個別の写真のMeshを作成
  2. 各写真を3D空間に配置

Step4. スマートフォンにおける傾きの取得

スマートフォンなどデバイスの傾きが取得できる環境においては、以下のコードでデバイスの傾きを取得できます。

window.addEventListener("deviceorientation", onRotationChange);

デバイスが傾いた時に発行されるDeviceOrientationEventイベントにて、傾きの値としてevent.alphaevent.betaevent.gamma、そしてevent.webkitCompassHeadingを取得できます。

この傾きを使い、3D空間に対する視点の移動をします。デモでは、away.controllers.HoverControllerpanAngletiltAngleに適用することで、左右、上下の視点の移動を行います。

window.addEventListener("deviceorientation", onRotationChange);

/** デバイスの傾きが変わった時 */
function onRotationChange(event) {
  const alpha = Math.floor(event.alpha); // z軸の傾き
  const beta = Math.floor(event.beta); // x軸の傾き
  const gamma = Math.floor(event.gamma); // y軸の傾き

  // 方位の取得(Webkit限定)
  const compassHeading = Math.floor(event.webkitCompassHeading);
}

※ iOS、Android等傾きが検知できるデバイスでお試しください。

Tips. それぞれの軸について

縦長のスマートフォンをディスプレイを上にして地面に置きます。そのとき、ディスプレイから空に向かって垂直に伸びる軸(Z軸)に対する回転がevent.alpha、ディスプレイの左右にまっすぐ伸びる軸(X軸)に対する回転がevent.beta、ディスプレイの上下にまっすぐ伸びる軸(Y軸)に対する回転がevent.gammaです。各軸の詳細については、W3Cのドラフトに詳しいです。

W3Cの仕様によれば、event.beta-180から180まで、event.gamma-90から90まで変化するのですが、iOSではevent.beta-90から90まで、event.gamma-180から180までの変化となります。

また、event.alphaについては、デバイスを地面向きから空向きに傾けて行く際、途中で180度変わるという挙動があるため、カメラの水平視点移動には使用しませんでした。代わりに、方位を取得するevent.webkitCompassHeadingを使用しました。

まとめ

HTML5で使えるようになった新たな技術を用いれば、デスクトップ、スマートフォンを問わず、ブラウザだけでユーザーにリッチな体験を提供できるようになります。

たとえば、キャンペーンサイトの定番であったカルーセル表示等もWebGLを使えば3D表現にできますし、HTML5の位置情報APIを使えばユーザーの住所入力フォームで入力補助ができます。ブラウザのサポートが広がったことで実案件でも導入しやすくなったHTML5の技術をぜひ体験してみませんか?