表示の大きさを調整します。
48行目で、Safariブラウザには、window.scrollBy(0,0)で再描画を促します。
52~55行目のgetInlineOpacityは、要素の透明度を取得して返します。取得に失敗した場合は''を返します。
53行目で、要素のCSSのopacityプロパティの値を取得して返します。取得に失敗した場合は''を返します。
56~66行目のforceRerenderingは、Safariでfloat属性と透明度が絡んだときに、要素が正しくレンダリングされないバグを回避するための関数です。内部的にはEffect.Appearでのみ使われています
Effect
67~71行目の_elementDoesNotExistErrorは、指定のDOM要素が存在しなかった場合に投げられる例外です。
72~104行目のTransitionsには、比例や波などの関数が入っています。等速直線運動や波打つ動きをさせるのに使います。どれも、0.0~1.0の実数をとって、0.0~1.0の実数を返す関数です。linearのPrototype.Kとは、Prototype.jsで定義されている恒等関数です。
linear | 比例の関数(function(x) { return x }) | |
sinoidal | sin関数 | |
reverse | 逆比例の関数 | |
flicker | 乱数でゆらめいてから収束する関数 | |
wobble | 徐々に激しくなる震えのような関数 | |
pulse | 三角波のような関数 | |
spring | バネのようにビョーンとする関数 | |
none | 常に0の定数関数 | |
full | 常に1の定数関数 | |
105~113行目のDefaultOptionsは、エフェクトのデフォルトのオプション設定です。
- duration
- エフェクトの持続時間です。デフォルトは 1.0(秒)です。
- fps
- フレームレートです。デフォルトは 100(fps)です。この値が大きいほど、動きが滑らかになります。
- sync
- 他のエフェクトと同期するかどうかです。デフォルトはfalseです。Effect.Parallelにエフェクトを複数追加するときにだけ使います。
- from
- 開始時点のポジションです。デフォルトは0.0です。
- to
- 終了時点のポジションです。デフォルトは1.0です。
- delay
- エフェクトの開始を遅延する時間です。デフォルトは0.0です。
- queue
- エフェクトをどのキューにいれるかです。デフォルトは'parallel'です。
114~130行目のtagifyTextは、要素の子ノードにあるテキストノードについて、その文字列の1文字1文字をspan要素で囲ったものに置き換える関数です。内部的には使われていません。これを使うと、文字列の1文字ずつにエフェクトをかけたりできます。
116行目で、IEのCSSのバグを回避するために、CSSのzoomプロパティを1にします。
119行目で、要素の子ノードたちを取り出し、それぞれに以下の処理をします。
120行目で、テキストノードか調べます。
121行目で、テキストノードであれば、その文字列の1文字1文字に、以下の処理をします。
122~125行目で、1文字ずつspanタグで囲い、それらをテキストノードのところに挿入します。new Element('span',...).updateと、Prototype.jsの1.6で入った機能を使っています。
124行目で、文字で' 'と等しいものは、Unicodeの160番(00A0番)に統一します。
127行目で、元となったテキストノードは削除します。これで置き換わったことになります。
131~149行目のmultipleは、要素(か、その配列)とエフェクトのクラスを引数にとり、それぞれにエフェクトをかける関数です。3番めの引数にオプションを渡すことで、各エフェクトの開始時刻を少しずつずらすこともできます。
133~136行目で、1番めの引数に要素の配列が与えられたらそれらを対象にします。$Aで配列になるものなら良いようです。
138行目で、1番目の引数に要素が与えられたらその子ノードたちを対象にします。
140~143行目で、3番めの引数をオプションとして扱います。speedは開始時刻をずらす幅を、delayは全体の開始時刻を遅らせる幅を、それぞれ表します。
146行目で、それぞれにエフェクトをかけます。少しずつ開始時刻をずらします。
150~154行目のPAIRSは、対義なエフェクトのペアの対応表です。項の値は、現れる・消えるの順番に並んでいて、例えば'slide'の項の値は'Slidedown'(スライドして現れる)と'SlideUp'(スライドして消える)です。
155~165行目のtoggleは、上記の対義なエフェクトのペアの名前を引数にとって、要素が見えていれば、ペアの'閉じるエフェクト'、そうでなければ'開くエフェクト'をかけます。
157行目で、もしエフェクトの指定がなければ、'appear'のペアを使います。
158行目で、オプションを設定して、キューの最後尾に追加するようにします。。要素のidがあれば、idの名前がついたキューに追加します。なければ'global'キューに追加します。limitを1にしているので、既に他のエフェクトがキューにある場合は追加されません。これによって、例えばマウスクリックに応じて出たり消えたりするようにした場合に、連打してもエフェクトが積み重なりません。このキューのオプションについては後述します。
161行目で、要素が見えていれば'閉じるエフェクト'、そうでなければ'開くエフェクト'をかけます。
166行目で、Effect.DefaultOptions.transitionsをサイン関数にします。クラス定義が終わってからでないと参照できないのでこうしています。
Effect.ScopedQueue
Effect.ScopedQueueは、エフェクトのキュー(順番待ちの列)を作るためのクラスです。データ構造のキューがあるわけではなく、エフェクトの開始時刻と終了時刻を連続して並べることでキューを表現しています。ちなみに、エフェクトの中でoption.syncがtrueのものは、このキューに入りません。それらは順番にではなく同時に実行する必要があるからです。
171~174行目のinitializeは、空のキューを作る関数です。
172行目で、effectsには、キューに含まれるすべてのエフェクトが入ります。
173行目で、intervalは、エフェクトを順次実行するタイマが入ります。
175~177行目の_eachは、キューにあるエフェクトたちに、引数で指定した関数をおのおの適用する関数です。Prototype.jsのEnumerableのeachメソッドを使えるようにするために定義してあります。
178~209行目のaddは、キューにエフェクトを追加する関数です。順番待ちの列のどこに追加するについては、オプションで'front'先頭、'with-last'最後尾と同時、'end'最後尾から選べます。キューの長さがoptions.limitを越えている場合は追加されません。
179行目で、現在時刻をミリ秒単位でブラウザから取得します。エフェクトはすべて、ブラウザの時刻に基づいて実行されます。そのおかげで、コンピュータやブラウザの性能に左右されずにエフェクトを描画できます。
181行目で、options.queueかoptions.queue.positionから、値を取り出します。少々ややこしいですが、options={queue: 'front'}という与えかたと、options.queue = {position: 'front', scope:'hoge', limit:5}という与えかたが用意されているので、こうなっています。このややこしいのは、キューが当初は1本しかなく、スコープの概念は後付けだったためです。
184~199行目で、以下のような場合分けをします。
- 'front'(先頭)に挿入する場合は、すでにキューにあるエフェクトの開始、終了時刻をすべて後にずらします。この時点ではeffect.finishOnには、何ミリ秒後に終わるかという、相対時間が入っていることに注意してください。202行目で絶対時間になります。
- 'with-last'(最後尾と同時)にする場合は、すでにキューにあるエフェクトで開始時刻が一番後ろのものと開始時刻をあわせます。
- 'end'(最後)にする場合は、すでにキューにあるエフェクトで終了時刻が一番後ろのものの後に開始時刻をあわせます。
204行目で、options.queue.limitが設定されている場合は、キューの長さがそれを越えていないことを確認してから、キューに挿入します。
207行目で、まだタイマがなければ、タイマを作ります。このタイマは0.015秒ごとに、キューのエフェクトたちを実行します。
210~216行目のremoveは、キューから引数のエフェクトを削除する関数です。
211行目で、キューから引数のエフェクトを削除します。
212行目で、もしキューにひとつもエフェクトがなければ、タイマを止めます。
217~223行目のloopは、キューのエフェクトたちを実行する関数です。0.015秒ごとに呼ばれます。一見、キューのエフェクトすべてを実行しているように見えますが、現在時刻が開始終了時刻にそぐわないエフェクトはそれを無視するので大丈夫です。
218行目で、現在時刻をミリ秒単位で取得します。
219行目で、for(var i=0, len=this.effects.length;i<len;i++) となっているのは、このメソッドが頻繁に呼ばれるので、ループごとのlength取得さえもったいないということです。これはループ内不変式のくくり出しという最適化です。
Effect.Queues
Effect.Queuesは、上述のEffect.ScopedQueueで作るキューに名前をつけて区別することで、複数のキューを使えるようにするクラスです。
225行目で、instancesは、単なるハッシュテーブルです。名前とキューの対応表にします。
226~231行目のgetは、名前をとって、それに応じたキューを返す関数です。その名前のキューが無いときは、新しくキューを作って返してきます。
Effect.Queue
233行目で、単にEffect.Queueと呼んだときは、名前が'global'のキューを返すようにします。キューが1本しかなかったころの名残です。
Effect.Base
全てのエフェクトの基礎となるクラスです。
236行目のpositionには、常に現在のポジション(0.0~1.0)が入ります。renderメソッドが常にこの値を更新するようになっています。ポジションは進捗度にサイン関数やパルス関数を適用したもので、0.0~1.0の間でいろいろな動きをします。この動きにあわせて、透明度や位置を変化させることで、フェードアウトや揺れる動きができます。
237~271行目のstartは、エフェクトの生成時に呼ばれる関数です。
238~243行目のcodeForEventは、'afterFinishInternal'、'afterFinish'などの内部イベントにフックが設定されているかを前もって判定して、適切なコード文字列を生成する関数です。renderメソッドの動的な定義に使われます。
245行目で、optionを読み込みます。Effect.DefaultOptionsが上書きされないように、一度コピーしてから使っています。
246行目で、currentFrameは、常に現在のフレームが入ります。loopメソッドが常にこの値を更新するようになっています。
247行目で、stateは、エフェクトの開始時刻が来るまでは'idle'、実行中は'running'、終了時は'finished'をとります。
248行目で、startOnは、エフェクトの開始時刻です。options.delayを使って、開始時刻を遅らせることもできます。
249行目で、finishOnは、エフェクトの終了時刻です。
250行目で、fromToDeltaは、開始時点のポジションの設定であるoptions.fromと、終了時点のポジションの設定であるoptions.toの差です。
251行目で、totalTimeは、開始時刻と終了時刻の差です。
252行目で、totalFramesは、エフェクトにかかるフレーム数です。
254~265行目は後述します。
267行目で、'beforeStart'関係のフックを呼びます。
268行目で、options.syncが有効でなければ、このエフェクトをキューに追加します。options.queueの扱いが少々ややこしいですが、options={queue: 'front'}という与えかたと、options.queue = {position: 'front', scope: 'hoge', limit:5}という与えかたが用意されているので、こうなっています。
ループ内不変式をevalで削除する
254~265行目でrenderメソッドを、このstartの中で動的に定義します。このrenderメソッドは、0.015秒ごとに繰り返し繰り返し呼ばれる、いわゆるホットスポットです。そのため最適化のテクニックが使われています。それはループ内不変式の削除と呼ばれるテクニックです。読みやすくすると、本来は以下のような定義をしようとしています。
ここで、"フックがあればフックを実行"というif文が何度もでてきています。このif文の結果はループの中で変化しないとわかっているので、前もって判定してしまって、ループの中から削除することができます。プログラムを動的に書き換えるイメージでしょうか、つまり、renderメソッドについて、フックを呼ぶ定義と呼ばない定義とを、evalを使って、動的に作りわけるのです。evalは文字列のパースや評価をする非常に重い処理なので、使いどころを間違えると余計に遅くなることがありますが、このホットスポットの高速化については、それを差し引いておつりが来ます。フックがないことがわかれば、以下のような、より高速に動く定義を動的に作って、renderに与えることができ、性能を改善できます。
272~289行目のloopは、Effect.ScopedQueue.loopから呼ばれて、引数の現在時刻に応じて、エフェクトの動作を行う関数です。
274行目で、終了時刻を過ぎていた場合、以下の処理をします。
275行目で、最後の描画をします。
276行目で、エフェクトをキューから除きます。
277行目で、'beforeFinish'関係のフックを呼びます。
278行目で、finishフックをあれば呼びます。
279行目で、'afterFinish'関係のフックを呼びます。
開始時刻後の場合、以下の処理をします。
282行目で、現在時刻と開始時刻の差から、現在の進捗度を割り出します。
283行目で、進捗度から、現在のフレームを割り出します。
284行目で、前回のフレームと差があれば、現在の進捗度で描画をします。
290~295行目のcancelは、キューから自身を取り除いて、エフェクトを終了する関数です。
296~299行目のeventは、内部的なイベント名('afterFinish'など)を引数にとって、それに応じたフック('afterFinishInternal'、'afterFinish'など)を呼ぶ関数です。
300~306行目のinspectは、エフェクトのプロパティやオプションの内容を、人間に読みやすく記述する関数です。Prototype.jsのObjectクラスのinspectメソッドに対応します。
Effect.Parallel
Effectクラスでは、ひとつのインスタンスに、ひとつのエフェクトが対応するのが普通ですが、Effect.Parallelでは複数のエフェクトが対応します。これら複数のエフェクトはひとつのオプションを共有して、全く同じ開始時刻やスピードで動きます。
309~312行目のinitializeは、初期化をする関数です。エフェクトの配列と、オプションを引数にとります。
310行目で、エフェクトの配列を受け取って、this.effectsに保存しておきます。
311行目で、2番めの引数に与えられたオプションで、startを呼び出します。
313~315行目のupdateは、renderメソッドから描画のたびに呼ばれる関数です。
314行目で、invokeメソッドを使って、配列のエフェクトのそれぞれのrenderメソッドを、同じポジションで呼び出します。
316~326行目のfinishは、エフェクトの終了時に呼ばれる関数です。
配列のエフェクトのそれぞれに、Effect.Base.loopでエフェクトの終了時に行われているのと全く同じ終了処理を行います。
Effect.Tween
Effect.Tweenは、なんでも好きな関数を渡して、エフェクトとして動かせるクラスです。
328~336行目のinitializeは、初期化をする関数です。1番めの引数にオブジェクトか要素名、2番めに開始時点のポジション、3番めに終了時点のポジションを取ります。4番めの引数にオプションを、5番めの引数に関数か、1番めの引数のオブジェクトのメソッド名か、プロパティ名を与えることができます。
322行目で、5番めの引数をもとに、updateメソッドで呼ばれる関数を作ります。引数が関数であれば、オブジェクトにbindしておきます。あるいはオブジェクト用のメソッド名であれば、やはりbindします。プロパティ名であれば、そのプロパティの値をポジションで更新する関数を作ります。
337~340行目のupdateは、initializeで設定した関数を呼び出す関数です。
Effect.Event
持続時間0で、一瞬呼ばれるだけのエフェクトのクラスです。1番目の引数にオプションをとることができます。例えばキューの最後のエフェクトが終わったタイミングで何か処理を挟みたいときなどに使います。
343~345行目のinitializeは、初期化をする関数です。持続時間0でstartを呼びます。
346行目で、一応、updateに空の関数を入れてあります。呼ばれることはありません。
Effect.Opacity
要素の透明度が変化するエフェクトのクラスです。デフォルトでは現在の透明度から不透明になるフェードインエフェクトを作ります。
350~361行目のinitializeは初期化をする関数です。2番めの引数にオプションをとることができます。
354行目で、IEのCSSのバグを回避するために、CSSのzoomプロパティを1にします。
356行目で、オプションを読み込みます。デフォルトではfromは現在の透明度(なければ透明)、toは不透明になります。
362~365行目のupdateは、ポジションに応じて透明度を変化させる関数です。
Effect.Move
要素の位置が変化するエフェクトのクラスです。デフォルトでは幅0で移動する(つまり動きません)エフェクトのインスタンスを作ります。後方互換性のために用意されているEffect.MoveByの実体でもあります。
368~377行目のinitializeは、初期化をする関数です。2番めの引数にオプションをとることができます。
371行目で、オプションを読み込みます。デフォルトではx移動幅0、y移動幅0で、"相対座標モード"の設定になっています。
378~386行目のsetupは、要素の位置を動かす前の準備をする関数です。初めてrenderが呼ばれたときにこの関数が呼ばれます。
379行目で、prototype.jsのmakePositionedを呼んで、position: relativeにして、要素の位置移動に備えます。
380,381行目で、現在の位置を取得します。移動の基準点として使います。
382行目で、もし"絶対座標モード"なら、絶対座標を計算します。
387~393行目のupdateは、ポジションに応じて要素の位置を動かす関数です。CSSのleft、topプロパティの値を変更して、移動を表現します。
395~400行目のEffect.MoveBy は、後方互換性のためにあるクラスです。移動位置をオプション経由でなく引数で渡すところが違います。
Effect.Scale
要素の表示の大きさが変化するエフェクトのクラスです。終了時の大きさの倍率をパーセントで指定(100で等倍)します。
402~415行目のinitializeは、初期化をする関数です。2番めの引数で終了時の倍率をとります。3番めの引数にオプションをとることができます。
- scaleX
- 横幅を変えるかどうかです。デフォルトはtrueです。
- scaleY
- 縦幅を変えるかどうかです。デフォルトはtrueです。
- scaleContent
- 内容のテキストの大きさを変えるかどうかです。デフォルトはtrueです。
- scaleFromCenter
- 中心を動かさずに拡大縮小する設定です。デフォルトはfalseです。
- scaleMode
- デフォルトの'box'は、要素のoffsetHeight、offsetWidthに基づいて大きさを変えます。'content'は、要素のscrollHeight、scrollWidthに基づいて大きさを変えます。これらのプロパティの意味についてはリンク先を参照してください。
- scaleFrom
- 大きさの初期倍率です。パーセントで指定します。デフォルトは100(等倍)です。
- scaleTo
- 大きさの目標倍率です。2番めの引数の値が入ります。
416~446行目のsetupは、要素の大きさを変える準備をする関数です。
417行目で、options.restoreAfterFinishは、終了時に要素の大きさを元に戻すオプションです。
418行目で、elementPositioningに、要素のCSSの'position'プロパティの値を取りだします。
421~426行目で、終了後に元に戻せるように、元のCSSのプロパティの値をいくつか保存しておきます。
428~434行目で、CSSの'font-size'プロパティの値を取得します。このとき、'font-size'の単位も調べる必要があるので、indexOfを使って単位を探しています。
436行目で、factorは、倍率の変化量です。正なら拡大、負なら縮小を意味します。
438~445行目で、options.scaleModeに応じて、幅と高さを取得します。
439行目で、'box'なら、要素のoffsetHeight、offsetWidthに基づいて大きさを変えます。
441行目で、'content'なら、要素のscrollHeight、scrollWidthに基づいて大きさを変えます。これらのプロパティの意味についてはリンク先を参照してください。
443行目で、options.scaleModeが'box'でも'content'でもなければ、scaleMode: { originalHeight: 400, originalWidth: 200 }の形でオプションを与えられたと見込んで読み込みます。
447~452行目のupdateは、ポジションに応じて要素の大きさを変える関数です。
448行目で、現在の倍率をポジションから計算します。
449行目で、options.scaleContentが有効で、fontSizeの取得に成功していたら、内容のテキストの大きさをフォントサイズによって変えます。CSSのfontSizeプロパティの値を変えます。setContentZoomを使わないのは、高速化のためでしょう。
451行目で、後述のsetDimensions関数を呼びます。現在の倍率の幅と高さを渡します。
453~455行目のfinishは、エフェクトの終了時に呼ばれる関数です。
454行目で、大きさを元に戻すオプションがあった場合は、前もって保存してあったCSSの値に戻します。
456~474行目のsetDimensionsは、幅と高さを受け取って、実際に要素の大きさを変える関数です。
458行目で、options.scaleXが有効なら、幅を変更します。
459行目で、options.scaleYが有効なら、高さを変更します。
460行目で、options.scaleFromCenterが有効なら、幅や高さの変化に応じて座標を変更します。具体的には、サイズを変化させるのに、変化量の半分を座標の移動によってまかなうことになります(ここでthis.dimsには元のサイズが入っています)。
461、462行目で、topd、leftdのdは、位置の移動量のデルタを意味しています。
464、465行目で、elementPositioningに入った、要素のCSSの'position'プロパティの値に応じて、'abusolute'ならば座標を絶対座標に直します。
467、468行目で、相対座標であれば変化量を加えるだけですみます。
471行目で、以上の計算を実際にCSSに反映します。
Effect.Highlight
要素の背景色が変化するエフェクトのクラスです。デフォルトでは明るい黄色から元の背景色(なければ白)に変化するハイライトエフェクトを作ります。
476~481行目のinitializeは、初期化をする関数です。
479行目で、options.startcolorのデフォルト設定を明るい黄色にします。
482~498行目のsetupは、背景色を変える準備をする関数です。
484行目で、要素が表示されていなかったらエフェクトを中止します。
487行目で、options.keepBackgroundImageが設定されていない場合は、背景画像を無しにします。終了時に元に戻せるよう保存しておきます
491行目で、options.endcolorが設定されていない場合は、元の背景色を取得します。parseColorの機能で、読み取りに失敗した場合は1番めの引数の値が使われるというものがあります。よって元の背景色の取得に失敗すると白('#ffffff')が設定されます。
493行目で、options.restorecolorが設定されていない場合は、元の背景色に戻すように設定します。
496行目の、_baseは、3原色RGBのそれぞれの初期値(0~255)が入った配列です。[255,128,200]という形です。
497行目の、_deltaは、3原色RGBのそれぞれの変化量が入った配列です。[-8,5,-16]という形です。
499~502行目のupdateは、ポジションに応じて背景色を変化させます。
500行目で、CSSのbackground-colorプロパティに値を"#xxxxxx"と設定します。ここでinjectメソッドが使われています。このinjectは、3原色RGBの初期値と変化量の配列から現在の値を計算して、"#xxxxxx"の形式にして返しています。
503~509行目のfinishは終了時に呼ばれる関数です。
504行目で、背景イメージや背景色の値を元に戻します。
Effect.ScrollTo
要素に向かってメインウィンドウをスクロールするエフェクトのクラスです。
512行目で、prototype.jsの1.6で入った、document.viewport.getScrollOffsetsメソッドで、現在のビューポートのオフセットを取得します。
513行目で、Element.cumulativeOffsetメソッドで、要素のドキュメントの左上からのオフセット位置を取得します。
514行目で、スクロール量の最大値を求めます。このコードは1月28日のリビジョン8686で改訂されているので注意してください。
516行目で、要素のオフセット位置にoptions.offsetの値を加えます。
518行目で、Effect.Tweenを生成して即席のスクロールエフェクトを作っています。
522行目で、ポジションに応じて徐々にスクロールする関数を与えます。