JavaScript
3D
Web3D
Mithril.js
Solufa

Solufaを使って簡単にWebで3Dをアニメーションさせる[移動編]

More than 3 years have passed since last update.


導入

今回は簡易的なアニメーションを書いていきたいと思います。

Solufaを使って簡単にWebで3Dを扱ってみる(入門)

まずはこの記事の流れに沿って入門してみてください

なんとなくわかったなぁ、、、そろそろ動かしたいなあ、、、と感じると思ったらこの記事に目を通してみてください。


前回より

入門編からソースコードを持ってきます。

S(function(m) {

var App = {
controller: function() {
return {
geo: {
type: "Box",
value: [ 1, 1, 1 ]
},
mtl: {
type: "MeshPhong",
value: {
color: "#00f",
specular: "#999"
}
}
};
},
view: function( ctrl ) {
return <scene>
<obj>
<mesh geo={ ctrl.geo } mtl={ ctrl.mtl } style={{ rotate: [ 30*Math.PI/180, 45*Math.PI/180, 0 ] }}/>
</obj>
<cam id="cam" style={{ posZ: 10 }}/>
<light init={{ type: "Dir" }} style={{ pos: [ 0, 2, 0 ] }}/>
<light init={{ type: "Amb" }}/>
</scene>;
}
};
m.mount( S.document.body, App );
m.render( S.document.head, <rdr init={{ frame: "#solufa", antialias: true, preserveDrawingBuffer: true }}>
<OrbitVp cam="#cam"/>
</rdr> );
});

今回は少し遠目から見たいのでカメラのZ座標を10に設定します。


アニメーション

アニメーションを作成していきます。

アニメーションの定義はController内でします。

controller: function() {

return {
geo: {
type: "Box",
value: [ 1, 1, 1 ]
},
mtl: {
type: "MeshPhong",
value: {
color: "#00f",
specular: "#999"
}
}, //カンマを忘れずに
//ここにアニメーションを定義していきます。
};
},


上昇の実装

まずは上に移動するアニメーションを作ってみましょう。

translate:function(elem, isInitialized) {

if (isInitialized) return;
S.update(function(delta) {
elem.style.posY += delta * 1;
});
}

これでアニメーションは完成です。

ソースの解説は後ほど

次にViewに流し込みます。

Mithrilのconfig属性を使います。

config属性はDOM生成後にその要素に対してメソッドが呼べるすごいやつです。 

meshタグ内を以下のように書き換えます

<mesh geo={ ctrl.geo } mtl={ ctrl.mtl } config={ctrl.translate} style={{ rotate: [ 30*Math.PI/180, 45*Math.PI/180, 0 ] }}/>/>

config属性が追加されました。Viewの引数で持ってきたControllerのtranslateメソッドを呼んでいます。

以上でアニメーションが描画されるようになりました。

liftup.gif]


ソースの解説

translate:function(elem, isInitialized) {

まずtranslateというアニメーションの関数を定義します。

引数のelemは動かすオブジェクトを配列で取ってきています。

isInitializedは再描画されたらtrueになります。(今回は特に気にしないで大丈夫です)

if (isInitialized) return;

再描画されたらアニメーションを止めます。(今回は特に気にしないで大丈夫です)

S.update(function(delta) {

Solufaのアップデート関数を使って1フレームごとに動かします。

第一引数にdelta timeとってこれます。

delta timeは異なるfpsの端末でも同様な動きをするために使用します。

elem.style.posY += delta/1000 * 1;

delta/1000はミリ秒を秒に変換しています。


elem.style.posY += delta * 1;

こうすることで、どんなfpsの端末でも1秒に1ずつelem内のオブジェクトのstyleのX座標を1ずつ進むようになりました!

バージョンが新しくなるとともにdeltaの単位が秒に変更されました!

しかしこのままでは無限に上昇し続けてしまいます。

次に一定位置まで上昇したら下降し、それを繰り返すようにしましょう。


上昇してから下降を繰り返す実装

一定位置まで上昇したら下降し、それを繰り返させます。

translate:function(elem, isInitialized) {

if(isInitialized){
return;
}
var isLimit = false;
S.update(function(delta) {
if(!isLimit){
elem.style.posY += delta * 1;
if(elem.style.posY >= 1){
isLimit = true;
}
}else{
elem.style.posY -= delta * 1;
if(elem.style.posY <= -1){
isLimit = false;
}
}
});
}

ControllerはそのままでOKです。

このように動いたら成功です。

updown.gif]


ソースの解説

まずupdateの外に変数isLimitを定義します。

var isLimit = false;

isLimitはBoxの座標が1になったらtrueになって、-1になったらfalseにするようにします。

次にisLimitがfalseの間は1秒間に1進むようにします。

if(!isLimit){

elem.style.posY += delta * 1;

if(!isLimit)はif(isLimit == false)と同じことです。

さらにこのif文の中で Boxのy座標が1になったらisLimitをtrueにします。

if(!isLimit){

elem.style.posY += delta * 1;
if(elem.style.posY >= 1){
isLimit = true;
}
}

これでBoxのy座標が1になったときにisLimitがtrueになり、動作が止まるようになりました。

次にisLimitがfalseの時の処理を書きます。

if(!isLimit){

//さっきの処理
}else{
elem.style.posY -= delta * 1;
if(elem.style.posY <= -1){
isLimit = false;
}
}

このように書くとisLimitがfalse*ではない*とき、つまりtrueの時の処理がかけます。

下に動きたいのでBoxのy座標を1秒間に-1動かします。

これで全ての処理が書き終わりました。


最後に

簡単な移動アニメーションのみですが解説をしてみました。

posYの部分をposXやPosZなどに変えてみて色々作ってみてください!

なにかわからないところがあったらコメントにお願いします。