EdgingMover

目次

はじめに

EdgingMover クラスは DHTML 用アニメ―ションクラスです。
macromedia FLASH のイージングという考えをもとに加速度を意識した直線アニメーションを実現します。
DHTML ではかなり重宝するクラスで再利用性の高いクラスでもあります。
イージングについては 補足 で説明します。

このクラスでは Dimension クラスPoint3 クラスClient クラスElement クラスを使用しています。

使用例

var element       = new Element( "LAYER" );
var startPosition = new Dimension( 100 , 100 );
var endPosition   = new Dimension( 300 , 500 );

// EdgingMover クラスのインスタンスを生成
var emover = new EdgingMover(
    element ,       // 対象レイヤー
    startPosition , // 始点
    endPosition ,   // 終点
    30 ,            // 分割数
    0               // イージング値
);

// この関数を実行すると、レイヤー LAYER が startPosition から、
// endPosition まで 30 ステップで等速移動(イージング値=0)します。
function move(){
    if( emove.step() ) setTimeout( "move();" , 50 );
}

インスタンス変数

Element element ;     // 対象レイヤー
Dimension sPosition ; // 始点
Dimension ePosition ; // 終点
number tmpCount ;     // ステップ数
number divCount ;     // 分割数
number edging ;       // イージング値
多いように思いますが、これらについてはコンストラクタ↓で説明しています。
divCount というのは分割数のことですが、これで何段階でアニメーションするかを決めます。

コンストラクタ

EdgingMover();
EdgingMover( Element target , Dimension endPosition , number count );
EdgingMover( Element target , Dimension startPosition , Dimension endPosition , number count );
EdgingMover( Element target , Dimension startPosition , Dimension endPosition , number count , number edging );
インスタンスを生成します。宣言の仕方は上の 4 つです。
何だかややこしいですね。これらの説明をする前に EdgingMover クラスの機能について説明しましょう。
EdgingMover クラスはレイヤーを直線移動させるためのクラスです。 JavaScript を使って移動させるために必要な情報は

動かそうとするレイヤー( target )、
移動しはじめる始点のX座標、Y座標( startPosition )、
どこまで移動するかという終点のX座標、Y座標( endPosition )、
そして、何段階( count )で移動するか

です。これらがアニメーションさせるために必要な情報です。

これに加え EdgingMover クラスは加速しながら移動するのか、減速しながら移動するのかといった、加減速を考慮した直線アニメーションを実現します。その加速、減速を決めるのがイージング値( edging )です。
イージングの値は -100 〜 100 の値で、この値が大きいほど減速、小さい程加速になります。 そして、イージング値が 0 のとき等速です。

では、コンストラクタの説明に戻ります。一番ややこしそうな
EdgingMover( Element target , Dimension startPosition , Dimension endPosition , number count , number edging );
はもう分かりますね。上述したアニメーションに必要な情報と edging 値を順に書いているだけです。

次に EdgingMover( Element target , Dimension startPosition , Dimension endPosition , number count ); は、上との違いは edging 値がないだけですね。 この場合、edging 値は 0 に設定されます。つまり等速アニメーションです。

次に EdgingMover( Element target , Dimension endPosition , number count ); です。
指定するのは 対象レイヤー(target)、終点(endPosition)、分割数(count) ですが、始点とイージング値がありませんよね。
この場合、始点は target で指定したレイヤーの現在位置となり、イージング値は 0 (等速)になります。

最後に引数なしの EdgingMover(); ですが、これはメンバ変数に何も設定されません。
変数の型等を明示的に示したいときや、値をあとで設定したいときに使用します。 この場合、各インスタンス変数はメソッド等で設定しなくてはいけません。

var element   = new Element( "LAYER" );
var sPosition = new Dimension( 100 , 200 );
var ePosition = new Dimension( 300 , 500 );

var emover0 = new EdgingMover();
var emover1 = new EdgingMover( element , ePosition , 50 );
var emover2 = new EdgingMover( element , sPosition , ePosition , 50 );
var emover3 = new EdgingMover( element , sPosition , eposition , 50 , 0 );

インスタンスメソッド

対象レイヤーの設定 : setElement
void setElement( Element target );
アニメーションさせる対象レイヤーを設定又は変更します。
var element = new Element( "LAYER" );
var emover = new EdgingMover( element , new Dimension( 100 , 100 ) , 50 );

emover.setElement( new Element( "ANOTHER_LAYER" ) );
始点、終点の設定 : setPosition
void setPosition( Dimension endPosition );
void setPosition( Dimension startPosition , Dimension endPosition );
始点と終点を設定又は変更します。
引数が一つの場合、始点はインスタンス変数で設定された対象レイヤーの現在位置になります。
これは重要なので覚えておいて下さい。
var element = new Element( "LAYER" );
var sPosition = new Dimension( 200 , 100 );
var ePosition = new Dimension( 400 , 500 );

var emover = new EdgingMover( element , sPosition , ePosition  , 50 );

var sPosition0 = new Dimension();
var ePosition0 = new Dimension( 100 , 200 );

emover.setPosition( sPosition0 , ePosition0 );
分割数の設定 : setDivCount
void setDivCount( number count );
何段階でアニメーションするかという分割数を設定又は変更します。
var element = new Element( "LAYER" );
var emover = new EdgingMover( element , new Dimension( 100 , 100 ) , 50 );

emover.setDivCount( 20 ); // 50 段階が 20 段階に変更されます。
イージング値の設定 : setEdging
void setEdging();
void setEdging( number edging );
イージング値を設定します。
イージング値は -100 〜 100 の範囲で、大きい値(100に近い)程減速、小さい値(-100に近い)程加速になります。
引数なしは 0(等速)に設定されます。
var emover = new EdgingMover();
emover.setEdging( -100 ); // 加速
移動 : step
boolean step();
このメソッドが移動させるために必要なメイン関数です。
今までどうやって移動させるのか説明しませんでしたが、とりあえず先ずはプログラムの例を見てみましょう。
// インスタンス生成
// コンストラクタの形式は EdgingMover( element , startPosition , endPosition , divCount )
// イージング値は設定しないので 0 ( 等速 )
var emover = new EdgingMover(
    new Element( "LAYER" ),     // 対象レイヤー
    new Dimension(  50 ,  50 ), // 始点
    new Dimension( 400 , 300 ), // 終点
    100                         // 分割数
);

// setTimeout を含める移動関数を作成
function move(){
    
    // step メソッドは呼び出すたびに対象レイヤーをワンステップ進める。
    // step メソッド自体は論理値を返す。
    // 終点についたとき step メソッドは false を返し、再帰を終了する。
    if( emover.step() ){
        
        setTimeout( "move();" , 10 ); // 移動は 10msec 間隔
        
    }
    
}

// 移動開始
move();
このプログラムを実行すると、レイヤー LAYER は ( 50 , 50 ) から、( 400 , 300 )まで、100 段階で等速アニメーションします。
どうなるかは説明するよりも実際に見た方が一目瞭然ですね。

この原理ですが、step 関数自体は一回呼び出せばワンステップ移動させるだけのメソッドです。
これを他の関数(上の例では move() )を再帰的に呼び出し、終点まで移動させるわけです。
内部ではインスタンス変数 tmpCount を毎回インクリメントしており、tmpCount < divCount が成り立つ間、位置座標を計算し対象レイヤーを移動させ、step 関数自体は true を返します。
tmpCount < divCount が成り立たなくなったとき、step 関数は false を返し、tmpCount を 0 に戻します。
標準出力文字列 : toString
string toString();
EdgingMover クラスの標準出力文字列を返します。
返される文字列は "EdgingMover : " + this.element.getID() です。
var emover = new EdgingMover( new Element("LAYER") , new Dimension(200,100) , 50 );

status = emover // 結果は EdgingMover : LAYER

クラスメソッド

再帰関数の省略 : auto
void auto( EdgingMover emover );
void auto( EdgingMover emover , number time );
僕が楽したいために作成したメソッドです(^^);
今まで移動させるのに、step とは別に再帰関数を用意していました。
これって結構だるくありませんか?
このクラスメソッドはその再帰関数を作成せずに移動させることが可能になります。

emover で対象となる EdgingMover オブジェクトを、time で時間間隔[msec]を指定します。 time を指定しない場合、50[msec]となります。
var emover = new EdgingMover( new Element("LAYER") , new Dimension(100,100) , 50 );

EdgingMover.auto( emover , 10 );
これもまたどうなるか実例で見てみましょう。
step メソッドのところで書いた クラスメソッド auto で書き換えた例

注:クラスメソッドなので、この方法で 同時に複数のレイヤーを移動させることは出来ません。

メソッド一覧

EdgingMover クラスで使用できるメソッドのリストです。
メソッド名説明
voidsetElement対象レイヤーの設定
voidsetPosition始点、終点の設定
voidsetDivCount分割数の設定
voidsetEdgingイージング値の設定
booleanstepワンステップ移動
stringtoString標準出力文字列
クラスメソッド
voidauto再帰関数の省略

補足

■ イージングとは?
イージングとはオブジェクトが移動するときのコマ割を決めてやるもので、これをインよりに多くとったり、アウトよりに多くとったりした結果、加速、減速したように見せます。
EdgingMover クラスの実際のイージング値 ( edging ) は FLASH と同じく -100 から 100 で指定できます。
この値が負の場合、インよりにコマ数が多くとられますので、見た目的には加速しているように見えます。そして正の値のときは逆でアウトよりにコマ数が多くとられるので減速するような効果が得られます。0 のときは等速です。
では、実際の例 を見てどの程度加減速が変わるのかを見てみましょう。
イージングの原理については DHTML > Mathematics > Edging で詳しく説明しています。
■ EdgingMover クラスの利点
EdgingMover クラスを利用するときちっと終点で止まります。加速、減速させるときこれは重要です。
何のことか分かりませんね(^^); 例えば、他の関数で加速させてみましょう。放物線的に加速させます。放物線の式は
y = a * Math.pow( x , 2 );
です。この考えを使いレイヤーを横方向に加速させ移動させます。 始点は 0 , 終点は 800 の位置としましょう。プログラムで書けば
var tmpCount =  0 ;
var divCount = 10 ; // 10 段階で加速

function move(){
    if( tmpCount < divCount ){
        var x = 10 * Math.pow( tmpCount++ , 2 );
        element.setPositionX( x );
        setTimeout( "move();" , 50 )
    }else{
        element.setPositionX( 800 );
    }
}
確かにこのプログラムを実行すれば、x = 800 の点で止まってくれます。でも、よく見てみましょう。
tmpCount = 9 のとき、element の位置はどこでしょう?
x = 10 * Math.pow( 9 , 2 ) = 810 です。
終点の 800 を飛び越えています。その次の瞬間には else より、800 に行きますが、この飛び越える又は足りないというのが実にキツイときがあります。これのために、いちいちブラウザのスクロールバーが表示されたり、見た目的に見苦しいものです。
このような単純な関数であれば逆算して、きちっと止まるようにもできますが(例えば上の例で 800 の位置にきちっと止めるためには 10 * のところを 9.9 ぐらいにすれば良いです)、イージングの考え方ではこのようなことは起きません。必ず終点の座標は終点です。
■ EdgingMover クラス豆知識
EdgingMover クラスで一つの関数を使い複数を動かしたいときがあるかも知れません。特に↓のような例です。
var emovers = new Array();

for(var i=0;Elements["LAYER"+i]!=null;i++){
    emovers[i] = new EdgingMover(
        Elements["LAYER"+i],
        new Dimension( 300 , 300 ),
        50
    );
}
何がしたいかというと LAYER+number と連番の付いたレイヤーを EdgingMover を使い、一つの点に集結させたり、任意の点に同時に移動させたいわけです。
この場合、どうするとスマートな move 関数を作成できるでしょうか?
一番手間がかかるのは下のようにした場合です。
function move0(){ if( emover[0].step() ) setTimeout( "move0();" , 10 ); }
function move1(){ if( emover[1].step() ) setTimeout( "move1();" , 10 ); }
function move2(){ if( emover[2].step() ) setTimeout( "move2();" , 10 ); }
function move3(){ if( emover[3].step() ) setTimeout( "move3();" , 10 ); }
function move4(){ if( emover[4].step() ) setTimeout( "move4();" , 10 ); }

function move(){
    move0();
    move1();
    move2();
    move3();
    move4();
}
分かりやすいけど、汎用性ゼロです。↓はどうだろう。
var moves = new Array( emovers.length );

for(var i=0;i<moves.length;i++) moves[i] = function(i){
    if( emovers[i].step() ) setTimeout( "moves["+i+"]("+i+")" , 10 );
}

function move(){
    for(var i=0;i<moves.length;i++) moves[i](i);
}
汎用性はあるけど何か複雑ですね。 簡単な方法は step メソッドが、boolean 値を返すということでそれを利用します。
var commonFlag = true ;

function move(){
    if( commonFlag ){
        for(var i=0;i<movers.length;i++) commonFlag = movers[i].step();
        setTimeout( "move();" , 10 );
    }
}
んじゃ、これも実際の例を

↓でも良いですが、少し不安定です。
function move(){
    if( movers[0].step() ){
        for(var i=1;i<movers.length;i++) movers[i].step();
        setTimeout( "move();" , 10 );
    }
}