WebARのアニメーション


## やること

ARで立方体のオブジェクトを表示して、
その立方体オブジェクトをクリック(orタッチ)すると回転する

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">Three.js+ARでクリックイベント/タッチイベント使えた。Tween.jsも一応使えた。Win10+Firefox, Android9+Chrome, iOS13+Safariで動いた。ふぅ <a href="https://t.co/C6a67RMxvY">pic.twitter.com/C6a67RMxvY</a></p>&mdash; j4amountain (@zsipparu) <a href="https://twitter.com/zsipparu/status/1254037169066270720?ref_src=twsrc%5Etfw">April 25, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

Three.jsのrotationを使った回転と
Tween.jsを使った回転のサンプルスクリプト


## ソース

githubに格納 -> [ClickToMove](https://github.com/zgw426/Three.js-FirstStep/tree/master/ClickToMove)

ブラウザがカメラにアクセスするため ↓ などでSSL(https)対応Webサーバを用意

- [[Node.js] Windows10にオレオレ証明書でSSL通信のWebサーバを作る](https://qiita.com/zgw426/items/3e3dcb1ae26b2efa2eb7)



ARマーカ `hiro` はネットで別途調達が必要

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274270/7eb75a5b-a34e-6a9f-0755-796685fb1a87.png" width=200>


## 動作確認結果

以下の条件で動作を確認

- Win10 Chrome (バージョン 81.0.4044.122)
- Win10 FireFox (バージョン 75.0)
- Win10 Microsoft Edge
- iOS13 Safari
- Android9 Chrome

※スマホ（iOS,Android）では、画面タッチしても立方体が動かずに何回も画面タッチした。


## スクリプトの説明

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274270/8f90f250-b26b-e08c-469e-b1bea1d5ac8c.png)

`mesh01`の立方体を作るスクリプトはここ

```javascript
//-- ↓ 立方体1つ目 ↓ ---------------------------------//
let geometry01	= new THREE.CubeGeometry(1,1,1);
let material01 = [
    new THREE.MeshLambertMaterial({color: 0xE9546B,opacity: 0.3,transparent: true,side: THREE.DoubleSide}), // right
    new THREE.MeshLambertMaterial({color: 0xE9546B,opacity: 0.4,transparent: true,side: THREE.DoubleSide}), // left
    new THREE.MeshLambertMaterial({color: 0x00A95F,opacity: 0.5,transparent: true,side: THREE.DoubleSide}), // front
    new THREE.MeshLambertMaterial({color: 0x00A95F,opacity: 0.6,transparent: true,side: THREE.DoubleSide}), // back
    new THREE.MeshLambertMaterial({color: 0x187FC4,opacity: 0.7,transparent: true,side: THREE.DoubleSide}), // top
    new THREE.MeshLambertMaterial({color: 0x187FC4,opacity: 0.8,transparent: true,side: THREE.DoubleSide}), // bottom
];
mesh01 = new THREE.Mesh( geometry01, material01 );
mesh01.position.y = 0.5;
mesh01.name = "OBJECT001";
markerRoot01.add( mesh01 );
//-- ↑ 立方体1つ目 ↑ ---------------------------------//
```


クリック(orタップ)自の処理はここ、これもどこかのソースをコピーさせたもらった（どこだっけ・・）

```javascript
//===================================================================
// マウスダウン（タップ）によるピッキング処理
//===================================================================
window.addEventListener("mousedown", function(ret) {
  let mouseX = ret.clientX;                           // マウスのx座標
  let mouseY = ret.clientY;                           // マウスのy座標
  //console.log(mouseX + " : " + mouseY);
  mouseX =  (mouseX / window.innerWidth)  * 2 - 1;    // -1 ～ +1 に正規化されたx座標
  mouseY = -(mouseY / window.innerHeight) * 2 + 1;    // -1 ～ +1 に正規化されたy座標
  let pos = new THREE.Vector3(mouseX, mouseY, 1);     // マウスベクトル
  pos.unproject(camera);                              // スクリーン座標系をカメラ座標系に変換
  // レイキャスタを作成（始点, 向きのベクトル）
  let ray = new THREE.Raycaster(camera.position, pos.sub(camera.position).normalize());
  let obj = ray.intersectObjects(scene.children, true);   // レイと交差したオブジェクトの取得
  if(obj.length > 0) {                                // 交差したオブジェクトがあれば
	picked(obj[0].object.name);                       // ピックされた対象に応じた処理を実行
	console.log("debug 001");
  }
  console.log("-----");
});
```

クリック(orタップ)したところにオブジェクトがあれば、各オブジェクトに用意したフラグが変わる


`mesh01`, `mesh02`の立方体は、フラグ(`m01Flg`,`m02Flg`)が1以外のときに、Three.jsのrotationで回転する。

```javascript
function animate()
{
	requestAnimationFrame(animate);
	deltaTime = clock.getDelta();
	update();
	render();
}

function update()
{
	if( arToolkitSource.ready !== false ) arToolkitContext.update( arToolkitSource.domElement );
	if( m01Flg != 1){
		mesh01.rotation.x += 0.1
	}
	if( m02Flg != 1){
		mesh02.rotation.y += 0.1
	}
}
```

`mesh03`の立方体は、フラグ(`m03Flg`)が0以外のときに、Three.jsのrotationで回転する。（ソースは [**こちら**](https://qiita.com/MasaoBlue/items/1ff943dde6ab5f53ef02)から拝借 ）

```javascript
//===================================================================
// TWEEN
//===================================================================
let cube_size = 1; // cubeのサイズ
let cube_count = 1; // cubeの配置数
let grid_count = 10; // グリッドの分割数
let interval = 1000; // 移動アニメーションの間隔
let duration = 500; // 1回の移動時間
let stop_rate = 0; // 移動しない確率
let cube_half = cube_size / 2;

TWEEN.autoPlay(true);
let next_move = function () {
	if(m03Flg != 0){
		let origin_pos = mesh03.position.clone();
		let origin_quaternion = mesh03.quaternion.clone();
		let move_axis = 'x';
		let move_offset = cube_size;
		let from_param = { x: 0 }; // tween開始時の値
		let to_param   = { x: 1 }; // tween終了時の値
		let duration = 500; // 単位はミリ秒
		let tween = new TWEEN.Tween(from_param)
			.to(to_param, duration)
			.easing(TWEEN.Easing.Linear)
			.on('update', function (_a) {
				let x = _a.x;
				let r = cube_half * Math.sqrt(2); // 中心の回転半径
				let center_angle = 45 + (90 * x); // 現在の中心位置の角度（移動開始時は45°）
				let center_rad = center_angle * Math.PI / 180; // 角度をラジアンに変換
				let current_height = Math.sin(center_rad) * r - cube_half; // 移動中のcube中心の高さ
				let current_move = (cube_half - Math.cos(center_rad) * r) * move_offset; // 移動中のcube中心の位置
				// 移動前のpositionを基準に計算
				mesh03.position[move_axis] = origin_pos[move_axis] + current_move;
				mesh03.position.y = origin_pos.y + current_height;
				let axis = new THREE.Vector3(0, 0, -1); // 回転軸
				let new_q = origin_quaternion.clone();
				let target = new THREE.Quaternion();
				let rad = (Math.PI / 2) * x;
				target.setFromAxisAngle(axis, rad);
				new_q.multiply(target);
				mesh03.quaternion.copy(new_q);
			});
		tween.start();
	}
};
setInterval(next_move, interval);
```

![line.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274270/dfac5efd-a71e-340b-b42c-eabdc438621f.png)