iOS8以降では、ブラウザ上でプラグイン無しに高度なグラフィックが描画できるWebGLが使えるようになりました。またHTML5の多くのAPIがほとんどのモダンブラウザで既に使えるようになってきています。今回は、HTML5で実装予定のファイルの取り扱いAPI、ドラッグアンドドロップのAPI、デバイスの傾き検知処理、そしてWebGL(CanvasAPIを使用)を使ったWebアプリケーションの作例として、そのiPhoneでも動く簡単な3Dの写真ビューアを作ってみました。本記事ではデモで使用されているこれら4つの技術を中心に解説していきます。

※ Androidについては手元の環境では動作しませんでした。動作しない理由については現在検証中です。

iPhoneでの動作ビデオ

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

はじめに. 制作環境について

今回のデモは、JavaScriptでそのまま制作することも可能なのですが、クラス設計の容易さ、静的型付けの利便性が魅力的で、将来性もあるTypeScriptを使用して作りました。以下、TypeScriptのコードとして話を進めていきますが、適宜JavaScriptに読み替えるとJavaScriptでも動作するコードとなります。

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", (e)=> this.inputChangeHandler(e));
// イベント処理
private inputChangeHandler(event):void
{
    var files:FileList = event.target.files;
}

JavaScriptでは、changeイベントが発生した時に発行されるイベントオブジェクトeventから、event.target.filesとすることで、input要素に指定されたファイルを取得することができます。

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

Drag and Drop APIを使うと、HTMLの任意の要素にドラッグしたファイルを取り扱うことができます。Facebookの写真投稿機能やGoogle ドライブのファイルアップロード機能など多くのWebサービスで使われている手法です。ドロップを受け付けたい要素でdropイベントが発生した時に発行されるイベントオブジェクトeventから、event.dataTransfer.filesでファイルを取得できます。

<!--HTMLコード-->
<input type="file" multiple="multiple" />
// JavaScriptコード
// イベント設定
var fileZone:HTMLElement = document.getElementById("fileZone");
fileZone.addEventListener("dragover", (e) => this.dragOverHandler(e));
fileZone.addEventListener("drop", (e) => this.dropHandler(e));

// ドラッグオーバー時のイベント処理
private dragOverHandler(event):void
{
  // デフォルトの挙動を停止
  event.preventDefault();
}

// ドロップ時のイベント処理
private dropHandler(event):void
{
  // デフォルトの挙動を停止
  event.preventDefault();
  var files:FileList = event.target.files;
}

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

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

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

ファイルの読み込みが完了した時に発行されるイベントオブジェクトeventから、event.target.resultを参照することで読み込みが完了したデータ(今回の場合はデータURLのみじ列)が取得できます。したがって、下記のようにすればHTMLIMageElementが作成されます。src属性を指定しただけではimageが作成されないことがある(※)ので、HTMLIMageElementが完全に生成された後に処理を行いたい場合は、loadイベント処理を行うとよいでしょう。
※ デスクトップ、スマートフォンのSafariでこのような挙動をします。

Step3. WebGLで描画

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

※ JavaScriptでの解説記事ですが、基本的にはTypeScriptでも同じです。
デモのWebGLでの描画は、demo.Mainクラスで実装しています。Mainクラスの初期化処理(init())で、Step1、Step2で作成した画像リストを受け取ります。写真のレイアウトについて行っている処理は大きく分けて2つです。

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

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

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

window.addEventListener("deviceorientation", (event:DeviceOrientationEvent) => this.onRotationChange(event));

デバイスが傾いた時に発行されるイベント(DeviceOrientationEvent)オブジェクトeventから、それぞれの軸に対しての傾きevent.alpha、event.beta、event.gamma、そしてevent.webkitCompassHeadingを取得できます。この傾きを使い、3D空間に対する視点の移動をします。デモでは、away.controllers.HoverControllerのpanAngleとtiltAngleに適用することで、左右、上下の視点の移動を行っております。

// デバイスの傾きが変わった時のイベント処理
private onRotationChange(event:DeviceOrientationEvent):void {
  var alpha:number = event.alpha;  // z軸の傾き
  var beta:number = event.beta;  // x軸の傾き
  var gamma:number = event.gamma;  // y軸の傾き
  var compassHeading:number = event.webkitCompassHeading; // 方位の取得(Webkit限定)
}

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

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

axis

縦長のスマートフォンをディスプレイを上にして地面に置きます。そのとき、ディスプレイから空に向かって垂直に伸びる軸(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で使えるようになった新たな技術を用いれば、デスクトップ、スマートフォンを問わず、ブラウザだけでユーザーにリッチな体験を提供できるようになります。例えばキャンペーンサイトの定番であったカルーセル表示等もHTML5のWebGLを使えば3D表現にできますし、HTML5の位置情報APIを使えばユーザーの住所入力フォームで入力補助ができたりします。ブラウザのサポートが広がったことで実案件でも導入しやすくなったHTML5の技術を是非体験してみませんか?