D3 forceSimulation和dragging,什么是node.fx / node.fy? [英] D3 forceSimulation and dragging, what is node.fx/node.fy?

查看:116
本文介绍了D3 forceSimulation和dragging,什么是node.fx / node.fy?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

For d3 force layouts that include drag functionality with d3-drag, it seems that the functions called on each drag event modify d.fx/d.fy, eg:

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

拖动启动事件通常基于 d.fx / d.fy on dx / dy 而结束事件将 d.fx / d.fy 设置为 null

The drag start event often bases d.fx/d.fy on d.x/d.y while the end event sets d.fx/d.fy to null.

在哪里 d.fx / d.fy 来自,为什么它被用于被拖动的元素?这是以某种方式构建成d3还是d3-force?它被分配给被拖动的元素在哪里?

Where does d.fx/d.fy come from and why does it get used on elements that are being dragged? Is this built into d3 or d3-force in some way? Where is it assigned to the element being dragged?

推荐答案

d3强制布局和 node.fx / fy



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

d3 force layout and node.fx/fy

Within a d3 force simulation, a node's fx/fy properties can be used to set a fixed position for that node. If the fx/fy values are undefined or null, the nodes is free to move around. If they are set, the x/y properties of the node will always be set to match the fx/fy properties:


最后每个tick的应用,在应用任何力之后,带有已定义node.fx的节点
将node.x重置为此值,node.vx将
设置为零;同样,具有已定义node.fy的节点将node.y重置为
此值,node.vy设置为零。要解除之前修复的
节点,请将node.fx和node.fy设置为null,或删除这些
属性。 ( docs

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

These fx/fy properties are used to fix nodes in general, not just during drag events.

在d3强制模拟中每个的位置节点在每个tick上更新。在整个模拟过程中反复点击以保持节点位置更新,它足够快以显示动画节点移动。

In a d3 force simulation the position of each node is updated on every tick. The tick fires repeatedly throughout the simulation to keep the nodes position updated, it does so fast enough to appear to animate the nodes movement.

拖动时你想保留节点的鼠标所在的位置。在拖动过程中,每次移动鼠标时,都会触发拖动事件。除非鼠标移动,否则它不会持续闪光。

While dragging you want to keep the node's position where the mouse is. During a drag, each time the mouse is moved, the drag event fires. It doesn't fire continuously unless the mouse moves.

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

When dragging we don't want to apply a force to the node being dragged: we want the node to follow the mouse (we generally also don't want to freeze the rest of the nodes by stopping the simulation during drags).

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

In order to remove the effects of the force layout on the dragged node, we can set the node.fx/fy properties so that the force doesn't pull the nodes away from the mouse position. When the drag is complete, we want to unset (using null) those values so the force will position the node again.

在下面的代码段中,显示了两个强制布局。每个都表现不同:

In the snippet below two force layouts are presented. Each will behave differently:


  • 在红色布局节点中有 fx / fy 拖动过程中设置为鼠标位置的属性。

  • 在蓝色布局节点中,只需设置 x / y 属性集在拖动过程中鼠标位置。

  • In the red layout nodes have there fx/fy properties set to the mouse position during the drag.
  • In the blue layout nodes simply have their x/y properties set to the mouse position during the drag.

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

In the red layout the force won't re-position a node during a drag. In the blue layout the force will continue to act upon a node during a drag. In the blue example both drag and force continuously place the node based on their individual rules, though normally tick events will generally place the node frequently enough that a drag may not be very visible. Try dragging the blue node a bit then don't move the mouse - it'll drift according to the force layout only:

在两个示例中,拖动功能仍然更新有关拖动节点位置的力布局

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

<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.10.0/d3.min.js"></script>

作为单个节点的拖动函数中的 d 节点数据数组(被拖动的节点),力布局基于其计算以及更新位置的位置

The d in the drag functions being an individual node in the nodes data array (the node being dragged), from which the force layout bases its calculations and where it updates positions

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

Also, some drag started events may use d.fx = d.x, this will simply set the node's position to its current position (as I do above), you could also use the mouse's current position without any noticeable difference.

这篇关于D3 forceSimulation和dragging,什么是node.fx / node.fy?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆