javascriptはシングルスレッドであるため、アニメーションを開始してから(おそらくsetTimeout()
将来的には短期間で実行されます)、最初のJavaScriptよりもはるかに長い時間がかかる他のJavaScriptを実行するとsetTimeout()
、setTimeout()
他のJavaScriptの実行が完了するまで起動/実行できます。それが最終的に実行されると、聖なる牛、それは予定よりもはるかに遅れており、適切なトゥイーンアルゴリズムはスケジュールに戻り、アニメーションの最初の部分の束をスキップしようとすることがわかります。これはあなたが見て説明しているように聞こえます。
これを回避する唯一の方法は、アニメーションを開始した後にコードを実行する時間を大幅に回避することです。これは、アニメーションを実行するコードがスケジュールより遅れるからです。この問題のためだけに開始から終了までのアニメーションプロセスを最適化する意思があり、変更できるのが独自のアニメーションコードである場合は、すべての初期状態が事前に計算されるように、すべてのアニメーションを実行して初期化できますが、実際のアニメーションの開始されました。次に、すべてのコードを実行してすべてのアニメーションを設定したら、1つの非常に高速なループを実行して、すべてのアニメーションの実行を開始します。これにより、最初のアニメーションが開始された後のコードの実行時間が最小限に抑えられます。
何がすべての初期化時間を取っているのか、実際にどこに行くのかをベンチマークしていませんが、すべての初期アニメーションパラメータを事前に計算し、それらをすべて配列に格納してから、このように現在の関数を最適化できます。次のようにすべての事前計算を行った後、すべてのアニメーションを開始します。
アイデア#1:
function runAnimation() {
// create node list of paths
var allPaths = document.getElementById('svgcontainer').getElementsByTagName('path');
// define the animate function
var doAnim = function(currentPath, dur, begin) {
setTimeout(function(){
$(currentPath).animate({'stroke-dashoffset': 0}, dur);
}, begin);
};
var anims = [];
// iterate through the nodelist
for (var i=0, len = allPaths.length; i<len; i++) {
var pathAnim = allPaths[i].firstChild;
startTime = parseFloat(pathAnim.getAttribute('begin'));
pathDuration = parseFloat(pathAnim.getAttribute('dur'));
// change times from seconds to milliseconds
startTime = startTime * 1000;
pathDuration = pathDuration * 1000;
// accumulate animation parameters, but don't start animation yet
anims.push([allPaths[i], pathDuration, startTime]);
}
// now start all animations as fast as possible
for (var i = 0, len = anims.length; i < len; i++) {
doAnim.apply(this, anims[i]);
}
}
正直なところ、このコードの変更は大きな節約にはならないように見えます(このコードでは、多くの時間がかかる可能性のある多くのことは発生していません)が、ループのサイズが大きい場合は、有意義な改善。
アイデア#2:
ここに別のアイデアがあります。doAnim()
アニメーションを並べ替えて、 startTimeが最も速いアニメーションを呼び出します。これにより、アニメーションの初期化中にaが起動する可能性が低くなりsetTimeout()
ます。
function runAnimation() {
// create node list of paths
var allPaths = document.getElementById('svgcontainer').getElementsByTagName('path');
// define the animate function
var doAnim = function(currentPath, dur, begin) {
setTimeout(function(){
$(currentPath).animate({'stroke-dashoffset': 0}, dur);
}, begin);
};
var anims = [];
// iterate through the nodelist
for (var i=0, len = allPaths.length; i<len; i++) {
var pathAnim = allPaths[i].firstChild;
startTime = parseFloat(pathAnim.getAttribute('begin'));
pathDuration = parseFloat(pathAnim.getAttribute('dur'));
// change times from seconds to milliseconds
startTime = startTime * 1000;
pathDuration = pathDuration * 1000;
// accumulate animation parameters, but don't start animation yet
anims.push([allPaths[i], pathDuration, startTime]);
}
// sort array so that smallest startTime values are last
anims.sort(function(a, b) {
return(b[2] - a[2]);
});
// Now start all animations as fast as possible
// Because the array is sorted, it will start the longer setTimeout()
// calls first and lessen the chance that the short ones will not get
// get to fire when they want to
for (var i = 0, len = anims.length; i < len; i++) {
doAnim.apply(this, anims[i]);
}
}
これ以外の修正は、.animate()コード自体に含まれている必要があります。これには、独自のセットアップ時間が含まれているため、すべてのオブジェクトが同時にアニメーションを開始しようとすると、スムーズになりません。
SetTimeout()がイベントキューでどのように機能するかについての注意:
方法は次のとおりですsetTimeout()
javascriptイベントキューで動作します。システムタイマーは、将来のある時点でスケジュールされます。その時間に達すると、そのタイマーのイベントがjavascriptイベントキューに入れられます。その時点でjavascriptエンジンがアイドル状態の場合、対応するコールバックがすぐに実行されます。javascriptエンジンが現在何か他のものを実行している場合、そのタイマーはイベントキューにとどまります。現在実行中のjavascriptスレッドが実行を終了すると、イベントキューにさらにイベントがあるかどうかを確認します。待機中のイベントがある場合は、最も古いイベントをキューから取り出して実行を開始します。このプロセスは、実行のjavascriptスレッドが終了し、キューにイベントがなくなるまで繰り返されます。javascriptイベントは一度に1つしか実行できませんが、
ご覧のとおり、javascriptはシングルスレッドであるため、同時に実行することがたくさんある場合(開始するアニメーションがたくさんある場合など)、実際にはそのうちの1つだけが時間どおりに開始され、他のすべては時間どおりに開始されます。少し遅れます。
特定のコードで、コードが多数のアニメーションをすべて同時に開始しようとしている場合(たとえば、すべてが同じまたは非常に近いstartTime値で)、最初のアニメーションを開始し、次に2番目のアニメーションを開始します、3番目のアニメーションが開始されました。他のすべてのアニメーションが開始されていますが、実際にアニメーションを表示するタイマーが、開始しようとしているすべてのアニメーションの背後でキューに残っているため、実際のアニメーションはまだ実行されていません。重要なのは、アニメーションの実行後に発生する必要のある作業の量を最小限に抑え、作業を分散させて、一度に大量の作業が行われないようにすることです。