D3 forceSimulation和拖拽,node.fx/node.fy是什么?

Joe*_*Joe 0 javascript drag d3.js

对于包含 d3-drag 拖动功能的 d3 force 布局,似乎每个拖动事件调用的函数都修改d.fx/ d.fy,例如:

function dragged(d) {
  d.fx = d3.event.x;
  d.fy = d3.event.y;
}
Run Code Online (Sandbox Code Playgroud)

拖动开始事件通常基于d.fx/d.fyd.x/d.y而结束事件设置d.fx/d.fynull

d.fx/d.fy来自哪里以及为什么它会用于被拖动的元素?这是否以某种方式内置于 d3 或 d3-force 中?它在哪里分配给被拖动的元素?

And*_*eid 5

d3 力布局和node.fx/fy

在 d3 力模拟中,节点的 fx/fy 属性可用于为该节点设置固定位置。如果 fx/fy 值未定义或为空,则节点可以自由移动。如果设置了它们,则节点的 x/y 属性将始终设置为匹配 fx/fy 属性:

在每次滴答结束时,在施加任何力之后,具有定义 node.fx 的节点将 node.x 重置为此值并将 node.vx 设置为零;同样,具有定义 node.fy 的节点会将 node.y 重置为此值,并将 node.vy 设置为零。要取消修复先前已修复的节点,请将 node.fx 和 node.fy 设置为 null,或删除这些属性。(文档

这些 fx/fy 属性通常用于修复节点,而不仅仅是在拖动事件期间。

在 d3 力布局中拖动事件的应用程序:

在 d3 力模拟中,每个节点的位置在每个刻度上更新。在整个模拟过程中,tick 会反复触发以保持节点位置更新,它的速度足够快以显示节点移动的动画。

拖动时,您希望将节点的位置保持在鼠标所在的位置。在拖动过程中,每次移动鼠标时,都会触发拖动事件。除非鼠标移动,否则它不会连续发射。

拖动时,我们不想对被拖动的节点施加力:我们希望节点跟随鼠标(我们通常也不希望通过在拖动期间停止模拟来冻结其余节点)。

为了消除力布局对拖动节点的影响,我们可以设置node.fx/fy属性,使力不会将节点拉离鼠标位置。拖动完成后,我们希望取消设置(使用null)这些值,以便力将再次定位节点。

在下面的代码段中,展示了两种力布局。每个人的行为都会不同:

  • 在红色布局节点中有fx/fy属性设置为拖动期间的鼠标位置。
  • 在蓝色布局节点中,只需在拖动期间将它们的x/y属性设置为鼠标位置即可。

在红色布局中,力不会在拖动过程中重新定位节点。在蓝色布局中,力将在拖动过程中继续作用在节点上。在蓝色示例中,拖动和强制都根据各自的规则连续放置节点,但通常滴答事件通常会足够频繁地放置节点,因此拖动可能不太明显。尝试稍微拖动蓝色节点然后不要移动鼠标 - 它只会根据力布局漂移:

在这两个示例中,拖动函数仍会根据拖动节点的位置更新力布局

var data1 ={ "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 data2 ={ "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);
  
// FIRST SIMULATION
var simulation1 = d3.forceSimulation()
    .force("link", d3.forceLink().id(function(d) { return d.id; }).distance(50))
    .force("charge", d3.forceManyBody())
    .force("center", d3.forceCenter(width / 3, height / 2));
    
var link1 = svg.append("g")
  .selectAll("line")
  .data(data1.links)
  .enter().append("line")
  .attr("stroke","black");

var node1 = svg.append("g")
 .selectAll("circle")
 .data(data1.nodes)
 .enter().append("circle")
 .attr("r", 10)
 .call(d3.drag()
   .on("drag", dragged1)
   .on("end", dragended1))
 .attr("fill","crimson");
 
simulation1.nodes(data1.nodes)
 .on("tick", ticked1)
 .alphaDecay(0)
 .force("link")
 .links(data1.links);
      
function ticked1() {
 link1
   .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; });
 node1
   .attr("cx", function(d) { return d.x; })
   .attr("cy", function(d) { return d.y; });
}    
    
function dragged1(d) {
  d.fx = d3.event.x;
  d.fy = d3.event.y;
}

function dragended1(d) {
  d.fx = null;
  d.fy = null;
}

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

var node2 = svg.append("g")
 .selectAll("circle")
 .data(data2.nodes)
 .enter().append("circle")
 .attr("r", 10)
 .call(d3.drag()
   .on("drag", dragged2))
 .attr("fill","steelblue");
 
simulation2.nodes(data2.nodes)
 .on("tick", ticked2)
 .alphaDecay(0)
 .force("link")
 .links(data2.links);
      
function ticked2() {
 link2
   .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; });
 node2
   .attr("cx", function(d) { return d.x; })
   .attr("cy", function(d) { return d.y; });
}    
    
function dragged2(d) {
  d.x = d3.event.x;
  d.y = d3.event.y;
}
Run Code Online (Sandbox Code Playgroud)
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.10.0/d3.min.js"></script>
Run Code Online (Sandbox Code Playgroud)

d在拖动功能是所述节点的数据阵列中的一个单独的节点(节点被拖动),从该力布局立足其计算和它的位置更新

此外,一些拖动开始的事件可能会使用d.fx = d.x,这只会将节点的位置设置为其当前位置(如我上面所做的那样),您也可以使用鼠标的当前位置而没有任何明显差异。