D3 v4中的静力布局

时间:2016-08-26 15:58:34

标签: force-layout d3.js-v4

我正在尝试在D3 v4中重现this example ("Static Force Layout" by Mike Bostock)

我将迈克的代码与我在创建动态力图(我可以找到here)时学到的内容结合起来。

我(可能是错误地)认为制作静态力图会更容易,但我并不完全理解Mike教程的逻辑,我不能“将其翻译成v4语言”。

This is how far I got.

在Mike的示例中,每次刷新页面时,节点都会采用不同的位置。我的理解是每个tick操作随机移动每个节点。因此,这部分代码(随机)确定了位置:

// Use a timeout to allow the rest of the page to load first.
setTimeout(function() {

// Run the layout a fixed number of times.
// The ideal number of times scales with graph complexity.
// Of course, don't run too long—you'll hang the page!
force.start();
for (var i = n * n; i > 0; --i) force.tick();
force.stop();

svg.selectAll("line")
  .data(links)
  .enter().append("line")
  .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; });

svg.selectAll("circle")
  .data(nodes)
  .enter().append("circle")
  .attr("cx", function(d) { return d.x; })
  .attr("cy", function(d) { return d.y; })
  .attr("r", 4.5);

loading.remove();
}, 10);

其中n是任意设定的。

这就是我翻译那部分的方式:

setTimeout(function() {

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

var n = 1000;
//I thought that since I have many nodes and edges in my graph I should use a highgher n. However, wheter n=10 or 1000 nothing changes in the final result

for (var i = n * n; i > 0; --i) simulation.tick();

simulation
  .nodes(data.nodes)
  .on('tick', ticked);

simulation.force('link')
  .links(data.edges)
  .distance(distance);

function ticked () {
    d3.selectAll('circle')
        .attr('cx', function(d) { return d.x = Math.max(radius,  Math.min(width - radius, d.x)); })
        .attr('cy', function(d) { return d.y = Math.max(radius, Math.min(height - radius, d.y)); });

    d3.selectAll('line')
        .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; });        
}   
//Strange fact: I need to have both this tick function and the position attributes for each node and edge for the visualization to show something.

simulation.stop();

var link = graph.append("g")
    .attr('class', 'links')
    .selectAll("line")
    .data(data.edges)
    .enter()
    .append("line")
    .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; });

var node = graph.append("g")
    .attr('class', 'nodes')
    .selectAll("circle")
    .data(data.nodes)
    .enter()
    .append("circle")
    .attr("r", radius)
    .attr('fill', 'red')
    .attr('cx', function(d) { return d.x = Math.max(radius, Math.min(width - radius, d.x)); })
    .attr('cy', function(d) { return d.y = Math.max(radius, Math.min(height - radius, d.y)); });

loading.remove();
}, 10);

知道我做错了吗?

2 个答案:

答案 0 :(得分:0)

更新2016-10-18 下面关于实现一个不需要的滴答功能的东西,看起来完全不正确,这是我在摆弄一天左右之后所能说的。我想的不仅仅是删除帖子,我还会以后来的新理解更新它。为了清楚起见,从我所知道的情况来看,确实需要让模拟对你的对象做任何事情。

我现在正在通过类似的问题工作并且没有解决方案,但确实注意到一些可能值得重新考虑的代码:

来自on("tick", f(x)) doc

  

请注意,手动调用simulation.tick时不会调度tick事件;事件仅由内部计时器调度,用于模拟的交互式渲染。要影响模拟,请注册力而不是修改节点事件侦听器内的节点位置或速度。

这一切都让我相信以下代码的主体是不必要的,甚至可能是错误的:

function ticked () {
d3.selectAll('circle')
    .attr('cx', function(d) { return d.x = Math.max(radius,  Math.min(width - radius, d.x)); })
    .attr('cy', function(d) { return d.y = Math.max(radius, Math.min(height - radius, d.y)); });

d3.selectAll('line')
    .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; });        

}

希望有所帮助(我知道现在已经老了,但谁知道......)

答案 1 :(得分:-1)

您需要在计算结束位置之前停止并重新启动模拟:

    let _newNodes = null        
    const force = d3.forceSimulation(nodes);

    // bind listeners first
    force.on('tick', () => {
      const {nodes} = this.state;
      nodes.forEach(d => {  // keep within bounds
        d.x = Math.max(d.z, Math.min(this.props.width - d.z, d.x)); // incrementally derive bound x
        d.y = Math.max(d.z, Math.min(this.props.height - d.z, d.y)); // incrementally derive bound y
      });
      _newNodes = nodes;
    });

    // register force events
    force
      .force('y', d3.forceY(200).strength(0.01))
      // all of your configs here ...
      .stop();

    // calculate end state
    for (let i = 0, n = Math.ceil(Math.log(force.alphaMin()) / Math.log(1 - this.force.alphaDecay())); i < n; ++i) {
      force.tick();
    }
    // resume rendering
    force.restart();
相关问题