three.js キーフレームアニメーション基礎


# 概要
three.jsには様々なアニメーションの方法がありますが、今回はキーフレームアニメーションによってアニメーションを行う方法について説明します。

# デモ
今回は例として以下のデモのような回転しながら動くティーポットを作成します。

https://arihide.github.io/demos/keyframe/
[![teapod.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/323576/5dec9371-5bf1-66c1-09e1-483f956a1358.gif)](https://arihide.github.io/demos/keyframe/)

ソースコードはこちら
https://github.com/Arihide/demos/tree/master/keyframe

# キーフレームアニメーションとは
プログラムの説明に入る前にキーフレームアニメーションについて簡単に説明します。キーフレームアニメーションとは、**キーフレーム**と**補間**を用いたアニメーションのことです。ここで**キーフレームとは時刻とそれに対応する値のペア**のことです。数学っぽく書くと

```math
keyframe = (t, v)
```
という風になります。キーフレームを以下のように複数個用意します。

```math
(t_0, v_0), (t_1, v_1), (t_2, v_2),\ldots, (t_n, v_n)
```
座標軸上にプロットすると以下のような感じになります。（適当に値を与えています）

![plotpoint.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/323576/84f3e87a-4b07-9f52-ef8a-b9b52a8554ae.png)
見てもらえばわかる通り、このままでは、キーフレームの存在する時刻上にしか値が存在せず、例えばt=4のとき値が存在せず困ってしまいます。
なので、キーフレームアニメーションに必要な補間をして任意の時刻に値が存在するようにします。このように連続な関数を作ることでアニメーションさせることができます。以下は補間の例です。
![interp_linear-1024x768.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/323576/2fb3e408-507c-72f0-5ca0-dfe8a2ab5d16.png)
上のようなキーフレーム間を直線で補間する方法を線形補間と呼びます。
もちろん、補間の方法はこの一通りではありません。例えば、以下のようなものも考えられます。
![interp_cubic.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/323576/ee674a57-af3e-f0eb-042e-94a88cd4b4e3.png)

このように、キーフレームと補間の方法を定めてアニメーションをさせる方法をキーフレームアニメーションといいます。次にthree.js上での実装方法について説明します。

# three.jsでの実装
three.jsでキーフレームアニメーションをするにはKeyframeTrackオブジェクトを生成する必要があります。といっても直接KeyframeTrackクラスを呼び出す必要はなく、まず、以下のようにJSON形式で用意します。

```javascript
var positionKeyframeTrackJSON = {
    name: ".position",
    type: "vector",
    times: [0, 1, 2],
    values: [0, 0, 0, 2, 1, 15, 0, 0, 0]
}
```

上からわかるように、JSONにはname, type, times, values計4つのプロパティが必要になります。

nameにはアニメーションさせたいプロパティ名を指定します。今回は位置を動かしたいので”.position”ですね。詳しくはthree.jsのドキュメントの.parseTrackNameの説明を参照してください。

typeにはプロパティの型を指定します。positionはVector3型なので、’vector’とします。このtypeには’vector’の他にも’number’,’color’,’quaternion’,’boolean’,’string’などの文字列を使用することができます。詳しくはKeyframeTrackのソースコードの_getTrackTypeForValueTypeNameメソッドを参照してください。

timesとvaluesには時刻とそれに対応する値をそれぞれ配列で渡します。例えば今回は
times: [0, 1, 2],
values: [0, 0, 0, 2, 1, 15, 0, 0, 0]
としていますが、これは0秒の時に位置が(0,0,0)、1秒の時は(2,1,15),2秒の時は(0,0,0)であることを表しています。

```javascript
var rotationKeyframeTrackJSON = {
    name: ".rotation[y]",
    type: "number",
    times: [0, 2],
    values: [0, 2 * Math.PI],
    interpolation: THREE.InterpolateSmooth
}
```
次はrotationのｙ軸回転に関してアニメーションさせたいのでnameは”.rotation[y]”としました。これは単なるスカラーですのでtypeは”number”とします。ここで、新しく’interpolation’というプロパティがあることに気づかれたと思います。これは先ほど説明した補間の式を表します。これは現在、’THREE.InterpolateLinear’,’THREE.InterpolateSmooth’,’THREE.InterpolateDiscrete’の三種類から選ぶことができ、何も入力しないと自動的に’THREE.InterpolateLinear’になります。

以上のようにいくつかキーフレームトラックを用意したら以下のようにまとめてパースします。

```javascript
var clipJSON = {
    duration: 2,
    tracks: [
        positionKeyframeTrackJSON,
        rotationKeyframeTrackJSON
    ]
}
 
var clip = THREE.AnimationClip.parse(clipJSON)
```
これで、アニメーションクリップを作成することができました。(THREE.AnimationClip.parseメソッド内でKeyframeTrackオブジェクトが作成されています。)
あとは以下のようにAnimationMixerに登録してアニメーションループを回すことで冒頭のアニメーションを作ることができます。

```javascript
var mixer = new THREE.AnimationMixer(cube)
var action = mixer.clipAction(clip)
action.play()

animate()
function animate() {
 
    requestAnimationFrame(animate)
 
    mixer.update(0.01)
    controls.update();
    renderer.render(scene, camera);
 
}
```