74

我希望我的力导向布局中的一些节点忽略所有力并根据节点的属性保持在固定位置,同时仍然能够被拖动并对其他节点施加排斥并保持它们的链接线。

我认为它会像这样简单:

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 属性,但是如果节点受到力的影响,链接会继续浮动到节点所在的位置。

显然,我对这应该如何工作有一个基本的误解。如何将节点固定在某个位置,同时保持链接并仍然允许它们可拖动?

4

2 回答 2

83

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

有关更多详细信息,请参阅强制布局文档(v3 文档当前文档),还可以查看此示例中根节点的位置。

于 2012-05-01T15:34:38.983 回答
44

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

在 d3v3中,将在和d.fixed处修复节点;但是,在 d3v4/5 中不再支持此方法。d3 文档指出:d.xd.y

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

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>

d3v6 对事件监听器的更改

在上面的代码片段中,拖动事件使用表单

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

d被拖动节点的基准在哪里。在 d3v6 中,现在的形式是:

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

或者:

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

事件现在直接传递给监听器,传递给事件监听器的第二个参数是数据。这是Observable 上的规范示例。

于 2017-06-20T04:52:32.390 回答