web-dev-qa-db-ja.com

力指向グラフでアニメーションを無効にする方法は?

D3の力指向グラフでアニメーションを無効にする方法はありますか?

私はこの例で作業しています: https://bl.ocks.org/mbostock/4062045

最初のアニメーションなしでグラフをレンダリングしたい、つまり、すべてのノードとリンクを最終的な位置に表示したい。

12
sosloucr

[〜#〜]編集[〜#〜]

このメソッドは、シミュレーションのアニメーション部分を非表示にするだけです。 Gerardo Furtadoの回答 を参照してください。これは、中間結果を描画せずにシミュレーションを実行します。つまり、ユーザーは待つ必要がありません。ソリューションがゆっくりと進化している間。

========

「アニメーション」は実際に実行中のシミュレーションです。シミュレーションの実行時間で遊ぶことは可能ですが、これはノードが極小値でスタックすることを意味する場合があります- 詳細についてはこちらのドキュメントを参照してください

シミュレーションが終了したときに発生するendイベントにリスナーを追加するオプションがあります。グラフが最初は非表示になっていて、シミュレーションが終了すると表示されるスニペットを作成しました。

別の方法は、チャートをサーバー側でレンダリングし(これがオプションの場合)、d3でさらに操作できる既製のSVGを提供することです。

var svg = d3.select("svg"),
  width = +svg.attr("width"),
  height = +svg.attr("height");

var color = d3.scaleOrdinal(d3.schemeCategory20);

var simulation = d3.forceSimulation()
  .force("link", d3.forceLink().id(function(d) {
    return d.id;
  }))
  .force("charge", d3.forceManyBody())
  .force("center", d3.forceCenter(width / 2, height / 2))
  .on('end', function() {
    svg.classed('hidden', false)
    d3.select('#loading').remove()
  });

// I wasn't able to get the snippet to load the original data from https://bl.ocks.org/mbostock/raw/4062045/miserables.json so this is a copy hosted on glitch
d3.json("https://cdn.glitch.com/8e57a936-9a34-4e95-a03d-598e5738f44d%2Fmiserables.json", function(error, graph) {
  if (error) {
    console.log(error)
  };

  var link = svg.append("g")
    .attr("class", "links")
    .selectAll("line")
    .data(graph.links)
    .enter().append("line")
    .attr("stroke-width", function(d) {
      return Math.sqrt(d.value);
    });

  var node = svg.append("g")
    .attr("class", "nodes")
    .selectAll("circle")
    .data(graph.nodes)
    .enter().append("circle")
    .attr("r", 5)
    .attr("fill", function(d) {
      return color(d.group);
    })
    .call(d3.drag()
      .on("start", dragstarted)
      .on("drag", dragged)
      .on("end", dragended));

  node.append("title")
    .text(function(d) {
      return d.id;
    });

  simulation
    .nodes(graph.nodes)
    .on("tick", ticked);

  simulation.force("link")
    .links(graph.links);

  function ticked() {
    link
      .attr("x1", function(d) {
        return d.source.x;
      })
      .attr("y1", function(d) {
        return d.source.y;
      })
      .attr("x2", function(d) {
        return d.target.x;
      })
      .attr("y2", function(d) {
        return d.target.y;
      });

    node
      .attr("cx", function(d) {
        return d.x;
      })
      .attr("cy", function(d) {
        return d.y;
      });
  }
});

function dragstarted(d) {
  if (!d3.event.active) simulation.alphaTarget(0.3).restart();
  d.fx = d.x;
  d.fy = d.y;
}

function dragged(d) {
  d.fx = d3.event.x;
  d.fy = d3.event.y;
}

function dragended(d) {
  if (!d3.event.active) simulation.alphaTarget(0);
  d.fx = null;
  d.fy = null;
}
.links line {
  stroke: #999;
  stroke-opacity: 0.6;
}

.nodes circle {
  stroke: #fff;
  stroke-width: 1.5px;
}

.hidden {
  visibility: hidden
}

img {
    display: block;
    margin-left: auto;
    margin-right: auto;
   }
<script src="https://d3js.org/d3.v4.min.js"></script>
<img id ="loading" src="http://thinkfuture.com/wp-content/uploads/2013/10/loading_spinner.gif" />
<svg width="960" height="600" class="hidden"></svg>
0
John

この質問にはすでに 受け入れられた回答 がありますが、提案された解決策は、D3フォースチャートのアニメーションを無効にする正しい方法ではありません。ブラウザはまだすべてのティックでノードとリンクを移動しています!あなたはそれらが動くのを見ないだけですが、ブラウザはそれらを動かし、多くの計算を行い、多くの時間/リソースを浪費しています。また、これにはサーバー側は必要ありません。

私の答えは、実際にはアニメーションを描画しない別の解決策を提案しています。たとえば、Mike Bostock(D3作成者)による このコード で確認できます。

このソリューションは、tick関数とは何かを理解していれば、簡単に理解できます。これは、シミュレーションのすべての位置を計算し、1ステップ進むだけの関数です。 D3の力指向グラフの大部分は、ティックごとにノードとリンクを描画しますが、それを行う必要はありません。

これがあなたができることです:

  1. シミュレーションを定義した直後に、stop()を使用してシミュレーションを停止します。

    _var simulation = d3.forceSimulation(graph.nodes)
        .force("link", d3.forceLink().id(function(d) { return d.id; }))
        .force("charge", d3.forceManyBody())
        .force("center", d3.forceCenter(width / 2, height / 2))
        .stop();//stop the simulation here
    _
  2. 何も描画せずにシミュレーションを実行します。これが最も重要なステップです。各ティックで要素を移動する必要がありません。ここでは、300ティックを実行しています。これは、ほぼデフォルトの数値です。

    _for (var i = 0; i < 300; ++i) simulation.tick();
    _
  3. 次に、シミュレーションで作成されたプロパティ(xysourcetarget)を使用して、円と線を描画します一度だけ

    _node.attr("cx", function(d) { return d.x; })
        .attr("cy", function(d) { return d.y; })
    _

これらの変更のみが含まれるリンクされたブロックは次のとおりです。 http://bl.ocks.org/anonymous/8a4e4e2fed281ea5e2a5c804a9a03783/85ced3ea82a4bed20a2010530562b655d8f6e464

このソリューションの時間と「ノードを隠す」ソリューションの時間(受け入れられた答え)を比較します。ここのこれはずっと速いです。私のテストでは、次の結果が得られました。

  • 「ノードを隠す」ソリューション:約5000ms
  • このソリューション:約200ms

つまり、25倍高速です。

PS:簡単にするために、フォークされたブロックのticked関数を削除しました。ノードをドラッグする場合は、ノードを追加し直します。


D3 v5.8の編集

D3v5.8でインタラクションの数をsimulation.tick()に渡すことができるようになったので、forループも必要なくなりました。したがって、代わりに:

_for (var i = 0; i < 300; ++i) simulation.tick();
_

あなたはただすることができます:

_simulation.tick(300);
_
19
Gerardo Furtado