06 | 2009/07 |  08

  1. 無料サーバー

User forum-FC2BLOG-Info-Edit Template-Post-Edit-Upload-LogOut

CSSやJavascript自習の苦闘史を綴っていきたい。恐縮ですがJavascriptを有効にしてご覧ください。
2005年12月から社会問題も掲載!


スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

jquery.js におけるアニメーションコードの解読 ( 5 )

jQuery().animate() メソッド───その 2( 逐次実行と併行実行 )

このエントリイでは animate メソッドによって、複数のアニメーションを起動する 2 つの方法について考察します。複数アニメーションを、1 つずつ順番に起動する方法と、恰も同時に引き起こされているかのように並行して表示する─── こうした 2 つの異なるアニメ起動方法について、その差異をもたらすコード進行過程を考えてみます。

アニメーションの逐次起動はどのように行われるか?

【 opt.queue!==false ( opt.queue が true か undefined の時 )のコード進行 】

逐次実行アニメーションサンプル

本題に入る前に逐次実行アニメーションのサンプルを見てみます。

逐次実行アニメの一例

アニメの逐次実行とは、複数のアニメーションを 1 つずつ順番に実行することです。例えば、(1) 或るボックスを右に 200 ピクセル移動させ、(2) 背景色を変化させ、(3) 文字サイズ小さくしてからまた色を変更し、(4) 最後に文字サイズを大きくするアニメーションは以下のようになります。

なお、色は animate メソッドの操作対象外ですから、animate メソッドの引数である complete メソッドを利用してアニメートさせています。animate メソッドで作動させたアニメーションが終わる時に、色が変わるようにしたわけです。

 
逐次実行アニメ sample

上の逐次実行アニメーションのスタイルシートとスクリプトコードは以下のように作成しました。

■ スタイルシート
 #animTestIn722, #animTest2In722{
   width:200px;background-color:royalblue;
   border:2px white solid;font-size:1em;
   margin-left:100px;
 }

■スクリプトコード
var goSequential = function(){
 $("#animTestIn722").animate({marginLeft:"+=200px"},2000,
  function(){$(this).css({backgroundColor:"darkgreen"});})
  .animate({fontSize:"0.75em"},2000,
  function(){$(this).css({backgroundColor:"indigo"});})
  .animate({fontSize:"1.5em"},2000,function(){$(this).text("逐次実行アニメ終了")});
}
var backSequential = function(){
 $("#animTestIn722")
  .animate({fontSize:"0.75em"},2000,
   function(){$(this).css({backgroundColor:"darkgreen"});})
  .animate({marginLeft:"-=200px"},2000,
   function(){$(this).css({backgroundColor:"royalblue"});})
  .animate({fontSize:"1em"},2000,function(){$(this).text("逐次実行アニメ sample")});
}
var startSequential = function(){
 $("#animTestIn722").text("逐次実行アニメ sample")
  .css({backgroundColor:"royalblue",marginLeft:"100px",fontSize:"1em"});
}
var endSequential = function(){
 $("#animTestIn722").text("逐次実行アニメ終了")
  .css({backgroundColor:"indigo",marginLeft:"300px",fontSize:"1.5em"});
}
$("#btn1In722").click(function(){
 if ($(this).css("margin-left")!==100+"px") startSequential();
 goSequential();
});
$("#btn2In722").click(function(){
 if ($(this).css("margin-left")!==300+"px") endSequential();
 backSequential();
});
逐次実行アニメーションコードの解読

問題を単純化するために、まず、或るインスタンスから animate メソッドが初めて起動される場合を考えます。animate メソッドは非常に複雑なコード進行をするので、問題を単純化・細分化して考察する必要があるのです。

  1. animate メソッドの最初のサブルーチン speed メソッドが実行されると、opt.complete プロパティに、$(this).dequeue() と opt.old.call(this) の 2 つのメソッドが登録されます。なお、ここではあくまで登録が行われるだけで登録されたメソッドは実行されません。
  2. その後 $(this).queue メソッドが実行され、animate メソッドの唯一の引数である無名関数全体が、"fxqueue" 名で新たに関連づけられた配列に追加されます。これで関連づけ配列の要素は 1 つとなります。
  3. すると queue メソッドの定義により、queue メソッドによって当該配列に追加された当該無名関数が実行されます
  4. そしてこの無名関数が実行されて初めて、アニメーションを具体化するコードの履行が進み、そのアニメーションコード進行の最後段階で opt.complete メソッドが起動されると、opt.old に登録済みの、animeteメソッドの第 4 引数である callback 関数(すなわちアニメ終了後に起動することを命じられていた関数)が実行されます。
  5. このとき opt.old に登録済みのもう 1 つのメソッドである $(this).dequeue() メソッドは、引数がないので、インスタンスの要素毎に $.dequeue(this) が起動されます。その結果、各要素に既に "fxqueue" 名にて関連づけられている配列から、最初の要素が削除されます。この削除される最初の要素とは、2. で追加された関数そのものです。また $.dequeue メソッドが第 2 引数なしで呼び出されるため、関連づけ配列の 2 番目の要素があれば実行されますが、それは存在しないので、 $.dequeue(this) メソッドは、関連づけ配列の第一要素を削除する以外のことは何もしません。
  6. こうして $() インスタンスから起動された最初のアニメーションが終わります。

▲ToTop

次に、以上に続けて、同一インスタンスに 2 番目の animate メソッドが適用される場合を考えてみます。特に先行するアニメーションが未だ進行中の段階で、2 番目の animate メソッドが起動される場合を考えてみます。

アニメーションの逐次起動とは、このような場合でも、後のアニメーションが起動されないことなのですから...

  1. animate メソッドの最初のサブルーチン speed メソッドが実行されると、opt.complete プロパティに、$(this).dequeue() 及び opt.old.call(this) が登録されます。登録されるだけで実行されないことも含めて最初のアニメーションの場合と何ら変わりません。
  2. その後、$(this).queue メソッドが実行され、animateメソッドの唯一の引数である無名関数が関連づけ配列に追加されます。これも上と同一です。異なるのは、最初のアニメーションが作動中ですから、関連づけ配列に登録された無名関数はこの時点で 2 つとなります。まだ 5. による最初の要素の削除は起こっていないのです。するとこの時点では関連づけ配列の要素数は 2 以上となるので、$(this).queue メソッドの定義から 2 番目の無名関数の実行は引き起こされず、$(this).queue メソッドは関連づけ配列に 2 番目の無名関数を追加しただけでその役割を終えます。
  3. こうして、2 番目の無名関数、すなわち 2 つめのアニメーションを実行する関数は「待機」状態となります。
  4. さて、その待機関数が実行されるのは、最初のアニメーションが終わろうとする段階です。それは上の 5. の段階です。この段階では、関連づけ配列の最初の要素(つまり最初の無名関数)が削除されます。しかしここでは、既に 2 番目の animate メソッドが起動済みで、2 番目の無名関数が関連づけ配列に登録済みとなっています。従って、最初の要素が削除された後の関連づけ配列には、1つの要素=「2 番目に追加された無名関数」が存在しています。そして $.dequeue(this) メソッドは、関連づけ配列の要素数が 1 の場合にはその要素を実行します
  5. こうして、2 つめのアニメーションを引き起こす無名関数が起動され、2 つめのアニメーションが引き起こされます。このようにして、前のアニメーションが終わってから、次のアニメーションが励起されるのです。

▲ToTop

アニメーションの並行起動はどのように行われるか?【 opt.queue===false の時 】

併行実行アニメーションサンプル

本題に入る前に併行実行アニメーションのサンプルを見てみます。

併行実行アニメの一例

アニメの併行実行とは、複数のアニメーションをほぼ同時に実行することです。例えば、(1) 或るボックスを右に 200 ピクセル移動させながら、(2) 併行して文字サイズ大きくするアニメーションは以下のようになります。

但し、そもそも色は animate メソッドの操作対象外ですから、色は併行起動できません。animate メソッド内の complete メソッドを利用して逐次起動せざるを得ません。

 
併行実行アニメ sample

上の併行実行アニメーションのスクリプトコードは以下のように作成しました。(スタイルシートは上の逐次起動のものと同一であり、そちらに記載してあります。)

■ スクリプトコード
var goSidebyside = function(){
 $("#animTest2In722").animate(
  {marginLeft:"+=200px"},{queue:false,duration:2000,easing:"swing",
   complete:function(){$(this).css({backgroundColor:"darkgreen"})}})
  .animate({fontSize:"1.5em"},2000,function(){$(this).text("併行実行アニメ終了")});
}
var backSidebyside = function(){
 $("#animTest2In722")
  .animate({fontSize:"1em"},{queue:false,duration:2000,
   complete:function(){$(this).css({backgroundColor:"royalblue"})}})
  .animate({marginLeft:"-=200px"},2000,
   function(){$(this).text("併行実行アニメ sample")});
}
var startSidebyside = function(){
 $("#animTest2In722").text("併行実行アニメ sample")
  .css({backgroundColor:"royalblue",marginLeft:"100px",fontSize:"1em"});
}
var endSidebyside = function(){
 $("#animTest2In722").text("併行実行アニメ終了")
  .css({backgroundColor:"darkgreen",marginLeft:"300px",fontSize:"1.5em"});
}
$("#btn3In722").click(function(){
 if ($(this).css("margin-left")!==100+"px") startSidebyside();
 goSidebyside();
});
$("#btn4In722").click(function(){
 if ($(this).css("margin-left")!==300+"px") endSidebyside();
 backSidebyside();
});
併行実行アニメーションコードの解読

解説書には「同時起動」と言う表現もありますが、以下に見るように複数のアニメーションを全く同時に引き起こせる訳ではありません。「先行するアニメーションが終わる前に、次のアニメーションを起動する」意ですから、正確には「並行」起動というべきでしょう。

さて、queue プロパティに何も指定しなければアニメーションは 1 つずつ逐次起動されますが、並行起動はユーザーが opt.queue の値を意図的に false としない限り引き起こせません。ここに opt オブジェクトの queue プロパティ指定は次のように行います。

animate メソッドの第 2 引数 speed を { queue:false, duration:nn, easing:"xyz" } のようなオブジェクトとします。この方法によってのみ opt.queue を false に指定することが出来ます。

  1. この場合、speed メソッド内において opt.complete メソッドに登録されるメソッドは、opt.old.call(this) だけであって、$(this).dequeue() は登録されません。
    また、speedメソッド実行後に起動されるイテレータは $().queue メソッドではなく、$().each メソッドです。
  2. こうして、each メソッドが無条件で無名関数を実行しアニメーションが引き起こされます。
  3. そして当該アニメーションの最後の段階で opt.complete メソッドが起動されて、animate メソッドの第 4 引数に記述された callback 関数が実行され、アニメート終了後の何らかの操作が行われます。
  4. 次に同一インスタンスに登録された 2 つめの animate メソッドが、1 つ目の animate メソッドによるアニメーションが終わる前に起動された場合を考えます。この場合には。直上の 1. ~ 3. の過程が新しい animete メソッド内で進行します。すなわち dequeue メソッドは登録されず、たとえ先の animate メソッドが起動中であっても淡々と 2 つ目の無名関数が実行されます。こうして 1 つ目のアニメーションに並行して 2 つ目のアニメーションが引き起こされる訳です。
スポンサーサイト

jquery.js におけるアニメーションコードの解読 ( 4 )

愈々、jquery アニメーションの中核をなす animate メソッドを解読します。なお、このメソッドは多くのサブルーチンを階層的に伴うので、その階層構造に見合った構成で記述します。

jQuery().animate(prop, speed, easing, callback)───その1

引数:順にアニメ用CSSオブジェクト、アニメ継続時間、easing、アニメ終了時の実行関数。但し、speed をオブジェクト形式で与える場合には第 3、第 4 引数はなし。

返値:jQueryインスタンス

機能:animateメソッドは言うまでもなく、アニメーションの中心的役割を果たすメソッドで、多くのサブルーチンを利用してアニメーションを実現します。

以下順にそのサブルーチンを見ていきます。

jQuery.speed(speed, easing, fn) クラスメソッド( 起動元は#3866 )

最初に、animate メソッドの引数をチェックし確定し、返値となるオブジェクトのプロパティを整理する jQuery.speed メソッドが登場します。このメソッドでは3つの引数の順番がどのように与えられても、そのデータ型などから内容が判断され、適正に処理されます。

引数:引数は全て animate メソッドから渡されます。

返値:オブジェクトで次の 5 つのプロパティを持ちます。 old、complete、queue、duration(= speed)、easing。この返値オブジェクトは変数 optall に代入されます。

機能:アニメーションの第 1 段階の情報整理を行います。

duration

ここにアニメーション継続時間 duration 値は 4 つのケース別に設定されます。

第 1 は jQuery.fx.off が true の時です。このときには duration 値がゼロになり、アニメーションは瞬時に履行されます。

一旦 jQuery.fx.off = true とすると、opt.duration 値がゼロになるので、同じ要素に登録されたそれ以降に起動される予定のアニメーションは、たとえ各々の継続時間がどのように指定されていようとも、全て duration 値はゼロとなり、全て瞬時に起動されます。言い換えれば全てが想定したアニメ後の状態になります。

なお、 jQuery.fx.off = true の指定は、アニメの継続時間をゼロにするのであって、アニメそのものを停止するわけではありません。アニメーションを停止させるメソッド $().stop() との差異に留意する必要があります。

jQuery.fx.off の値は引数などから与えられないので、ユーザーが外挿します。

第 2 は引数 speed が数値型の時です。このときには speed 値がそのまま opt.duration 値となります。( opt は speed メソッド内のローカル変数。以下同様)

第 3 は引数 speed が数値型でもオブジェクト型でもない場合です。この場合 speed が "slow" ならば 600 が、"fast" ならば 200 が、speed がこれらのいずれでもない場合には 400 が、それぞれ opt.duration に代入されます。(#3976~3977)

第 4 は、speed がオブジェクト型の時です。この場合には当該オブジェクトのプロパティである duration のプロパティ値が duration の値となるだけではなく、この形式で引数を指定する事によって初めて、併行起動のための指定( options.queue = false )やアニメーション途中で何かを行わせるメソッドの指定( options.step メソッド)が可能となります。

アニメ完了後に実行する関数を登録し、実行する

speed メソッドの 1 つの重要な作用として、アニメ終了後に起動するメソッドを指定する役割があります。

speed メソッドは #3980~3986 において、opt.old に animate メソッドの第 4 引数 callback を代入し、opt.complete に「 opt.queue が 未定義又は true ならば(つまりアニメを逐次起動する場合)dequeueメソッドを起動し、opt.old が関数ならばそれを実行する 」関数を登録します。

こうすることにより、opt.complete が実行されると 2 つ又は 1 つののメソッドが起動されます。dequeue メソッドが起動される場合には、当該要素に登録されている次の アニメーションが起動され、opt.old に登録されている callback( 最初のアニメーション終了後に起動するよう登録されたもの )が関数ならば、それが起動されます。

アニメを逐次起動するか、併行起動するか

更に重要なポイントがあります。それはアニメの逐次起動か、並行起動かの指定がここで行われていることです。animate メソッドの第 2 引数 speed をオブジェクト形式で与え、そのプロパティ queue をユーザーが false とします。この場合には第 3 及び第 4 引数は不要です。すると 3983 行の jQuery(this).dequeue() は opt.complete メソッドに登録されず、かつ jQuery(this).queue() ではなく jQuery(this).each() メソッドが起動されます。

これらの結果、、同一インスタンスの同一要素に登録されている別々のアニメーションの起動は相互に抑止されず、コード進行に応じて複数のアニメーションが並行的に起動されます。

他方、 opt.queue を 指定しないか true とすれば、3983 行の jQuery(this).dequeue() が opt.complete メソッドに登録され、かつ jQuery(this).each() ではなく jQuery(this).queue() メソッドが起動されます。

すると、同一インスタンスの同一要素に複数の animate メソッドが登録されている場合において、先行する アニメーション用メソッドが終わり opt.complete が呼び出され、その中にある jQuery(this).dequeue() が起動されるまでの間は、後に続く アニメーション用メソッドは起動を抑止され続けます。そして先行するアニメーション用メソッドによるアニメーションが終わり jQuery(this).dequeue() が実行されたその後に、後続するアニメーション用メソッドの内、最初に位置するそれが起動され、当該アニメーションが開始されます。

このように speed クラスメソッドと each または queue インスタンスメソッドの組み合わせによって、アニメの逐次起動か並行起動かが分岐処理されます。

▲ToTop

$().each 又は $().queueメソッド(起動元は#3868)

引数:1 つの function

返値:jQuery インスタンス

機能:このメソッドはアニメーションコード全体の 1 つの根幹的位置を占めています。インスタンスの個々の要素毎、ここのプロパティ毎に、それぞれ初期値、終了値、定期的変動値、経過時間等々を設定し、操作します。

jQuery.speed メソッドの返値を受け取った変数 optall の queue プロパティ値によって、処理が分岐されます。( なお、animate メソッドの第 2 引数 speed をオブジェクト形式で与え、そのプロパティ queue をユーザーが与えない限り queue===true に指定したことと同意になります。queue をユーザーが与えた場合には、speed メソッドの返値オブジェクト optall のプロパティに queue が設定されます。)

optall.queue プロパティが false の場合には each インスタンスメソッドが起動され、引数である唯一の関数が無条件に実行されます。他方、optall.queue プロパティが未定義か true の場合には、queue インスタンスメソッドが起動され、その時点で当該要素に関連づけられている待ち行列配列が空ならば、引数である関数が実行され、配列が空でなければ、当該関数が配列の要素に追加されます(追加された関数はこのときには実行されません)。

このようにしてアニメーションの同時並行的実行と、順次実行がコントロールされることになります。

each 並びに queue インスタンスメソッドの定義から、いずれの場合もインスタンスに登録されている各要素ノードを対象にして、巡回処理を行います。以下はこの関数の処理内容です。

▲ToTop

1. ローカル変数定義(#3870-3872)

第一引数 prop を処理するために必要なローカル変数を定義します。(1) optall から未定義プロパティを除いたプロパティを持つ opt、(2) hidden 属性の有無を登録する hidden、(3) jQuery インスタンスに格納されている要素ノードを指し示す self などが定義されます。

2. 第一引数であるpropオブジェクトの走査その1(#3875-3877)

第一引数 prop はオブジェクト形式で与えます。そのプロパティ値が hide で、変数 hidden が存在する場合(つまり当該要素は隠蔽済みとなっている)、あるいは、プロパティ値が show で変数 hidden が存在しない場合(つまり当該要素は表示済みとなっている)には、opt.complete に登録されている関数を実行します。

これは hidden 変数の値からアニメ処理が完了したと見なして、アニメ完了後のメソッドを起動するものです。

なお、opt.complete には queue 値に応じて 2 つまたは 1 つの関数が登録されていること、これによりアニメの同時並行進行か、逐次進行かが左右されることを改めて思い出すべきでしょう。(詳細は speed メソッドの項を参照)

3. 第一引数であるpropオブジェクトの走査その2(#3878-3889)

プロパティ名が height か width の場合で、当該要素が style 属性を有する場合、opt.displayプロパティに style.display 値または算出 display 値を代入し、また、style.overflow 値を opt.overflow に代入します。これらの操作は、当該要素の display プロパティと overflow プロパティの初期値を、opt オブジェクトに記憶させるもので、アニメ完了後にこれらのプロパティを初期化するときに使用します。

ここにおいて留意すべき点は、後者は styleオブジェクトのプロパティ値に限定されますが、前者は算出スタイル値の場合もあるということです。算出値と定義値との使い分けと区別は、以後にもたびたび登場しますが、animate メソッドを理解する上で重要なポイントです。

代入された opt.overflow 値が null 値でなければ、つまり overflow 値に何らかの値が定義されていれば、要素ノードの style.overflow 属性値を hidden とします。これはIE6以前のバージョンに対するバグ対策です。( 参照 → 要素のはみ出しの処理の不具合(IE/overflow):スタイルシート(CSS)辞典 - HTMLタグボード

4. prop オブジェクトの複写・待避(#3890-3891)

prop オブジェクトの未定義以外のプロパティを、opt オブジェクトのプロパティ curAnim に複写待避します。これは初期値を保存する処置であり、アニメ終了時にここで複写された値が利用されます。

▲ToTop

5. propオブジェクトをそのプロパティ毎に走査しアニメーションを描画する(#3892-3925)

jQuery インスタンスの各要素を巡回処理する中で、それぞれの要素毎にその prop オブジェクトのプロパティを巡回処理します。つまり二重の巡回処理を行うのですが、このシーンで、愈々 jquery アニメーションの肝とも言うべき jQuery.fx クラスが登場します。

5-1. jQuery.fx インスタンスの生成(#3892)

jQuery.fx クラスのインスタンス e を作り、その各プロパティを利用してアニメーションを実現しますが、まず fx クラスの定義(#4002-4009)により、次の 3 つの引数が e のプロパティとなります。

(1) e.elem = self(各要素)、(2) e.options = opt(duration、easing、old、complete、display、curAnim、及び queue の各プロパティから成るオブジェクト)、(3) e.prop = name(propオブジェクトのプロパティ名)です。

これらの 3 つのプロパティの他に、fx インスタンスには、#4013-4149 において prototype オブジェクトが定義され、update、cur、custom、show、hide、step の各メソッドが付加され、更に #4151-4171 において speeds と step というクラスプロパティも設定されます。これらについては後述しますが、同一の単語が異なる意味で使われたり、メソッド内で別のメソッドが呼び出される多重構造になっているため、jQuery.fx インスタンス e は非常にわかりにくくなっています。

5-2. propオブジェクトのプロパティ巡回の中で、その値が toggle、show または hide の場合の処理(#3895-3896)

(1) そのプロパティ値が toggle で当該要素に hidden 属性があれば、e.show(prop) メソッドが実行され、(2) そのプロパティ値が toggle で当該要素に hidden 属性がなければ、e.hide(prop) メソッドが実行され、(3)そのプロパティ値が show ならば e.show(prop)を、hideならば e.hide(prop) が実行されます。

なお、ここで登場する e インスタンスの 2 つのメソッド、e.show(prop)、e.hide(prop) については後述します。

5-3. prop オブジェクトのプロパティ巡回の中で、その値が toggle、show 及び hideでない場合の処理(#3897-3919)

このブロックでは、prop の値が数値で指定されている場合、または toggle、show、hideでも数値でもない場合(具体的にどのようなプロパティ値の場合がこれに該当するのかは解明できていません)の対応が記述されています。

まず、数値指定の場合には、算出スタイル値を変数 start に、prop オブジェクトのプロパティ値を変数 end に、それぞれ単位付きで登録します。ここで初期値と終了値が登録されます。px 以外の単位指定がされている場合には、指定された単位表現に叶う値に start 値が換算され、相対加減値指定(+= や -=)の場合にも対応しています。

こうして得られた start、end 及び unit の 3 つの引数を伴って e.custom メソッドが起動されます。

また prop の値が数値でもない何らかの値(valとする)の場合には、start( #3899 により算出スタイル値が入っているはず )、val 及び空文字をの 3つを引数として e.custom メソッドが起動されますが、具体的にどのようなプロパティがこうしたケースに該当するのか未解明です。

■ $().animate メソッド
3865: animate: function( prop, speed, easing, callback ) {
     // speed メソッドの返値 (object) を optall に代入する。
3866:  var optall = jQuery.speed(speed, easing, callback);
3867:  // queue が false ならば each メソッドを、さもなければ queue メソッドを実行
3868:  return this[ optall.queue === false ? "each" : "queue" ](function(){
3869:   // 変数定義 opt には optall のプロパティの内未定義ではないものが代入される。
3870:   var opt = jQuery.extend({}, optall), p,
       // イテレートの都度 hidden 属性の有無を hidden に登録する。
3871:    hidden = this.nodeType == 1 && jQuery(this).is(":hidden"),
3872:    self = this;
3873: 
3874:   for ( p in prop ) { // prop オブジェクトを走査
       // プロパティ値が hide で hidden が true、または
       // プロパティ値が show で hidden が false ならば
3875:    if ( prop[p] == "hide" && hidden || prop[p] == "show" && !hidden )
        // opt オブジェクトの complete メソッドを呼出しを返す。
3876:     return opt.complete.call(this);
3877:    // プロパティ名が height か width で style 属性があれば
3878:    if ( ( p == "height" || p == "width" ) && this.style ) {
3879:     // Store display property
        // opt.displayプロパティに display 値を代入する。
3880:     opt.display = jQuery.css(this, "display");
3881:
3882:     // Make sure that nothing sneaks out
3883:     opt.overflow = this.style.overflow; // overflow 属性値を代入する。
3884:    }
3885:   }
3886:
3887:   if ( opt.overflow != null ) // nullでなければ
3888:    this.style.overflow = "hidden"; // overflow 値を hidden とする。
3889:   // 引数 prop の未定義値でないプロパティを opt.curAnim に代入する。
3890:   opt.curAnim = jQuery.extend({}, prop);
3891:   // prop オブジェクトのプロパティ毎に走査
3892:   jQuery.each( prop, function(name, val){
       // $.fx クラスのインスタンス e を作る。
3893:    var e = new jQuery.fx( self, opt, name );
3894:    // プロパティ値に toggle、show、又は hide がある場合には
3895:    if ( /toggle|show|hide/.test(val) )
        // val が toggle で hidden が true ならば e.show(prop) を実行し、
        // val が toggle で hidden が false ならば e.hide(prop) を実行し、
        // val が toggle でないならば e.val(prop) を実行する。
3896:     e[ val == "toggle" ? hidden ? "show" : "hide" : val ]( prop );
3897:    else { // プロパティ値に toggle、show、及び hide がない場合には
        // valを文字列に変換してから match メソッドで調べ、最初に見つかる
        // +=数値 又は -=数値 、あるいは数値を変数 parts に代入する。
3898:     var parts = val.toString().match(/^([+-]=)?([\d+-.]+)(.*)$/),
          // e.cur(true) 実行結果があれば、変数 start にそれを代入する。
          // ここに e.cur(true) は、jQuery インスタンスに登録されて
          // いる各要素が持つ、当該プロパティ名のタグ属性値または
          // カレントスタイル値を px 単位で取得するメソッドである。
3899:       start = e.cur(true) || 0; // なければ 0を代入する。
3900:
3901:     if ( parts ) { // parts が存在すれば
3902:      var end = parseFloat(parts[2]), // /[\d+-.]+/ の値を変数 end に 代入。
3903:        unit = parts[3] || "px"; // 単位名称を 変数 unit に代入。
3904:      // start 値を所定の単位に基づく値に変換する。
3905:      // We need to compute starting value
3906:      if ( unit != "px" ) { // 単位名が px でない場合
          // 数値と単位名を連結して end 値に対応する name スタイル値を設定する。
3907:       self.style[ name ] = (end || 1) + unit;
          // 所定単位による end値 を px 単位による end 値【e.cur(true)】で除し、
          // その比率に px 単位による start 値を乗じて所定単位の start 値を得る。
3908:       start = ((end || 1) / e.cur(true)) * start;
          // 所定単位値で name スタイル値を設定する。
3909:       self.style[ name ] = start + unit;
3910:      }
3911:
3912:      // If a +=/-= token was provided, we're doing a relative animation
3913:      if ( parts[1] ) // 相対加減値が指定されている場合には
          // start 値に end 値を加減する。
3914:       end = ((parts[1] == "-=" ? -1 : 1) * end) + start;
3915:
3916:      e.custom( start, end, unit ); // custom メソッドを起動
3917:     } else // parts が存在しない場合
3918:      e.custom( start, val, "" ); // end 値は val で、unit はなしでcustom起動
3919:    }
3920:   }); // each メソッド終了
3921:
3922:   // For JS strict compliance
3923:   return true;
3924:  }); // #3868 の each or queue メソッド終了
3925: },

jquery.js におけるアニメーションコードの解読 ( 3 )

このエントリイでは、jquery アニメーションで活用される基本的な次の 5 つの関数やメソッドを解読します。

genFx、show、hide、toggle、fadeTo

genFx( type, num )

引数:第 1 引数の type には show 、hide 又は toggle を代入し、第 2 引数 num には 1~3 を代入して利用します。

返値:オブジェクト

機能:配列 fxAttrs からプロパティ名を CSS style 属性名、プロパティ値を type とするオブジェクトを生成します。

この関数は、要素を 「非表示 → 表示へ」、「表示 → 非表示へ」、「表示/非表示の循環」とアニメートさせるために、animate メソッドの第 1 引数である prop オブジェクトを作成するものです。CSS スタイル値を toggle、show、hide とするので最初は違和感があります。しかし、アニメーションの全容を理解すれば、この関数の必要性や重要性が理解できます。

■genFx( type, num ) 関数コード解読
3765:var elemdisplay = {}, // トップレベルで 3 つの変数を定義する。
3766: timerId,
3767: fxAttrs = [
3768:  // height animations
3769:  [ "height", "marginTop", "marginBottom", "paddingTop", "paddingBottom" ],
3770:  // width animations
3771:  [ "width", "marginLeft", "marginRight", "paddingLeft", "paddingRight" ],
3772:  // opacity animations
3773:  [ "opacity" ]
3774: ];
3775:
3776:function genFx( type, num ){
3777: var obj = {};
    // each 適用対象は num==1 ならば ["height",・・・,"paddingBottom"]、
    // num==3 ならば ["height",・・・,"paddingBottom","width",・・・,"opacity"] となる。
3778: jQuery.each( fxAttrs.concat.apply([], fxAttrs.slice(0,num)), function(){
3779:  obj[ this ] = type; // each 適用対象ごとに type 値を代入し、
3780: });
3781: return obj; // obj={ height:type, marginTop:type, ・・・} となる。
    // つまり、返値は CSS style 属性名をプロパティ名とし、
    // type をプロパティ値とする複数のプロパティからなるオブジェクトとなる。
3782:}

▲ToTop

jQuery().show(speed,callback)

引数:アニメの継続時間、アニメ終了後に起動させるcallback 関数

返値:jQuery インスタンス

機能:言うまでもなく jQuery インスタンスに登録されている要素を表示させるメソッドです。

要素の display プロパティ値は、block だけではなく inline やその他の指定もあります( inline-block、table、inline-table 等々 )。ですから、「このメソッドは引数を与えなければ、単に display="block" とするだけの単純な関数だろう」との推測は、見事に裏切られてしまいます。

コードは2つの部分から成り、引数がある場合には animate メソッドが起動されますが、その解説は animate メソッドの項に譲り、ここでは引数がない場合の show メソッドの進行を跡付けます。

show メソッドの要点

第一は、hide メソッドも同様ですが、当該メソッドが同一インスタンスに対して複数回呼び出された場合の対応についてです。2 度目以降の呼び出しに対して省力化の仕組みが組み込まれており、そのことやその意義を理解する必要があります。

第二は、display プロパティについて、その算出( IE の場合には「カレント」と呼ぶことが多い)スタイル値と style オブジェクトのプロパティ値の区別を理解しなければならないということです。明示的なスタイル設定と暗黙裏に設定されるスタイル値は別物であることを踏まえなければなりません。

▲ToTop

以上のことを踏まえてコード進行を見てみます。

或る jQuery インスタンスに初めて show メソッドを適用する場合
  • あるインスタンスに対して初めて show メソッドを適用すると、jQuery.data メソッドの定義から、この時点では olddisplay 名で関連づけられた情報は存在しないので、style.display に空文字が代入されます(#3790-3792)。つまり this.style.display プロパティ値を初期化します。
  • 次に、jQuery.css メソッドにより当該要素の算出(カレント)display スタイル値を検証し、それが none だったら(#3794)トップレベル変数である elemdisplay オブジェクトの、当該要素のタグネーム名のプロパティ値の有無を調べます。この時、同一名称のタグに対して過去に show メソッドが呼び出されていなければ、このプロパティは存在していませんから、3800 行以下が履行されます。
    • 3800 行から3808 行では、show メソッドが対象としているタグ要素と同じ名称のタグを当該サイト内の body 部に追加し、そのカレントスタイル値を取得し、その値が none 以外ならばそのプロパティ値を、他方 none ならば "block" を、elemdisplay[ tagName ] に代入します。(#3808)
    • こうして elemdisplay[ tagName ]プロパティ値を利用するか、あるいはそれに none 以外のプロパティ値を代入した後に、インスタンスの各要素に olddisplay 名で display 値を関連づけます(#3811)。
    • ここに変数 display 値は、elemdisplay 値が存在した場合、すなわち同じタグ名称の他の要素に対して show メソッドが適用済みの場合(当該インスタンスタンス内の他の同一名称のタグに適用済みの場合を含む。対象としたインスタンス内のタグ名称が全て同じ場合には、2巡目以降の場合がこれに該当する)には、elemdisplay 値になります。
    • 他方、elemdisplay が存在しなかった場合には、当該サイト内において今対象としているタグ要素に暗黙裏に設定されている display スタイル値が、none ならば block に、none 以外であれば当該値( block、inline等々 )となります。
  • こうして当該要素にそれに相応しい display 値が与えられ、ブラウザがそれにより当該要素を描画し、ディスプレイ画面に表示させます。

▲ToTop

同じ要素に対して二度以上の show メソッドを適用した場合

次に、同じ要素に対して二度以上の show メソッドを適用した場合を跡付けます。

この場合には、既に一度目の show メソッド適用によって、変数 elemdisplay オブジェクトの当該要素と同一のタグネームプロパティ値が none 以外となっており、かつ当該要素には olddisplay 名で none 以外の display 属性値がテキスト文字列で関連づけられています。

この結果、3792 行により当該要素の style オブジェクトの display 値が none 以外の値に設定され、更に constant reflow を避けるために設けられた 3817 行により、再度 style.display 値が none 以外の block 等となります。

なお、constant reflow を直訳すれば「定期的な再流」ですが、これでは何のことか皆目分かりません。ネット検索してみましたが未解明です。

show インスタンスメソッドが複雑なコードになっている理由

ところで、当初は show メソッドは単純に if (this[i].style.display=="none") this[i].style.display="block" とするだけで用が足りると思っていましたから、どうしてこのように複雑なコードになっているのか、その理由をここで考えてみたいと思います。

まず、elemdisplay オブジェクトの存在意義です。どうしてトップレベルの変数に、対象としている要素と同一のタグネームプロパティを設け、その値を none 以外とするのでしょうか。あたかも同一タグ名称の要素の display プロパティ値を、1 つに統一する必要があるかのようです。

例えば、ある p タグの n 番目の要素( p(n)と名付ける )を show メソッドで表示させたとします。すると elemdisplay.p = "block" となり、p タグの n 番目以外の要素( p(m)と呼ぶ )に show メソッドを適用する場合には、#3799~3809 がスルーされ、#3811 で olddisplay 名で文字列 block が p(m) に関連づけられ、最後に #3818 によって p(m) の display 値が block となり、ブラウザ上に p(m) 要素が表示されます。

append 行為を同一名称の要素に対して 2 度以上引き起こさせない効率化が目的なのかもしれません。但し、それ以外の目的があるかどうか、また、あるとすれば何かについては解明できていません。

次に、同一タグ名の要素を わざわざ append するのは何故でしょうか。

それは次のような理由によると考えられます。

body に対象要素と同一タグ名称の要素を追加し、その算出(カレント) display 属性を調べれば、そのサイトにおいて、当該タグがどのような display 値をとっているのか把握することが出来ます。この方法によって、当該タグがサイト内で有する算出(カレント)display 値を取得することにしたのでしょう。

これに対して、olddisplay 名による要素への none 以外の文字列の関連づけは、分かりやすいものです。jQuery.data メソッドを利用するのは、一度当該要素の display 属性値を none 以外に登録した要素に対して、二度目以降はこの登録済み文字列を利用して容易に display 値を同じ値に設定するためです。show メソッドにおいても hide メソッドにおいても、jQuery.data メソッドがしっかり活かされています。

■$().show メソッドコード解読
3785: show: function(speed,callback){
3786:  if ( speed ) { // 引数 speed が定義されていれば
      // 「genFx("show",3 )を CSS スタイル値、speed をアニメ所要時間とし、
      // callback 関数をアニメ終了後に起動する animateメソッドを、
      // jQuery インスタンスから呼び出して実行し、その返値を返す。
3787:   return this.animate( genFx("show", 3), speed, callback);
3788:  } else { // 引数 speed が未定義か null 値の時、インスタンスの個々の要素に対して
3789:   for ( var i = 0, l = this.length; i < l; i++ ){
       // olddisplay なる名称で各要素ノードに関連付けられている値を取り出す。
       // 関連づけられた値がない場合には old = false となる。
       // 当該要素ノードに対する初めての show() 適用時には old は false と
       // なるが、二度目以降の呼び出し時には 3811 行で関連づけが定義済みなので
       // old = "block" となる。
3790:    var old = jQuery.data(this[i], "olddisplay");
3791:    // old があれば各要素ノードの display スタイル値に old を、
       // old が false ならば null を代入する。
3792:    this[i].style.display = old || "";
3793:    // 各要素ノードの display スタイル値が "none" ならば
3794:    if ( jQuery.css(this[i], "display") === "none" ) {
3795:     var tagName = this[i].tagName, display; // ローカル変数定義
3796:     // 当該要素ノードに対する初めての show() 適用時には
        // elemdisplay[ tagName ] プロパティは存在していないが、二度目以降の
        // 呼び出し時には 3808 行で定義済みなので存在していることになる。
        // elemdisplay オブジェクトに tagName プロパティがあれば、
3797:     if ( elemdisplay[ tagName ] ) {
3798:      display = elemdisplay[ tagName ]; // display 値をその値とする。
3799:     } else { // elemdisplay オブジェクトに tagName プロパティがなければ
         // tagName と同じ名称の要素ノードを作成し、body 追加後に参照を取得する。
3800:      var elem = jQuery("<" + tagName + " />").appendTo("body");
3801:      // 追加したノードの display 属性を取得する。
3802:      display = elem.css("display");
3803:      if ( display === "none" ) // もしもその値が "none" ならば
3804:       display = "block"; // "block" に変更する。
3805:      // body に追加したノードを削除し、
3806:      elem.remove();
3807:      // elemdisplay の tagName プロパティ値を display つまり "block" とする。
3808:      elemdisplay[ tagName ] = display;
3809:     }
3810:     // 各要素ノードに olddisplay 名の display 値 つまり "block" を関連づける。
3811:     jQuery.data(this[i], "olddisplay", display);
3812:    }
3813:   }
3814:
3815:   // Set the display of the elements in a second loop
3816:   // to avoid the constant reflow
3817:   for ( var i = 0, l = this.length; i < l; i++ ){
       // 各要素ノードの display スタイル属性を olddisplay 名で関連づけら
       // れている値("block")、又は null に設定する。
3818:    this[i].style.display = jQuery.data(this[i], "olddisplay") || "";
3819:   }
3820:
3821:   return this; // インスタンスを返す。
3822:  }
3823: },

▲ToTop

jQuery().hide(speed,callback)メソッドの解読

引数:継続時間、callback関数

返値:jQueryインスタンス

機能:言うまでもなく jQuery インスタンスに属する要素を隠蔽させるメソッドです。

このメソッドにおいても引数が与えられた場合には、animate メソッドが利用されますがここでは触れません。引数がない場合のみ解読します。

ある要素に初めて hide メソッドを適用した場合

インスタンスの個々の要素に対して、最初に hide メソッドが適用された場合において、既に当該要素に show メソッドが適用されていれば、当該要素に olddisplay 名で関連づけられた情報は block や inline となっており、一度も show メソッドが適用されていなければ、当該要素に olddisplay 名で関連づけられた情報は存在しません。

つまり、#3830 の変数 old には、block、inline などのスタイル値か、又は undefined が代入されます。そして old が undefined 値の場合、!old==true となり、勿論 old!=="none" が成立します。

こうして事前に show メソッドが適用されていない要素に対して、初めて hide メソッドを適用した時には、#3832 が実行され、data メソッドと css メソッドにより、当該要素の style.display 値、または算出(カレント)display 値が、olddisplay 名で当該要素に関連づけられます。
(関連づけられる値は block、inlineblock、inline 等となりますが、if 条件から none だけはあり得ません。olddisplay 値は block や inline などの表示用の値となり、none となることは決してありません。)

show メソッドの後に hide メソッドを適用した場合、あるいは hide メソッドを二度以上適用した場合

他方、show メソッドが適用された後に hide メソッドを適用した場合や、同一要素に対して二度目以降の hide メソッドを適用した場合には、old は未定義ではなくなっていますから !old=false となり、#3831~3832 は実行されないまま、#3837~3838 が実行され当該要素は非表示、あるいは非表示のママとなります。

上の行為の後に再び show メソッドを適用した場合

olddisplay 値は block や inline 等となっているので、3792 行により display 値はそのままとなります。よって #3794-3812 は通過し、3818 行が実行されて当該要素が表示されることになります。

■$().hide メソッドコード解読
3825: hide: function(speed,callback){
3826:  if ( speed ) { // speed があれば animate メソッドを実行する。
3827:   return this.animate( genFx("hide", 3), speed, callback);
3828:  } else { // speed がないか null の時
3829:   for ( var i = 0, l = this.length; i < l; i++ ){
       // 各要素に olddisplay 名で関連づけられているデータを変数 old に代入する
3830:    var old = jQuery.data(this[i], "olddisplay");
3831:    if ( !old && old !== "none" ) // old が false か "none"でない時には
        // 各要素ノードの display スタイル値を
        // olddisplay 名で各要素ノードに関連づける。
3832:     jQuery.data(this[i], "olddisplay", jQuery.css(this[i], "display"));
3833:   }
3834:
3835:   // Set the display of the elements in a second loop
3836:   // to avoid the constant reflow
3837:   for ( var i = 0, l = this.length; i < l; i++ ){
3838:    this[i].style.display = "none"; // 非表示を設定する。
3839:   }
3840:
3841:   return this; // インスタンスを返す。
3842:  }
3843: },
3844:

▲ToTop

jQuery().toggle(fn,fn2)

引数:fn,fn2

返値:jQueryインスタンス

機能:jQuery インスタンスに属する要素を表示/隠蔽させる循環メソッドです。

toggle メソッドはイベント処理にも登場します。それが最初の分岐(#3851)の2つの引数が共に関数の場合です。この場合には 3852 行が適用されてイベントの toggle 処理が行われます。

fn、fn2 が共に存在しない場合、あるいはいずれか一方以上が関数でない場合が、アニメ-ション処理で利用する toggle メソッドです。

アニメで利用する toggle メソッドは 2 つのケースに分かれます。

第 1 のケースは、fn、fn2が共に存在しない場合、あるいは第1引数が真偽値の場合です。このときにはインスタンスに登録されている各要素毎に次のことを行います。

  1. 最初の引数が真偽値で true ならばその値を state に代入します。
  2. bool がない場合(つまり引数が全くない場合)や、最初の引数が false の場合には、対象要素のhidden 属性の有無を調べ、あれば true、なければ false を state に代入します。つまり引数を全く指定しない場合や、fn == false と指定した場合には、当該要素の hidden 属性の有無によって、state に代入される値が決まります。hidden 属性があれば true、なければ false となります。(#3855)
  3. 次に、state が true ならば、jQuery(this).show() メソッドを実行し、false ならば、jQuery(this).hide() メソッドを実行します。(#3856)

第 2 のケースは fn、fn2 のいずれか一方以上が関数でない場合で、かつ最初の引数が真偽値でない場合です。このときには、"toggle" をアニメ用オブジェクトのプロパティ値とし、fn と fn2 を順に speed、easing として、インスタンスから animate メソッドを起動します。(#3858)この animate メソッドにおいて第 1 引数であるオブジェクトのプロパティ値を toggle とする場合の挙動は別途詳述します。

■ toggle インスタンスメソッド
3845: // Save the old toggle function
3846: _toggle: jQuery.fn.toggle,
3847:
3848: toggle: function( fn, fn2 ){
3849:  var bool = typeof fn === "boolean"; // fn が真偽値かどうかを bool に代入
3850:  // (1)fn も fn2 も関数ならば_toggle メソッドを実行する。
     //(このケースはイベント処理時に登場する)
3851:  return jQuery.isFunction(fn) && jQuery.isFunction(fn2) ?
3852:   this._toggle.apply( this, arguments ) :
     // (2)引数がない場合、あるいはfn か fn2 のいずれか1つ以上が関数でない場合には、
      // (2-1)引数がないか、または fn が bool(真偽値)ならば
3853:   fn == null || bool ?
3854:    this.each(function(){ //各インスタンス毎に
        // bool が true ならば fn を、bool がない場合や false ならば当該要素の
        // hidden 属性有無(true or false)を変数 state に代入する。
3855:     var state = bool ? fn : jQuery(this).is(":hidden");
        // state が true ならば show メソッドを、
        // state が false ならば hide メソッドをそれぞれ起動する。
3856:     jQuery(this)[ state ? "show" : "hide" ]();
3857:    }) :
      // (2-2)fn が null でもなく真偽値でもない場合animateメソッドを実行する。
3858:    this.animate(genFx("toggle", 3), fn, fn2);
3859: },

▲ToTop

jQuery().fadeTo()

引数:speed、to、callback

返値:jQueryインスタンス

機能:不透明度を to、speed を duration、callback をアニメ終了後に実行される関数とする、animate メソッドを起動します。

■ fadeTo メソッド
3861: fadeTo: function(speed,to,callback){
3862:  return this.animate({opacity: to}, speed, callback);
3863: },

▲ToTop

jquery.js におけるアニメーションコードの解読 ( 2 )

これから jquery.js のアニメーションコードに関わる各種のメソッドを解読していきます。

最初に、アニメーションコードの随所に登場し、アニメーションの登録/解除/実行をコントロールするメソッドを扱います。

このエントリイで解読するメソッドは、jQuery.queue、jQuery.dequeue、
jQuery().queue、及び jQuery().dequeue の 4 つで、このエントリイの末尾に 4 つのメソッドの全コードとその解説を掲載しました。

jQuery.queue( elem,type,data ) クラスメソッド

引数:要素ノード、タイプ名称、data(一般に関数)

返値:配列( data が配列ならばその各要素を要素とする配列、data が配列でないならば [data] )

機能:タグ要素に data を要素とする待ち行列を type 名で関連づけ、関連づけた配列を返します。具体的にはアニメーションメソッドを登録したり、登録を削除するために使用します

引数を使って説明すれば、elem 要素に type + "queue" 名で data を要素とする配列を関連づけ、かつその配列を返します。例えば data が animate メソッドなどの関数ならば、当該関数を要素とする配列が返されます。

なお、同一の elem に、同一の type 名で複数の data( data は同一でも異なっても可)を関連づけることも出来ますし、同一の elem に異なる type 名で、同一または別の data を関連づけることも可能です。

このメソッドを複数回起動すると、その回数個数の data が type 名でインスタンスに関連づけられます。

また data 引数なしでこのメソッドを起動すると、type 名で elem に関連づけられている配列があれば当該配列(空配列も含む)が返されます。

▲ToTop

jQuery.dequeue( elem.type ) クラスメソッド

引数:要素ノード、タイプ名

返値:なし

機能:インスタンスの各要素に関連づけられている待ち行列から、その要素を取り出して実行するメソッドで、具体的にはアニメーションメソッドを実行するために使用します。

正確に言えば、type 名がない場合とそれが fx の時には、配列の最初の要素を配列から削除した上で、配列の2番目の要素に登録されていた関数を実行し、type 名が fx ではない別の文字列の時には、最初の要素を配列から削除した上で、削除した要素を実行します。

なお、関連づけられているデータが関数でない場合には何もしません。

例:
$.queue($("p").get(0),"te",function(){return alert("test")})
$.dequeue($("p").get(0),"te") // type!=="fx" だから test が alert される。

$.queue($("p").get(0),"fx",[function f1(){return alert("test1")},function f2(){return alert("test2")}])
$.dequeue($("p").get(0),"fx") // type=="fx" だから test2 が alert される。

$.queue($("p").get(0),"fx",function f3(){return alert("test3")},function f4(){return alert("test4")})
$.dequeue($("p").get(0),"fx") // test3 が alert される。(下記参照)
/* 2 番目の queue メソッドによって 2 つの関数が最初の paragraph 要素に関連づけられるが、
 * 2 番目の dequeue メソッドによって、関数 f1 は配列から除かれる。
 * そこで第 3 の queue メソッドを実行した時点において、 fx 名で最初の paragraph 要素に
 * 関連づけられている配列の要素は、f2 関数と、f3 関数の 2 つとなる。
 * ( 3 番目の queue メソッドの第 4 引数は queue メソッドの定義から無視される。)
 * その後に 3 番目の dequeue メソッドを実行すると、type =="fx" だから 配列の 2 番目の
 * 要素に登録されている関数 f3 を実行する。
 */

▲ToTop

jQuery().queue( type,data ) インスタンスメソッド

引数:タイプ名、data(一般には関数)

返値:配列またはjQueryインスタンス

機能:第 2 引数 data を与えた場合には、インスタンスに登録されている各要素毎に data を要素とする配列を関連づけ、或る条件下では関連づけた関数を実行します。一方、data を与えない場合には、要素に関連づけられた配列があればその配列を返します。
以上を一言にまとめれば、「dataを配列に格納してその配列を要素に関連づけ、場合によっては配列を返すか、配列要素である関数を実行する」と言ったところでしょうか。

  1. type が文字列型でない場合(引数が全く存在しない場合、つまり type == undefined の場合も含む)には、それを data に代入して、type には fx を代入します。つまり引数で与えた data は type に置き換えられます。
    その上で data が undefined の場合、つまり引数が全くない場合には、最初のインスタンスに type 名で関連づけられた配列があればそれを返し、関連づけられた配列がなければ空配列を返します。
    特徴的なことは返値がインスタンスではなく配列であることです。
  2. type が文字列でない場合で、data (それは既に type に置き換えられている)があれば、インスタンスの各要素毎に data を要素とする配列を type 名で関連づけ、各要素毎に待ち行列配列を作成します。このとき (1)type 名が fx で、かつ(2)待ち行列の要素数が 1 で、(3)当該要素が関数ならば、当該関数を実行した上で、インスタンスを返します。
    他方、type 名が fx でないか、あるいは配列の要素数が0か2以上の場合には関数を全く実行せず、単にインスタンスを返します。
  3. type が文字列で data がなければ 1.のケースと同様に配列を返します。
  4. type が文字列で data があれば、待ち行列を作成します。しかし当該の文字列が "fx" でなければ、要素に type 名で data を要素とする配列を関連づけるだけでこのメソッドを終了します。他方、当該文字列が "fx" ならば、登録された配列要素数が1でその要素が関数の時だけ、その関数を実行します。

具体的には animate メソッド内で jQuery().queue メソッドが利用されますが、その際の引数は唯一の関数しかないので、当該関数が data に代入され、type=="fx" となります。こうして、インスタンスの各要素に関連づけられた当該関数が 1 つだけの場合には、当該関数が実行されます。

なお、登録されている関数が既に 1 つ以上ある場合には、関数は実行されず配列内に登録されるだけです。

また、jquery.js には空配列だけを引数として jQuery().queue メソッドが利用されるシーンもあります。この場合には、上記 3. から type="fx"、data=[] となり、待ち行列の要素数はゼロですから、何も実行されません。但し、このときにはタグ要素に fx 名で関連づけられている配列は空配列となります。つまり、 jQuery().queue([]) を実行すると登録メソッドが削除されてなくなります。

▲ToTop

jQuery().dequeue( type ) インスタンスメソッド

引数:type

返値:インスタンスの個々の要素毎に jQuery.dequeue(this,type) を実行します。

機能:type 名がないか、それが fx の時には、配列の最初の要素を削除した上で、新たに最初の要素となった関数(それは削除前には配列の2番目の要素に登録されていた)を実行し、type 名が fx でない場合には、最初の要素を配列から削除した上で、その関数を実行します。なお、関連づけられているデータが関数でない場合にはこのメソッドは何もしません。

--------------------------------------------------------
■$.queue & $.dequeue メソッドコード解読
1271:jQuery.extend({ // jQuery オブジェクトの拡張
・・・・・・・・・・・・・
    // これは、ノードに data を要素とする配列を関連づけ、、かつ関連づけられた 
    // 配列を返すメソッドである。dataは関数であることを暗黙の前提としているが、
    // このメソッドではその関数を実行することはない。あくまでも登録するだけである。
1339: queue: function( elem, type, data ) {
1340:  if ( elem ){
1341: 
1342:   type = (type || "fx") + "queue"; // ex."fxqueue"
1343:   // 要素 elem に type 名で関連づけられた値を返す。
1344:   var q = jQuery.data( elem, type ); // 返値は配列または未定義値となる。
1345:   // q が 未定義か、または 第 3 引数 data が配列ならば
1346:   if ( !q || jQuery.isArray(data) )
       // elem に type 名で jQuery.makeArray(data) を関連づける
       // q には jQuery.makeArray(data) が代入される。
1347:    q = jQuery.data( elem, type, jQuery.makeArray(data) );
1348:   else if( data ) // q が存在するか、配列ではない第 3 引数 data が
1349:    q.push( data ); // ある場合には配列 q に data を格納する。
1350:   // 以上の結果 q は必ず配列型の変数となる。
1351:  }
1352:  return q; // elem があろうがなかろうが、兎に角 q を返す。
     // data が未定義の時には q は空配列となる。
     // なお、elem がないときには明らかに return q; がエラーを引き起こす。
     // (Error:ReferenceError: q is not defined)
1353: },
1354: // $.queue により elem に関連づけられた data(関数)を実行するメソッド
    // 但し、type が fx の時などには、2番目の data を実行することに留意。
1355: dequeue: function( elem, type ){
1356:  var queue = jQuery.queue( elem, type ), // queue = [data] or []
1357:    fn = queue.shift(); // queue の最初の要素を削除し、それを fn に返す。
1358:    // 最初の要素がない場合には fn は false となる。
1359:  if( !type || type === "fx" ) // type がないか "fx" ならば、
1360:   fn = queue[0]; // 1356行の変数 queue の2番目の要素
1361:   
1362:  if( fn !== undefined ) // fn が定義されていれば fn を実行する。
1363:   fn.call(elem); // fn が未定義ならば何もしない。
1364: }
1365:});
■$().queue & $().dequeue インスタンスメソッドコード解読
1367:jQuery.fn.extend({ // jQuery インスタンスのプロトタイプを拡張する
・・・・・・・・・・・・・
1392: queue: function(type, data){ // このメソッドは条件によっては関数を実行する。
1393:  if ( typeof type !== "string" ) { // type が文字列でないならば
1394:   data = type; // type 値を data に代入し
1395:   type = "fx"; // type 値を "fx" とする。
1396:  }
1397:  // (1)data が未定義の場合
1398:  if ( data === undefined )
      // type 名で最初のインスタンスに関連づけられている配列を返す。
      // 既に関連づけられている待ち行列配列がない場合には空配列が返される。
1399:   return jQuery.queue( this[0], type );
1400:  // (2)data が定義されている場合
1401:  return this.each(function(){ // jQuery インスタンスを返す。
      // jQuery インスタンスの個々の要素毎に待ち行列配列 queue を作成する。
1402:   var queue = jQuery.queue( this, type, data );
1403:   // もし type が fx で配列 queue の要素数が 1 ならば
1404:   if( type == "fx" && queue.length == 1 )
1405:    queue[0].call(this); // 関数 queue[0] を実行する。
      // type 名が fx でないか、待ち行列 queue の要素数が 0 か 2 以上の場合には
      // 何もせずに jQuery インスタンスが返される。
1406:  });
1407: },
1408: dequeue: function(type){
1409:  return this.each(function(){ // jQueryインスタンスの個々の要素毎に
1410:   jQuery.dequeue( this, type ); // type 名で登録されている関数 data を実行する。
1411:  });
1412: }
1413:});

jquery.js におけるアニメーションコードの解読 ( 1 )

jquery.js におけるアニメーションコードの全容解明のために、まずは基本的事項から認識を深めていこうと思います。

Javascript によるアニメーションとは何か

アニメーションとは、多数の静止画を所定の時間内で連続的に切り替えて「動き」を演出する一連の動作です。アナログ動画は Max 30 枚/秒の静止画から構成され、0.03 秒ごとに切り替わることにより動きを演出しますが、スクリプトによるアニメーションでも静止画の切り替えを連続的に行うことによって動きを表現します。

具体的には対象とするタグ要素の CSS スタイル属性値を、スクリプトから定期的に変化させてアニメーションを実現します。コンテンツの幅や高さなどの CSS スタイル値に、初期値、定期的増減値及び終了値の 3 つの値を与え、それらを時間軸上で処理することにより「動き」を演出します。

jquery.js ではアニメーションをどのように実現しているのか

jquery.js においてこのようなアニメーションを実現しているプロセスは以下のようになります。

  1. まずアニメーションが終わる状態である「 終了値 」を animate メソッドの第 1 引数 prop オブジェクトで指定します。animete メソッドでは他に引数として、アニメに必要となる easing、duration、並びにアニメ終了後に起動する callback 関数を指定し、結局これらの 4 つの引数によってアニメーションに係る全ての要素を指定します。

    なお、speed をオブジェクト形式で指定して、第 3 と第 4 引数を指定しない引数指定方法もあり、この方法によってのみ指定可能な値があります。queue と step メソッドです。

    queue は、同一要素の対する複数のアニメーションを、順次起動するか、並行的に起動するかを指定するプロパティです。queue を指定しないか true とすれば順次起動に、false とすれば並行的起動となります。

    step はアニメ進行中に何かを行わせる場合に指定するメソッドです。

    オブジェクト形式で指定する場合には、duration と easing もそのオブジェクト内の各プロパティとして指定します。

  2. アニメ-ション開始時の状態: 「 初期値 」 は、既にブラウザで描画済みの要素の状態から、e.custom メソッドによって取得します。

    ここに e は jquery.js 内部において、jQuery.fx コンストラクタから生成されるインスタンスです。

  3. アニメーションの定期的増減値は、初期値、終了値、easing関数、並びに開始時刻、継続時間及び現在時刻の6つの要因から e.step メソッドにより設定されます。

    なお、この e.step メソッドは、animate メソッドの第 2 引数をオブジェクト形式で指定して、そのプロパティで定義する step メソッドとは別物です。

  4. 最後に e.custom メソッドとその中から呼び出される e.step メソッドによって、必要な全ての値を取得し終えてから、e.update メソッドによって描画を更新します。

ここにおいて、以上の 2.~4.、すなわち初期値設定から描画更新までのフローは、animate メソッドを起動した jQuery インスタンスの登録要素毎に、かつ指定した CSS プロパティ毎に何百回、何千回と繰り返されます。この二重のイテレートによってアニメーションが実現されていることを理解することが、animate メソッドを理解する大きなポイントです。

因みにこれらの連続して起動される一連のメソッドを図式化すると以下のようになります。
$().animate(prop,speed,easing,callback){
 $().each(func(){ || $().queue(func(){ //イテレートその1( 対象要素毎 )
  $.each(prop,func(name,val){ //イテレートその2( CSSスタイルプロパティ毎 )
   ・val が toggle || show || hide
        → e.toggle || e.show || e.hide メソッド起動
         これらのメソッド内で最終的に e.custom メソッドが起動される
   ・val がその他ならば        → e.custom メソッドが起動される
   ・e.custom =初期値を取得し、アニメ開始時刻を記録する。
     ↓
    ・e.step =開始時刻からの経過時刻、その時点における CSS 値などを設定する。
      ↓
     ・e.update =その時点の CSS 値に基づいて要素を表示する。
  })
 });
};

▲ToTop

animate メソッドに登場するオブジェクトのプロパティ名一覧

animate は、CSSプロパティ値を頻繁に操作し、変化させるメソッドです。それらのプロパティ値を含むオブジェクトは animete メソッド内で複数個登場しますが、最終的には e インスタンスのプロパティに統合されます。

ですから、animeteメソッドを理解するには、オブジェクトとそのプロパティのそれぞれの役割を知ることが重要になります。

以下に、animate メソッドに登場する e インスタンスのプロパティとメソッド、並びに jQuery.fx のクラスプロパティを、簡略化した独自の表示形式で、最小限の説明を加えて羅列しておきます。

それぞれのプロパティの役割については、各メソッドの解読の中で行います。

■ e インスタンス ( コンストラクタは jQuery.fx )
 e = {
 elem:elem,// jQuery.fx の第 1 引数で、animete メソッドの起動元となる
       // jQuery インスタンスに登録されている要素
 prop:propName, // jQuery.fx の第 3 引数
 options: {
  queue:false || true, step:function(){・・・}, // これらはユーザーが指定する。
  old,complete,easing,duration,display,overflow,// animate メソッドから引き継がれる。
  curAnim, // animate メソッドの第一引数である prop オブジェクトが格納される。
  orig:{prop}, //同上であるが役割が異なる。
  show:true || false || undefined, hide:true || false || undefined
 },
 startTime, start, end, unit, now, pos, state,
 // メソッド
 cur(), custuom(), step(), update(), show(), hide()
}

■ jQuery.fx コンストラクタのクラスプロパティ
 jQuery.fx.speeds = {"slow":600, "fast":200, _default:400}
 jQuery.fx.step = {opacity:fn(e), _default:fn(e)} 

一ヶ月以上ブログを更新しない場合には、ブログが表示されなくなる!

この処置は余りに残酷である。こんな事をやられたら堪らない。
忙しくて更新できないこともあるのだから。

表示が遅くても良いから、広告表示をやめることを検討しなければならない!

 90%近いシェアを握っているインターネットエクスプローラの描画エンジンを利用したタブbrowser。沢山のタブbrowserがあるが、多機能、カスタマイズフリー、スクリプト利用等で一日の長がある。Gekkoエンジンへの対応も行われ、IEからの自立独立の方向に向かっている。2005年7月にはIE7が登場する見通しの中で、今後の発展が望まれる。

 多様なCSS作成支援機能を備えた、タグ入力式 HTML&CSS作成支援エディタ。スキンデザインもすっきりしている。テキストエディター上で作成するよりも確実で安全にタグ打ちが出来る。
文字コードを選べないのが欠点。

 StyleNote同様のタグ入力式 HTML&CSS 作成支援エディタ。長年使用してきたが現在StyleNoteに乗り換えつつある。

 クリップボード履歴情報を活用する為のソフト。画像まで履歴を取ってくれるのが嬉しい。このソフトを使わない日は絶対ない程に重宝し、愛用している。

 起動中のウィンドウの「コピーできない」説明文などの文字列を取得し、コピー可能な文字データにするツール。何かと便利。

 ストリーミングデータを保存することが出来るソフト。動画利用には不可欠なソフトだ。

 無料ながらレイヤー機能を有し、スクリプトによる拡張も可能な、sleipnir作者が提供している優れもの画像編集ソフト。

 画面キャプチャソフトと言えばこれに勝るものなし、ではないだろうか? 様々な取得方法を有しており、ブログ作成にもHomepage作成に不可欠だ。Jtrimと並んでWoodyBellsの作品。

 複数ファイルの同時編集は出来ないが、透過pngも作れる画像編集ソフト。
(以下当該サイトから抜粋)初心者にも簡単に操作が出来るフォトレタッチソフトです。多くの加工機能で画像に様々な効果を与えることができます。非常に軽快に動作するため、ストレスなく操作できます。

 Animation Gifファイルを作れる無料ソフト。

 キャプチャソフト。画面内にサイト全体が表示しきれない場合でも、これを使えば全体をキャプチャすることが出来る。

 画像処理。画像のフォーマット変換のみならず、色数やサイズ、圧縮率の変更まで一括処理できてしまう『BatchGOO!』は、大量の画像をまとめて処理したいときに大変便利なソフト。BMP, TIFF, JPEG, PCX, PNG の相互変換をはじめ、色数・サイズ・解像度の統一、JPEG圧縮率の調節など、ホームページ用の画像や携帯電話用の壁紙を揃えるのに抜群の相性を見せる。(Vectorの当該ソフト紹介頁より抜粋引用)

 名前から直ぐに想像が付くように画像のサイズを測るためのソフトだ。Homepage作成には欠かせない。2カラム、3カラムのレイアウトを行う場合に大変重宝する。

 ランチャーソフトは沢山あるが、中でもこれが一押しだ。2年以上使ってきたがその操作性には毎日満足している。これを使い始めてからデスクトップには一切のアイコンを表示することをやめてしまった。

 AdobeReader7によって、起動時間が長すぎるという長年のユーザーの不満はある程度解消した。そのためこの高速化ソフトは存在価値が低下してしまったかもしれない。AdobeReader6迄はこのソフトによる起動高速化で恩恵を受けてきた。

 IE専用が難点だが、様々なサイト内でIDやパスワードを入力するのに重宝するソフト。コンテキストメニューから簡単に起動できるのがGood! sleipnir等のIEの描画エンジンを利用しているブラウザでも使える。

 利用しているパソコンの諸元値を取得するには、このソフトがベストだ。インストール済みソフトの一覧が取得できるのも嬉しい。

 WMPは機能が豊富なだけ重い。RealPlayerも同様だ。そこでMedia Player Classicを使いたい。動作が軽快なだけではなく、対応しているファイル形式もすこぶる多く、これひとつで、wmvもrmも表示できてしまうのだから凄い! 数多あるMedia Playerの王様と言えるだろう。

 自宅でPCを起動しているときには必ず起動しているメディアプレーヤー。何かと過剰なWinampよりも、起動も速くスキンはシンプルだ。

 DivX, Xvid, Mov, Vob, Mpeg, Mpeg4, avi, wmv, dv, などの動画をDVD-Video形式に変換できるフリーソフト。クリックするとDVD関連ソフト紹介サイト=「DVDなToolたち」なるHomepageが開きます。

上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。