2012-06-18 5 views
12

私はバックボーンビューでD3を使ってグラフの視覚化に取り組んでいます。私は、ユーザーがグラフをピンチズームしたり、Webkit変換を使ってスムーズに移行したり、リリース時に再描画することを許可します。コードを単純にするために、要素の新しい位置とサイズを再計算するのではなく、新しいスケールでグラフを再描画しています(これは私の元のアプローチでしたが、私のチームは再描画ルートを要求しました)。DOMノードのクリーンアップはどのようにd3で機能しますか?

[私はBostockとTwitterで話しました。これは実際には良いことではありません]

私が気づいていることは、再描画するたびに、クリーンアップされていないドンノードをダンプすることです。

イベントハンドラ/クロージャ内の循環参照とは関係ありません。ラベル以外のすべてのラベルを無効にしています(ハンドラは添付されていません)。同様の動作が発生します。

グラフから要素を積極的に削除しようとしましたが、DOMノードがまだ漏れているようです。

ここにいくつかの関連するコードがあります。新しいラベルのセットに対して 'render'が呼び出されます。ズームが終了すると、「近くには」古いグラフに呼び出され、新しいものが別のビューのインスタンスを使用して作成し、「レンダリング」の呼び出しです:

render: function() { 

     // create the svg offscreen/off dom 
     //document.createElementNS(d3.ns.prefix.svg, "svg") 
     var svg = this.svg = d3.select(this.el) 
      .append("svg:svg") 
      .attr('width', this.VIEW_WIDTH) 
      .attr('height', this.VIEW_HEIGHT) 

     this._drawTimeTicks.call(this, true); 
     return this; 
    }, 



_drawTimeTicks: function(includeLabels) { 
    var bounds = this.getDayBounds(); 
    var min = bounds.start; 
    var date = new Date(min); 
    var hour = 1000 * 60 * 60; 
    var hourDiff = 60 * this.SCALE; 
    var graphX = (date.getTime() - min)/1000/60; 
    var textMargin = 7; 
    var textVert = 11; 

    // Using for loop to draw multiple vertical lines 
    // and time labels. 

    var timeTicks = d3.select(this.el).select('svg'); 
    var width = timeTicks.attr('width'); 
    var height = timeTicks.attr('height'); 

    for (graphX; graphX < width; graphX += hourDiff) { 
     timeTicks.append("svg:line") 
      .attr("x1", graphX) 
      .attr("y1", 0) 
      .attr("x2", graphX) 
      .attr("y2", height) 
      .classed('timeTick'); 

     if (includeLabels) { 
      timeTicks.append("svg:text") 
       .classed("timeLabel", true) 
       .text(this.formatDate(date)) 
       .attr("x", graphX + textMargin) 
       .attr("y", textVert); 
     } 

     date.setTime(date.getTime() + hour); 
    } 



close: function() { 
     console.log("### closing the header"); 
     this.svg.selectAll('*').remove(); 
     this.svg.remove(); 
     this.svg = null; 
     this.el.innerHTML = ''; 

     this.unbind(); 
     this.remove(); 
    } 

あなたが見ることができるように、私はトリッキーな何もしていませんよイベントハンドラまたはクロージャを使用します。いくつかのズームのやりとりでは、GCで再生できない数十のDOMノードが漏れてしまいます。

これはメモリーリークですか、またはd3が将来のグラフの作成/更新を最適化するための裏側で何かをしていますか?私が気づいていないグラフを破壊する良い方法がありますか?

アイデア?

答えて

20

D3はあなたのノードへの隠れた参照を保持しないので、「DOMノードのクリーンアップ」の内部概念はありません。 DOM要素の配列である選択肢とDOM自体があります。 DOMから要素を削除し、それに対する追加の参照を保持しない場合は、ガベージコレクタによってその要素を再利用する必要があります。以前は一部の古いブラウザでDOM間に循環参照がガベージコレクションされていたものがありました(DOMの間にガベージコレクションのガベージコレクションがありました)要素やJavaScriptのクロージャなどがありますが、現代のブラウザに影響するような問題は認識していません)。

DOMを更新する場合、最も一般的な方法はデータ結合ですこれにより、既存の要素を再利用し、変更する必要のある属性のみを変更することができます。データ結合にキー機能を使用することも、object constancyのための良い考えです。同じデータが更新前と更新後の両方に表示される場合は、すべての属性を再計算する必要はなく、ブラウザの更新が少なくなります。

場合によっては、子孫要素の位置を更新するのではなく、囲むG要素の変換属性を更新するなど、より高速な任意の更新の代替方法があります。別の例として、viewBox属性を変更するだけで、SVG要素内で幾何学的なズームを行うことができます。しかし幾何学的なズーミングは非常に限られたケースです。最も効率的な更新は、正確に何が変化しているかによって異なります。追加または削除する要素の数を最小限に抑え、再計算が必要な属性の数を最小限に抑えるために、データ結合を使用します。

あなたはむしろ、forループを使用するよりも、同時に複数のティックを作成するには、参加したデータを使用することができ、私は指摘しますカップル他のもの...

  • 。 Forループは、データ結合がループのない複数の要素(および階層構造)を作成できるため、D3ではほとんど使用されません。さらに、軸コンポーネント(d3.svg.axis)と時間スケール(d3.time.scale)を時間フォーマット(d3.time.format)で使用してください。 D3の

  • 最近のバージョンでは、「SVG:」必要としない名前空間を、あなたが追加することができ、「テキスト」、「ライン」など

  • 私はどこselectAll("*").remove()なりますどのような状況を考えることはできませんセンス。 "*"セレクタはすべての子孫にマッチするので、親からのすべての子孫が削除されます。 SVGコンテナの一番上の要素(ここでは、子要素の重複除去ではなく)を常に削除してください。

+0

ありがとうございました。私はさまざまなズームオプションを見ていきますが、私が以前に使っていなかったのは、グラフが四角形内にフォントサイズの固定されたテキストを表示していて、ズームイン/アウト時に切り捨てが必要であるということです。しかし、再び、すべての助けを感謝:) – Gopherkhan

+0

ええ、幾何学的なズーミングが不十分である理由の多くがあります。ズームレベルを上げながらフォントサイズを小さくするなどして、問題を修正することもできますが、多くの場合、要素をプログラマチックに配置する方が簡単です。 – mbostock

+0

これは古い質問ですが、DOMノードのクリーンアップに関するいくつかのIE特有のノートについてはこちらをご覧ください:http://stackoverflow.com/questions/18575452/why-do-my-svg-nodes-leak-memory-in 〜 – explunit

関連する問題