【Three.js】Tweenで複雑なアニメーション
次はうまくやる・・・
はじめに
もっとおしゃれでカッコいいアニメーションができないものかと頭を悩ませていました。
そしたらTweenには"chain()"っていう関数があるそうじゃないですか。これを使うと2つのアニメーションをつなげる事ができるとか。さっそく使ってみました。
できた
デモサイトはこちら。
アニメーションのコード
function clickButton() {
TWEEN.removeAll();
// ボタンのイベントリスナー削除(アニメーションの途中で押されると挙動がおかしくなるため)
document.querySelector('.plus').removeEventListener('click', clickButton);
// 開始時のカメラの動き
var startCamera = new TWEEN.Tween(camera.position)
.to({x: camera.position.x, y: -200, z: camera.position.z}, 1000)
.easing(TWEEN.Easing.Quadratic.InOut)
.start();
// 終了時のカメラの動き
var endCamera = new TWEEN.Tween(camera.position)
.to({x: camera.position.x, y: 0, z: camera.position.z}, 1000)
.easing(TWEEN.Easing.Quadratic.InOut);
// 質問文を裏返す
var reverse = new TWEEN.Tween(qObj.rotation)
.to({x: Math.PI, y: qObj.rotation.y, z: qObj.rotation.z}, 1500)
.easing(TWEEN.Easing.Exponential.InOut)
.start();
// 質問文(裏側)を裏返す
var reverse2 = new TWEEN.Tween(qObj2.rotation)
.to({x: Math.PI * 2, y: qObj2.rotation.y, z: qObj2.rotation.z}, 1500)
.easing(TWEEN.Easing.Exponential.InOut)
.start();
// 尾翼を回転させる
var rotateTail = new TWEEN.Tween(pObj.rotation)
.to({x: Math.PI / 180 * 90, y: pObj.rotation.y, z: pObj.rotation.z}, 1000)
.easing(TWEEN.Easing.Exponential.InOut)
.start();
// 尾翼を移動させる(上)
var moveZTail = new TWEEN.Tween(pObj.position)
.to({x: pObj.position.x, y: pObj.position.y, z: 16}, 1000);
// 尾翼を移動させる(左)
var moveXTail = new TWEEN.Tween(pObj.position)
.to({x: 100, y: pObj.position.y, z: 16}, 1000);
// 右大翼を広げる
var moveZlwr = new TWEEN.Tween(lwrObj.position)
.to({x: lwrObj.position.x, y: lwrObj.position.y, z: -1}, 100);
var openlwr = new TWEEN.Tween(lwrObj.rotation)
.to({x: lwrObj.rotation.x, y: lwrObj.rotation.y, z: Math.PI / 180 * 60}, 1000);
// 左大翼を広げる
var moveZlwl = new TWEEN.Tween(lwlObj.position)
.to({x: lwlObj.position.x, y: lwlObj.position.y, z: -1}, 100);
var openlwl = new TWEEN.Tween(lwlObj.rotation)
.to({x: lwlObj.rotation.x, y: lwlObj.rotation.y, z: Math.PI / 180 * -60}, 1000);
// 右小翼を広げる
var moveZswr = new TWEEN.Tween(swrObj.position)
.to({x: swrObj.position.x, y: swrObj.position.y, z: -1}, 100);
var openswr = new TWEEN.Tween(swrObj.rotation)
.to({x: swrObj.rotation.x, y: swrObj.rotation.y, z: Math.PI / 180 * -100}, 1000);
// 左小翼を広げる
var moveZswl = new TWEEN.Tween(swlObj.position)
.to({x: swlObj.position.x, y: swlObj.position.y, z: -1}, 100);
var openswl = new TWEEN.Tween(swlObj.rotation)
.to({x: swlObj.rotation.x, y: swlObj.rotation.y, z: Math.PI / 180 * 100}, 1000);
// 離陸
var takeOff = new TWEEN.Tween(plane.position)
.to({x: -2000, y: plane.position.y, z: 1000},4000)
.easing(TWEEN.Easing.Exponential.In)
.onComplete(function() {
plane.position.x = 2000;
if (qElem.textContent == 'Q.テイクアウトはできますか?') {
qElem.textContent = 'A.できません';
pElem.textContent = '-';
} else {
qElem.textContent = 'Q.テイクアウトはできますか?';
pElem.textContent = '+';
}
});
// 着陸
var landing = new TWEEN.Tween(plane.position)
.to({x: 0, y: plane.position.y, z: 0},4000)
.easing(TWEEN.Easing.Exponential.Out);
// 右の大翼を閉じる
var openlwr2 = new TWEEN.Tween(lwrObj.rotation)
.to({x: lwrObj.rotation.x, y: lwrObj.rotation.y, z: 0}, 1000);
var moveZlwr2 = new TWEEN.Tween(lwrObj.position)
.to({x: lwrObj.position.x, y: lwrObj.position.y, z: -11}, 1000);
// 左の大翼を閉じる
var openlwl2 = new TWEEN.Tween(lwlObj.rotation)
.to({x: lwlObj.rotation.x, y: lwlObj.rotation.y, z: 0}, 1000);
var moveZlwl2 = new TWEEN.Tween(lwlObj.position)
.to({x: lwlObj.position.x, y: lwlObj.position.y, z: -11}, 1000);
// 右小翼を閉じる
var openswr2 = new TWEEN.Tween(swrObj.rotation)
.to({x: swrObj.rotation.x, y: swrObj.rotation.y, z: 0}, 1000);
var moveZswr2 = new TWEEN.Tween(swrObj.position)
.to({x: swrObj.position.x, y: swrObj.position.y, z: -11}, 1000);
// 左小翼を閉じる
var openswl2 = new TWEEN.Tween(swlObj.rotation)
.to({x: swlObj.rotation.x, y: swlObj.rotation.y, z: 0}, 1000);
var moveZswl2 = new TWEEN.Tween(swlObj.position)
.to({x: swlObj.position.x, y: swlObj.position.y, z: -11}, 1000);
// 尾翼を移動させる(右)
var moveXTail2 = new TWEEN.Tween(pObj.position)
.to({x: 135, y: pObj.position.y, z: 16}, 1000);
// 尾翼を移動させる(下)
var moveZTail2 = new TWEEN.Tween(pObj.position)
.to({x: pObj.position.x, y: pObj.position.y, z: 0}, 1000);
// 尾翼を回転させる
var rotateTail2 = new TWEEN.Tween(pObj.rotation)
.to({x: 0, y: pObj.rotation.y, z: pObj.rotation.z}, 1000)
.easing(TWEEN.Easing.Exponential.InOut);
// 質問文を裏返す
var reverse3 = new TWEEN.Tween(qObj.rotation)
.to({x: 0, y: qObj.rotation.y, z: qObj.rotation.z}, 1500)
.easing(TWEEN.Easing.Exponential.InOut)
.onComplete(function() {
document.querySelector('.plus').addEventListener('click', clickButton);
});
// 質問文(裏側)を裏返す
var reverse4 = new TWEEN.Tween(qObj2.rotation)
.to({x: Math.PI, y: qObj2.rotation.y, z: qObj2.rotation.z}, 1500)
.easing(TWEEN.Easing.Exponential.InOut);
/***** アニメーションをつなぐ *****/
// 離陸前
rotateTail.chain(moveZTail);
moveZTail.chain(moveXTail);
reverse.chain(openlwr, openlwl, openswr, openswl, moveZlwr, moveZlwl, moveZswr, moveZswl);
moveXTail.chain(takeOff);
// 離陸⇀着陸
takeOff.chain(landing);
// 着陸後
landing.chain(openlwr2, moveZlwr2, openlwl2, moveZlwl2, openswr2, moveZswr2, openswl2, moveZswl2, moveXTail2);
moveXTail2.chain(moveZTail2);
moveZTail2.chain(rotateTail2, endCamera);
rotateTail2.chain(reverse3, reverse4);
}
地味な作業をひたすら繰り返しています。翼の1枚1枚にTweenを使ってアニメーションを作っていって、最後に"chain()"でアニメーションをつなげます。
ポイントを解説します。
onComplete関数でアニメーションの終了をつかまえる事ができる
// 離陸
var takeOff = new TWEEN.Tween(plane.position)
.to({x: -2000, y: plane.position.y, z: 1000},4000)
.easing(TWEEN.Easing.Exponential.In)
.onComplete(function() {
plane.position.x = 2000;
if (qElem.textContent == 'Q.テイクアウトはできますか?') {
qElem.textContent = 'A.できません';
pElem.textContent = '-';
} else {
qElem.textContent = 'Q.テイクアウトはできますか?';
pElem.textContent = '+';
}
});
飛行機が離陸する"takeOff"という名前を付けたアニメーションです。(x:-2000, y:今と同じ位置, z:1000)の座標に4秒かけて移動します。
この中で使っている"onComplete()"はアニメーションが終わったときに実行される関数です。コレは便利ですね。なにやってるか紙に書いたのでそちらをご覧ください。
chain関数でアニメーション同士をつなげる事ができる
// 離陸⇀着陸
takeOff.chain(landing);
これで「"takeOff"アニメーションが終了したら"landing"アニメーションを開始してくださいねー」ということになります。
// 着陸後
landing.chain(openlwr2, moveZlwr2, openlwl2, moveZlwl2, openswr2, moveZswr2, openswl2, moveZswl2, moveXTail2);
“chain()"の引数には複数のアニメーションを指定することができます。この例だと"landing"が終了したら、引数で指定した9個のアニメーションが同時に開始されます。
そんな感じでアニメーションを組み立てていきましたが、途中でワケが分からなくなりました。右の翼の設定したつもりなのに左が動いてるとか、隠した翼が見えちゃうからz軸方向にも動かさないとダメじゃん!とかまぁ大変。
変数の名前が良くなかったのと、アニメーションを関数化できればもっと楽に作れたのかなぁと思います。でもそう思った時にはもう手遅れ。今から変数名変えたりとかイヤすぎるだろうが。なぜなら眠たいから。明日やれよって感じですが、コレを明日に持ち越すぐらいならこのまま突っ走ってしまおう、と。
そんなこんなで「とりあえずできた」感がすごくて、なんかモヤモヤ。次からは簡単な設計図みたいなの作ってからコード書くようにしようかなと思います。
あとがき
「完成させたい」という欲望に負けた結果、完成度の低いものができあがってしまいました。ちょっとスッキリしませんが、まぁまぁ面白いものは作れたかなぁと思います。予想外の動きで「おっ!?」って言わせるようなwebサイトを作ってみたいなぁと思う午前3時なのでした。ありがとうございました。
おしゃれ度
★★★☆☆
ディスカッション
コメント一覧
まだ、コメントがありません