gpt4 book ai didi

javascript - D3 强制模拟和拖动,什么是node.fx/node.fy?

转载 作者:行者123 更新时间:2023-11-28 14:31:06 24 4
gpt4 key购买 nike

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

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

拖动开始事件通常基于d.x/d.y,而d.fx/d.fy基于d.x/d.y end 事件将 d.fx/d.fy 设置为 null

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

最佳答案

d3 强制布局和 node.fx/fy

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

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. (docs)

这些 fx/fy 属性用于 fix nodes一般来说,不仅仅是在拖动事件期间。

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

在 d3 力模拟中,每个节点的位置都会在每次更新时更新。在整个模拟过程中,蜱虫会重复触发,以保持节点位置更新,它的速度足够快,足以为节点移动设置动画。

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

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

为了消除受力布局对拖动节点的影响,我们可以设置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;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.10.0/d3.min.js"></script>

d拖动函数是节点数据数组中的单个节点(被拖动的节点),力布局基于该节点进行计算并更新位置

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

关于javascript - D3 强制模拟和拖动,什么是node.fx/node.fy?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51544007/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com