修复D3强制定向布局中的节点位置

时间:2012-05-01 01:29:00

标签: javascript d3.js transform force-layout

我希望我的力导向布局中的一些节点忽略力并根据节点的属性保持固定位置,同时仍然可以拖动并在其他节点上施加排斥并保持其链接线。我认为这很简单:

    force.on("tick", function() {
        vis.selectAll("g.node")
            .attr("transform", function(d) {
                return (d.someAttribute == true) ?
                    "translate(" + d.xcoordFromAttribute + "," + d.ycoordFromAttribute +")" :
                    "translate(" + d.x + "," + d.y + ")"
            });
     });

我还尝试手动设置节点的x和y属性on-tick,但是如果受到力的影响,链接将继续浮动到节点所在的位置。

显然,我对这应该如何工作有一个基本的误解,所以有人可以指出一个例子,其中一些节点固定在它们的位置(但仍然可拖动),其余节点浮动在力导向 - 像,所有链接仍在工作?

2 个答案:

答案 0 :(得分:74)

将所需节点上的d.fixed设置为true,并将d.xd.y初始化为所需位置。这些节点仍然是模拟的一部分,您可以使用正常的显示代码(例如,设置变换属性);但是,因为它们被标记为固定,所以它们只能通过拖动而不是通过模拟来移动。

有关详细信息,请参阅force layout文档,还可以查看根节点在this example中的位置。

答案 1 :(得分:24)

d3v4和d4v5强制布局中的固定节点

在d3v3中d.fixed将修复d.xd.y处的节点;但是,在d3v4 / 5中,不再支持此方法。 d3 documentation州:

  

要将节点固定在给定位置,您可以指定另外两个节点   属性:

     

fx - the node’s fixed x-position

     

fy - the node’s fixed y-position

     

在每个刻度结束时,在施加任何力之后,一个节点   使用已定义的node.fx将node.x重置为此值并设置node.vx   为零;同样,具有已定义node.fy的节点已将node.y重置为   此值和node.vy设置为零。取消固定的节点   先前已修复,将node.fx和node.fy设置为null,或删除这些   属性。

您可以为数据源中的强制节点设置fxfy属性,也可以动态添加和删除fxfy值。下面的代码段在拖动事件结束时设置这些属性,只需拖动一个节点来修复其位置:

var data ={ 
 "nodes": 
  [{"id": "A"},{"id": "B"},{"id": "C"},{"id":"D"}], 
 "links": 
  [{"source": "A", "target": "B"}, 
   {"source": "B", "target": "C"},
   {"source": "C", "target": "A"},
   {"source": "D", "target": "A"}]
}
var height = 250;
var width = 400;

var svg = d3.select("body").append("svg")
  .attr("width",width)
  .attr("height",height);
  
var simulation = d3.forceSimulation()
    .force("link", d3.forceLink().id(function(d) { return d.id; }).distance(50))
    .force("charge", d3.forceManyBody())
    .force("center", d3.forceCenter(width / 2, height / 2));
    
var link = svg.append("g")
  .selectAll("line")
  .data(data.links)
  .enter().append("line")
  .attr("stroke","black");

var node = svg.append("g")
 .selectAll("circle")
 .data(data.nodes)
 .enter().append("circle")
 .attr("r", 5)
 .call(d3.drag()
   .on("drag", dragged)
   .on("end", dragended));
 
simulation
 .nodes(data.nodes)
 .on("tick", ticked)
 .alphaDecay(0);

simulation.force("link")
 .links(data.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 dragged(d) {
  d.fx = d3.event.x;
  d.fy = d3.event.y;
}

function dragended(d) {
  d.fx = d3.event.x;
  d.fy = d3.event.y;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.6.0/d3.min.js"></script>