0

jqueryanimate()を使用してsvgマークアップから値を取得し、アニメーションを作成しています。(IEはSMILをサポートしていないため、スクリプトを使用して実行する必要があります。)

問題は次のとおりです。アニメーションには数千の要素があり、それぞれに独自の開始時間があります。そのため、for各要素を反復処理するために使用しているループには数秒かかり、低速のマシンやブラウザではさらに多くの時間がかかります。そのため、イベントキューが実際に開始されるまでに、ランタイムはすでにしばらくの間、数秒が経過しているように感じられます。そのため、アニメーションの最初の部分がぎくしゃくします。アニメーションはtime=zeroから始まります。アニメーションの先頭が途切れる場合があります。

ブラウザが異なれば、これも異なる方法で処理されるように見えます。また、プロセッサの状態にも依存するようです。そのため、何が起こっているのかを体系的に把握するのは困難です。)また、これは、アニメーションと非常に長いランタイムの両方への私の最初の進出です。私は何か間違ったことをしていると確信しています:)

したがって、私の具体的な質問は、この動作を回避する方法です。しかし、より一般的には、イベントキューのタイマーはランタイムの開始時に開始しますか、それとも終了時に開始しますか?これは正確にどのように機能しますか?

関連するコードのほとんどは次のとおりです。

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);
  };

  // iterate through the nodelist
  for (var i=0; i<allPaths.length; 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;

    doAnim(allPaths[i], pathDuration, startTime);
  }
}
4

3 に答える 3

2

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番目のアニメーションが開始されました。他のすべてのアニメーションが開始されていますが、実際にアニメーションを表示するタイマーが、開始しようとしているすべてのアニメーションの背後でキューに残っているため、実際のアニメーションはまだ実行されていません。重要なのは、アニメーションの実行後に発生する必要のある作業の量を最小限に抑え、作業を分散させて、一度に大量の作業が行われないようにすることです。

于 2012-06-20T01:00:55.000 に答える
0

アニメーションとJavaScript(jQuery)のパフォーマンスに関してすでに述べたことは、残念ながら非常に真実です。そのようなことをする方法を見つけようとして何年もの間、私はいくつかの障害を克服しましたが、結局それはすべて同じです:you have to give more than you'll get.

パフォーマンスの問題についてできることと、得られる以上のことをする理由は次のとおりです。

何を、どのように、そしてなぜ

jQueryはJavaScript関数を使用setIntervalして、オブジェクトの変更を反映するようにDOMを更新します。(位置、アスペクト、不透明度など)。

関数はjQueryでstepとして書き直され、stepは13ミリ秒ごとに起動するように設定されています。サイズ、形状、その他に関係なく、非常に多くのオブジェクトをアニメーション化しているため、ブラウザの過負荷になっています。

では、これについて何ができるでしょうか。

ここには2つの選択肢があり、どちらもエレガントなソリューションではありません。

アニメーションが実行される間隔を変更することもできます。これを行う際には、数千の要素を考慮に入れて、パフォーマンスと美的比率に最適なリフレッシュレートを決定する必要があります。

$ .fx.interval

ただし、一部のブラウザはこれを異なる方法で解釈することに注意してください。それをさらに最適化することを目的としたいくつかのプラグインもあり、いくつかは成功します。ただし、推奨事項はありませんので、グーグルで検索する必要があります。

ちなみに、上記はすべてのアニメーションのキュータイマーを変更します。特定のアニメーションでのみ機能する必要がある場合は、グーグルですばやく回答できます。これは、客観的な情報にすぎません。

または、特定の位置にある要素のスナップショットを取り、必要に応じて操作および交換できるより大きな画像にすることができます。爆発などを使用している場合、これは機能しない可能性がありますが、通常、このソリューションを使用して目的の効果を得ることができます。

これがお役に立てば幸いです

于 2012-06-20T00:38:21.643 に答える
0

2つのポイント:

  • Javascriptはシングルスレッドです。
  • 各setTimeoutは、新しいスレッドを起動します。

これらの2つの事実を合計するとfor、アニメーションの実行が開始される前にループが完了していることが保証されていることがわかります。

forループが遅延に比べて完了するまでに長い時間がかかる場合はstartTime、forループのスレッドが完了するとすぐに、キューに入れられた多くのアニメーションを開始できるようになります。慎重に計画されたすべてのマイクロタイミングがウィンドウから消えました。

うまくいけば、これはあなたが説明する振る舞いへの洞察を提供します。

編集:

jfriend00のアイデアに基づいて、これは(バグを除いて)アイデア#2と同じことを実現しますが、高価なソートの必要性を回避するものです。

function runAnimation() {
    var allPaths = document.getElementById('svgcontainer').getElementsByTagName('path'),
        pathAnim, startTime, pathDuration, anims = [],
        i, j, arr;

    function doAnim(i, dur, begin) {
        setTimeout(function(){
            $(allPaths[i]).animate({'stroke-dashoffset': 0}, dur);
        }, begin);
    }

    // iterate through the nodelist
    for (i = 0, len = allPaths.length; i<len; i++) {
        pathAnim = allPaths[i].firstChild;
        startTime = pathAnim.getAttribute('begin');
        pathDuration = pathAnim.getAttribute('dur');
        // Accumulate animation parameters in such a way that 
        // we avoid the need for an expensive array sort later
        if(!anims[startTime]) {
            anims[startTime] = [];//Allow for multiple events per startTime
        }
        anims[startTime].push([i, pathDuration*1000, startTime*1000]);
    }

    // At this point we have a sparse array indexed by startTime and containing arrays of animation data.
    // To iterate over a sparse array efficiently, we have to do something slightly tricky
    // ref: http://hexmen.com/blog/2006/12/iterating-over-sparse-arrays/
    for (arr in anims) {
        if (String(arr >>> 0) == arr && arr >>> 0 != 0xffffffff) {
            for(j = 0, len = arr.length; j < len; j++) {
                doAnim.apply(this, arr[j]);
            }
        }
    }
}

>>>スパースイテレータの内容を理解しようとしないでください。私はそれを完全には理解していません。私はそれが機能することを知っています;-)。ただし、これまで大量のタイムクリティカルなコンテキストで使用したことがないため、十分に高速かどうかを確認するのは興味深いことです。

于 2012-06-20T02:54:45.580 に答える