序文
この記事では、例としてjquery v3.5.1を使用して、アニメーションのラウンドの実行プロセスを追跡し、アニメーションシステムの設計アーキテクチャをスパイします。htmlテストコードは次のとおりです。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Document</title>
<script src="./lib/jquery.js"></script>
<style>
* {
margin: 0;
padding: 0;
}
.main {
width: 1200px;
margin: 0 auto;
border: 1px solid red;
height: 800px;
position: relative;
}
.top {
height: 100px;
width: 100px;
border: 1px solid #eee;
background-color: #eee;
opacity: 1;
}
.bottom {
border: 1px solid #000;
position: absolute;
top: 100px;
bottom: 0;
width: 100%;
}
</style>
</head>
<body>
<div class="main">
<div class="top">
hello world
</div>
<div class="bottom"></div>
</div>
</body>
<script>
$('.top').animate(
{
height: '300px',
opacity: 1,
},
2000
);
$('.top').animate(
{
width: '600px',
},
2000
);
</script>
</html>
演算結果:
追跡を実行する
1.1。
- これは最初に入力された関数です。propはhtmlページで渡したオブジェクトです{高さ: "300px"、不透明度:1}、速度は5000に等しく、次の2つのパラメーターは空です
- この関数の主な機能は、doAnimation関数を定義してから、this.queueを実行してキュー関数に入るというものです。Optall.queueは「fx」であり、キューで使用されます。
2.2。
- キューはキュー関数です。タイプは「fx」で、データは上記で渡されたdoAnimationです。
- スレッドはthis.eachで定義された関数に直接入ります。jQuery.queue関数はdomオブジェクト「fx」とdoAnimationの3つのパラメーターを渡します。domオブジェクトに新しいオブジェクトをマウントします。キー値は「fxqueue」です。値は[doAnimation]です。そして、キューに割り当てるオブジェクトを返します。
- 4786行のコード。jQuery.queueは、domオブジェクトにマウントされたキャッシュ配列(つまり、上の図のキュー)にアニメーション関数を追加します。実行すると、アニメーション関数が配列にプッシュされます。 dom要素をバインドできます複数のアニメーション関数を定義し、実行順序を設定します。
- 次に、jQuery.dequeueが実行されます。上記のgifアニメーションから、$( "。top")に2つのアニメーションが追加され、最初のアニメーションが完了した後に2番目のアニメーションが実行されることがわかります。コードでこの順次実行順序を達成しますか?重要なポイントは、4791行のコードです。queue[0]!== 'inprogress'。最初のアニメーションが実行されると、キューの0番目の要素が文字列に変更されました。 'inprogress'は、最初のアニメーションが完了する前にjQuery.dequeueが実行されないことを意味します。次に、jQuery.dequeueのキュー配列の要素を変更する方法を見てみましょう。最初の要素を "inprogress"に設定します。
3.3。
- var queue = jQuery.queue(elem、type)。domオブジェクトにマウントされているキューを取り出し、[doAnimation]としてqueue.queueに割り当てます。
- 最初の実行では、fnは関数doAnimationと同じです。その後、queue.unshift( "inprogress")を実行します。queueの値は["inprogress"]です。
- 次に、fnを実行し、次の関数を渡します。fnを実行すると、実際にはdoAnimationが実行されます。このような大きな円の後、最終的な目標は、最初のステップで定義したdoAnimation関数を実行することです。
- このデキュー関数は正確に何をしますか?最初にマウントされたdom上の配列オブジェクトを取り出します。最初は1つの要素doAnimationのみがインストールされています。実行されるたびに、配列の最初の要素が取り出され、シフトを介してfnに割り当てます。次に、配列の先頭に文字列「inprogress」を挿入し、fnを実行します。この「inprogress」の機能は、他にアニメーションがある場合は、現在アニメーションを実行していることを示します。スレッドを取得するアニメーション関数jQueryを使用する権利を実行する必要がある場合、キューの最初の要素が「進行中」であるかどうかを判断し、実行中の場合は実行を中止します。現在のアニメーションが最終的に実行されると、jQuery.dequeue関数を再度呼び出して、実行する必要のある待機状態の他のアニメーションがまだないかどうかを確認します。したがって、今回は、配列の最初の要素である文字列「inprogress」をfnに割り当てます。fnが文字列の場合、絶対に実行されないため、fn = againを実行します。queue.shift()。このようにして、fnは次のアニメーション関数を正常に取得します。 fnに値がある限り、fnを実行して2番目のアニメーションの実行を開始します。要約と分析では、domはマウントされたdomの上の配列オブジェクトに含まれています。デキュー関数のたびに、オブジェクトが実行する必要のある一連のアニメーション関数が実行されると、この配列の先頭から関数が取得されて実行され、デキュー関数が再度実行されて2番目のアニメーション関数の呼び出しが開始されます。つまり、dom要素を実行する必要があります。アニメーション関数は、デキュー関数を呼び出すことによってのみ実行でき、呼び出し後に1つ実行します。たとえば、最初のアニメーションは適切なタイミングで完了し、2番目のアニメーションはデキュー関数を再度実行することで実行できます。また、デキュー関数を実行する場合は、通常、コードの前に判定条件を追加して、終了配列の最初の要素が「進行中」に等しいかどうかを判断します。等しい場合は、デキュー関数は実行されません。 。
4.4。
- さて、大きな円を一周して最初の関数に戻りました。次に、キャッシュキューの最初のアニメーション関数である上の図で赤でマークされたdoAnimation関数の実行を開始します。
- Propは、htmlページの最初のアニメーション関数で定義された最終状態です{高さ: "300px"不透明度:1}。Optallは、3つのプロパティ{期間:2000、キュー: "fx"、イージング:未定義)を持つオブジェクトです。記録アニメーションの実行時間、domにバインドされたキャッシュアニメーション関数配列に対応するキー値、およびアニメーション遷移関数のイージング。
- Animation関数に記述されているのは、アニメーションを実行する実際のロジックであり、ユーザーが定義した最終的なスタイルの状態とoptalに従ってdom要素を移動します。
5.5。
function Animation( elem, properties, options ) {
var result,
stopped,
index = 0,
length = Animation.prefilters.length,
deferred = jQuery.Deferred().always( function() {
// Don't match elem in the :animated selector
delete tick.elem;
} ),
tick = function() {
...
},
animation = deferred.promise( {
elem: elem,
props: jQuery.extend( {}, properties ),
opts: jQuery.extend( true, {
specialEasing: {},
easing: jQuery.easing._default
}, options ),
originalProperties: properties,
originalOptions: options,
startTime: fxNow || createFxNow(),
duration: options.duration,
tweens: [],
createTween: function( prop, end ) {
var tween = jQuery.Tween( elem, animation.opts, prop, end,
animation.opts.specialEasing[ prop ] || animation.opts.easing );
animation.tweens.push( tween );
return tween;
},
stop: function( gotoEnd ) {
var index = 0,
// If we are going to the end, we want to run all the tweens
// otherwise we skip this part
length = gotoEnd ? animation.tweens.length : 0;
if ( stopped ) {
return this;
}
stopped = true;
for ( ; index < length; index++ ) {
animation.tweens[ index ].run( 1 );
}
// Resolve when we played the last frame; otherwise, reject
if ( gotoEnd ) {
deferred.notifyWith( elem, [ animation, 1, 0 ] );
deferred.resolveWith( elem, [ animation, gotoEnd ] );
} else {
deferred.rejectWith( elem, [ animation, gotoEnd ] );
}
return this;
}
} ),
props = animation.props;
propFilter( props, animation.opts.specialEasing );
for ( ; index < length; index++ ) {
result = Animation.prefilters[ index ].call( animation, elem, props, animation.opts );
if ( result ) {
if ( isFunction( result.stop ) ) {
jQuery._queueHooks( animation.elem, animation.opts.queue ).stop =
result.stop.bind( result );
}
return result;
}
}
jQuery.map( props, createTween, animation );
if ( isFunction( animation.opts.start ) ) {
animation.opts.start.call( elem, animation );
}
// Attach callbacks from options
animation
.progress( animation.opts.progress )
.done( animation.opts.done, animation.opts.complete )
.fail( animation.opts.fail )
.always( animation.opts.always );
jQuery.fx.timer(
jQuery.extend( tick, {
elem: elem,
anim: animation,
queue: animation.opts.queue
} )
);
return animation;
}
- このアニメーション関数は3つのパラメータを受け入れます。elemはアニメーション化されるdom要素、プロパティはユーザーが入力するスタイル、そしてその値は初めてトリガーされたときの{height: "300px"、opacity:1}です。オプションの3つのプロパティ値{期間:2000、キュー: "fx"、イージング:未定義}。
- アニメーションはティック関数とアニメーション変数を作成します。ティック関数の機能については後で説明します。アニメーションはdeferred.promiseを介して作成された遅延オブジェクトです(遅延オブジェクトは前の章で紹介されています)。
- アニメーション変数を作成するコア関数の1つは、アニメーションオブジェクトにプロパティトゥイーンを追加することです。次のコードjQuery.map(props、createTween、animation);を使用して、この関数を完成させます。トゥイーンの値を見てみましょう。種類を追加した後
- アニメーションオブジェクトには、配列に対応する追加のトゥイーンプロパティがあります。配列内の各オブジェクトは、上記のプロパティの各スタイルに対応する状態に対応します。たとえば、ユーザー定義の高さ: "300px"。最終的に解析されます。開始状態開始:100。終了状態終了:300。現在の状態は、作成時の開始状態開始と同じです。Elemはアニメーションのdom要素を参照します。Easeはアニメーション関数です。Propはの属性です。 startの値は、下部にあるgetComputedStyleAPIを介して取得されることに注意してください。
- 要約すると、トゥイーンは、アニメーションに必要なすべてのデータに変更されるすべての属性を解析します。たとえば、各フレームはトゥイーン配列をトラバースし、nowの値が計算されるたびに、値がdomに割り当てられます。要素のスタイル、これはdom要素を動かすことができます。次に、時間の経過に伴う各フレームの現在の値を計算する方法が次の重要なタスクになりました。
6.6。
- 上記のアニメーション関数の最後の部分では、jQuery.fx.timerが実行され、tick関数がパラメーターとして渡されます。一連の処理の後、schedule関数に入ります。
- スケジュール関数では、window.requestAnimationFrameを使用してjQuery.fx.tick関数を再帰的に呼び出すことがわかります。inProgressがfalseでない限り、jQuery.fx.tickはフレームごとに実行されます。
- jQuery.fx.tickの関数は何ですか?jQuery.timersは、過去に最初に渡したtick関数を含む配列です。tick関数を取り出してタイマーに割り当てて実行します。timerの戻り値によると()jQuery.fx.stop();を実行するかどうかを決定する値。inProgressが実行されると、jQuery.fx.stop()はnullに設定されます。
- ここから、tick関数の結果がinProgressの値を直接決定し、inProgressがjQuery.fx.tickを再帰的に呼び出すかどうかを決定することがわかります。
- まとめると、このブロックはjqueryの通常の呼び出しモジュールです。関数tickを渡すと、フレームごとにtick関数が実行され、tick関数の戻り値に従ってループ呼び出しを停止するかどうかが判断されます。
7。
- このティック関数は、タイミングモジュールがすべてのフレームを呼び出す関数の本体です。最初に現在のタイムスタンプcurrentTimeを定義し、次に現在の瞬間に完了する必要があるアニメーションのパーセンテージを計算します。5番目の説明によるとステップ、それはanimation.tweensで見ることができますそれは各属性の開始値と終了値を含めることができますそれから(end-start)* percentを使用する場合、この時点で属性の現在の値を取得できますか? Animation.tweens [index] .runメソッドは、まさにこれを実行します。
- now値が取得され、7449行目のsetメソッドを呼び出して、dom要素のスタイルの対応する属性にnowの値が付加され、domのスタイルが変更されます。
- 上記のタイミングモジュールの呼び出しと相まって、ティック関数はフレームごとに実行され、現在の値で変更されたdomスタイルは、ティック関数が実行されるたびに計算され、アニメーション効果を形成します。
- 手順6から、ティック関数の戻り値によってフレーム呼び出しがいつ停止するかが決まることがわかります。7832行のコードはpercent <1の場合に値を返します。percentが1以上の場合、アニメーションは終了します。 、したがって、flaseを返します。
- 7842行目deferred.resolveWith(elem、[animation]);このコードは、アニメーションの実行後に呼び出されます。Deferredは遅延オブジェクトです。resolveWithメソッドを呼び出すと、事前にバインドされた完了関数がトリガーされます。見てみましょう。完了した機能
8.8。
- 行7927は、done関数を定義し、最終的にanimation.opts.complete関数にバインドされます。
- opt.complete関数の最後の呼び出しは、前述のjQuery.dequeueです。これにより、2番目のアニメーション関数が呼び出される前に1つのアニメーション関数が呼び出されます。
総括する
- アニメーションシステムのオペレーティングアーキテクチャは、ユーザー定義の$ .animate関数から始まり、doAnimation関数を作成し、それを配列に配置してdomオブジェクトにバインドします。これを行う理由は、複数のセットの管理を容易にするためです。アニメーションの。
- 次に、配列内のdoAnimation関数を前から後ろに順番に実行し始めます。doAnimationのコア関数は、ユーザーが$ .animate関数を定義するときにアニメーションの継続時間を使用して、現在の時間を継続時間のパーセンテージに計算し、組み合わせます。入力された終了状態のスタイル値は、現在の瞬間に対応するスタイル値を計算し、それらをdom要素に更新します。これはtick関数がすべてのフレームで行うことです。
- ティック関数の戻り値は、ティック関数を再帰的に呼び出すためのループ条件になる可能性があります。フレームごとにティック関数を実行すると、domスタイルが変更され、フレーム数の連続呼び出しによってアニメーション効果が形成されます。