2012年7月25日水曜日

3D Translateの実装例



Nack Labで紹介している"ダイス"、およびTech Sampleの"3D 四面"、"3D 八面"はcss trahnsformを使用して3Dの動きを実現しています。

主にPaul R. Hayes氏のExper­i­ment: 3D cube with touch ges­tures and click and dragを参考にしています。

ここでは直方体を横方に回転させる"3D 四面"を例に取り上げて説明します。

4平面を作るためにDIV

<div id="divFaces">
  <div id="cube">
    <div class="face one"   id="one">One</div>
    <div class="face two"   id="two">Two</div>
    <div class="face three" id="three">Three</div>
    <div class="face four"  id="four">Four</div>
  </div>
</div>

上記DIV対応するCSS

全体を包むdivFacesの設定

#divFaces
{
  -webkit-perspective: 420;
  -webkit-perspective-origin: 50% 100px;
}

-webkit-perspective:
視点からの距離。数値が大きいほど望遠、小さいほど広角な効果となります。

-webkit-perspective-origin:
座標軸の中心の位置。この場合、横方向中央(50%)、縦は上から100pxとなります。

divFaces内の直方体の設定

#cube {
  -webkit-transition: -webkit-transform 0.2s linear;
  -webkit-transform-style: preserve-3d;
  margin: -150px auto 0px auto;
  height: 150px;
  width: 200px;
}

-webkit-transform-style: preserve-3d;
3D座標変換を行うための設定。

-webkit-transition: -webkit-transform 0.2s linear;
アニメーションの設定。3D座標変換とは関係ない。
margin、height、widthは適宜設定する。

四面に共通の設定

.face {
  position:absolute;
  height: 100%;
  width: 100%
  padding:0px;
  background-color: rgba(50, 50, 50, 0.5);
  font-size: 27px;
  line-height: 1em;
  color: #fff;
  border: 1px solid #555;
}

position:absolute;

同一平面上に配置するため。これがないと四面のDIVが縦方向に重ならずに並ぶ。

height: 100%;
width: 100%;

cubeのサイズと一致するように100%をセット。100%である必要はなく、pxなどの設定でもよい。

各面についての設定

#cube .one {
  -webkit-transform: translateZ(100px);
}
第1面はZ軸方向に+100px移動。手前に近付いている。

#cube .two {
  -webkit-transform: rotateY(90deg) translateZ(100px);
}
第2面はY軸で+90度回転(まわれ右)してから、Z軸で100px移動(右へ移動)。

#cube .three {
    -webkit-transform: rotateY(180deg) translateZ(100px);
}
第3面はY軸で+180度回転(後向き)してから、Z軸で100px移動(遠ざかる)。

#cube .four {
    -webkit-transform: rotateY(-90deg) translateZ(100px);
}
第4面はY軸で-90度回転(まれれ左)してから、Z軸で100px移動(左へ移動)。

直方体回転

次のようなループで直方体を連続回転させることができます。

//touch, mouseの移動量を yAngle にセットします。
var yAngle = 0;

//TransformでrotateYを行い、Y軸を中心に回転させます。
function rotate() {
  yAngle += 10;
  cube.style.webkitTransform = "rotateY(" + yAngle + "deg)";
  setTimeout("rotate()", 20);
}

yAngleは初期状態からの回転角で、直前に行ったTransformからの差分ではありません。
ループで回転させる場合はsetTimeoutを使用します。

3D Transformの留意点

3Dのtransformで頭が混乱するのは主に次の二点でしょう。
  • XYZについてのtransformの順序で結果が異なる。
    上の例ではY軸で回転してからZ軸で移動しているが、Z軸で移動してからY軸で回転したのでは全く異なる結果となります。
  • rotateは座標の回転で、オブジェクトの回転ではない。
    上の例ではY軸で回転してていますが、Z軸も共に回転しているため、回転後の移動は前後の移動とは限らず、回転角に応じた方向に移動することになります。
3D 四面、八面のソースコード

//コンストラクタ
Faces = function(divFaces, divCube) {
  //直方体が配置されていDIV(divFaces)
  this.div = divFaces;
  this.cubeDiv = divCube;
  //移動量計算のため直前のtouch位置をセット。初期値=Number.MIN_VALUE
  this.x = Number.MIN_VALUE;
  //Y軸を中心とした回転角。
  this.y = 0;
  //Startイベントハンドラをセット。
  this.div.addEventListener(Faces.EventStart, Faces.touchStart, false);
  Faces.instance = this;
  Faces.div = this.div;
}

//iOS, デスクトップでイベント名を使い分ける。
Faces.EventStart = isIOS ? "touchstart" : "mousedown";
Faces.EventMove = isIOS ? "touchmove" : "mousemove";
Faces.EventEnd = isIOS ? "touchend" : "mouseup";
Faces.EventOut = isIOS ? null : "mouseout";

//Facesが作られたときの引数のdiv。イベントが発生するDIVと異なる。
Faces.div = null;

//Startイベントで作られたオブジェクト。
Faces.instance = null;

//Startイベントハンドラ。Moveイベントハンドラセット。
//thisはイベントが発生するDIV.
Faces.touchStart = function() {
  if (Faces.div == null) return;
  Faces.div.addEventListener(Faces.EventMove, Faces.touchMove, false);
  Faces.div.addEventListener(Faces.EventEnd, Faces.touchEnd, false);
  if (!isIOS) {
    Faces.div.addEventListener(Faces.EventOut, Faces.touchEnd, false);
  }
}

//Moveイベントハンドラ。
Faces.touchMove = function() {
  if (Faces.instance == null) return;
  Faces.instance.newX = event.pageX;
  with (Faces.instance) {
   if (x != Number.MIN_VALUE) {
      y += (newX - x);
      var m = y % 90;
      if (m <= 2 &&  m >= -2) {
        y -= m;
      }
      cubeDiv.style.webkitTransform = "rotateY(" + y + "deg)";
    }
    x = newX;
  }
}

//Endイベントハンドラ。End, Moveイベントハンドラ解除。
Faces.touchEnd = function() {
  if (Faces.instance == null) return;
  Faces.instance.x = Number.MIN_VALUE;
  var div = Faces.instance.div;
  div.removeEventListener(Faces.EventMove, Faces.touchMove);
  div.removeEventListener(Faces.EventEnd, Faces.touchEnd);
  div.removeEventListener(Faces.EventOut, Faces.touchEnd);
}

Faces使用例

上記のようにDIVを配置し、body.onloadなどでFacesを作る。

<body onload="new Faces(divFaces, cube)">

3D 八面の場合

八面の場合もJavaScriptは共通で、面のモデリング、CSSが異なります。

<div id="divFaces8" style="margin-top:180px">
    <div id="cube8">
      <div class="face8 f1" id="f1">One</div>
      <div class="face8 f2" id="f2">Two</div>
      <div class="face8 f3" id="f3">Three</div>
      <div class="face8 f4" id="f4">Four</div>
      <div class="face8 f5" id="f5">Five</div>
      <div class="face8 f6" id="f6">Six</div>
      <div class="face8 f7" id="f7">Seven</div>
      <div class="face8 f8" id="f8">Eight</div>
  </div>
</div>

Facesを作成するときの引数を8面のものに変更します。

<body onload="new Faces(divFaces8, cube8)">

8面の場合のCSS

#divFaces8
{
  -webkit-perspective: 250;
  -webkit-perspective-origin: 50% 80px;
}

#cube8 {
  position:relative;
  margin: -100px auto 0px auto;
  height: 133px;
  width: 100px;
  -webkit-transition: -webkit-transform 0.2s linear;
  -webkit-transform-style: preserve-3d;
}

.face8 {
  position:absolute;
  height: 120px;
  width: 100px;
  padding: 0px;
  background-color: rgba(50, 50, 50, 0.5);
  font-size: 20px;
  line-height: 1em;
  color: #fff;
  border: none;
}

#cube8 .f1 {
    -webkit-transform: translateZ(120px);
    background:gray;
}

#cube8 .f2 {
    -webkit-transform: rotateY(45deg) translateZ(120px);
    background:green;
}

#cube8 .f3 {
    -webkit-transform: rotateY(90deg) translateZ(120px);
    ---webkit-transform: translateZ(-100px);
    background:blue;
}

#cube8 .f4 {
    -webkit-transform: rotateY(135deg) translateZ(120px);
    background:yellow;
}

#cube8 .f5 {
    -webkit-transform: rotateY(180deg) translateZ(120px);
    background:red;
}

#cube8 .f6 {
    -webkit-transform: rotateY(225deg) translateZ(120px);
    background:green;
}

#cube8 .f7 {
    -webkit-transform: rotateY(270deg) translateZ(120px);
    background:blue;
}

#cube8 .f8 {
    -webkit-transform: rotateY(315deg) translateZ(120px);
    background:yellow;
}

Manifest+LocalStorageによるオフラインアプリ作成

0 件のコメント: