Javascript 修复 D3 力导向布局中的节点位置

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/10392505/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-24 01:08:19  来源:igfitidea点击:

Fix Node Position in D3 Force Directed Layout

javascriptd3.jsdata-visualizationforce-layoutd3-force-directed

提问by Elijah

I want some of the nodes in my force-directed layout to ignore all forces and stay in fixed positions based on an attribute of the node, while still being able to be dragged and exert repulsion on other nodes and maintain their link lines.

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

I thought it would be as simple as this:

我认为它会像这样简单:

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 + ")"
        });
  });

I have also tried to manually set the node's x and y attributes each tick, but then the links continue to float out to where the node would be if it was affected by the force.

我还尝试在每个刻度上手动设置节点的 x 和 y 属性,但是如果节点受到力的影响,链接会继续浮动到节点所在的位置。

Obviously I have a basic misunderstanding of how this is supposed to work. How can I fix nodes in a position, while keeping links and still allowing for them to be draggable?

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

回答by mbostock

Set d.fixedon the desired nodes to true, and initialize d.xand d.yto the desired position. These nodes will then still be part of the simulation, and you can use the normal display code (e.g., setting a transform attribute); however, because they are marked as fixed, they can only be moved by dragging and not by the simulation.

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

See the force layout documentation for more details (v3 docs, current docs), and also see how the root node is positioned in this example.

有关更多详细信息,请参阅 force layout 文档(v3 docscurrent docs),并查看本示例中根节点的位置。

回答by Andrew Reid

Fixed nodes in force layout for d3v4 and d4v5

修复了 d3v4 和 d4v5 强制布局中的节点

In d3v3 d.fixedwill fix nodes at d.xand d.y; however, in d3v4/5 this method no longer is supported. The d3 documentationstates:

在 d3v3d.fixed中将修复节点在d.xd.y;但是,在 d3v4/5 中不再支持此方法。在D3的文件中指出:

To fix a node in a given position, you may specify two additional properties:

fx - the node's fixed x-position

fy - the node's fixed y-position

At the end of each tick, after the application of any forces, a node with a defined node.fx has node.x reset to this value and node.vx set to zero; likewise, a node with a defined node.fy has node.y reset to this value and node.vy set to zero. To unfix a node that was previously fixed, set node.fx and node.fy to null, or delete these properties.

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

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,或删除这些属性。

You can set fxand fyattributes for the force nodes in your data source, or you can add and remove fxand fyvalues dynamically. The snippet below sets these properties at the end of drag events, just drag a node to fix its position:

您可以为数据源中的力节点设置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>