我有一个粘滞力布局:http : //jsfiddle.net/smqsusdw/
我有将此功能拖到一个位置的节点:
function positionnodes(){
force.stop();
node.each(function(d, i){
if(i===1){
d.fixed = true;
d.x = 100;
d.y = 100;
}
}).transition().duration(1000).attr("cx", function(d){ return d.x }).attr("cy", function(d){ return d.y });
link.transition().duration(1000)
.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; });
}
现在,当它执行此操作时,我希望它看起来像是用鼠标拖动它。但是,当我按下按钮时,只有选定的节点移动。无论如何,是否可以在节点上模拟鼠标拖动,以便其他相关节点似乎随之移动?
例如,我按下按钮,只有一个节点移动,其余所有节点保持原状。
但是当我将一个节点拖到某个位置时,由于D3力的物理作用,相关节点也随之移动。有没有办法模拟这种运动
要选择正确的方法,重要的是要知道在D3的力布局中,计算与任何元素的实际渲染是分离的。d3.layout.force()
将根据指定的参数来计算运动和位置。渲染将由在中注册的处理程序完成.force("tick", renderingHandler)
。此功能将在每个刻度上被强制布局调用,并根据计算出的位置渲染元素。
考虑到这一点,很明显,您的解决方案将无法按预期工作。在图形元素上使用过渡将仅在不更新数据且不涉及力布局的情况下移动节点。为了获得所需的行为,您需要坚持将计算和渲染分离。这将使您摆脱实现鼠标事件模拟的需要。
这可以通过使用来完成d3.timer()
,它将重复调用将移动节点的位置设置为其起点和终点之间的插值的函数。设置完这些值之后,该函数将激活强制布局以对其余节点进行工作,并调用渲染处理程序.tick()
,该处理程序将更新整个布局。
function positionnodes(){
var move = graph.nodes[1], // the node to move around
duration = 1000, // duration of the movement
finalPos = { x: 100, y: 100 },
interpolateX = d3.interpolateNumber(move.x, finalPos.x),
interpolateY = d3.interpolateNumber(move.y, finalPos.y);
// We don't want the force layout to mess with our node.
move.fixed = true;
// Move the node by repeatedly determining its position.
d3.timer(function(elapsed) {
// Because the node should remain fixed, the previous position (.px, .py)
// needs to be set to the same value as the new position (.x, .y). This way
// the node will not have any inherent movement.
move.x = move.px = interpolateX(elapsed / duration);
move.y = move.py = interpolateY(elapsed / duration);
// Re-calculate the force layout. This will also invoke tick()
// which will take care of the rendering.
force.start();
// Terminate the timer when the desired duration has elapsed.
return elapsed >= duration;
});
}
请查看以下代码片段或更新的JSFiddle,以使您的代码适应工作。
var graph ={
"nodes": [
{"x": 469, "y": 410},
{"x": 493, "y": 364},
{"x": 442, "y": 365},
{"x": 467, "y": 314},
{"x": 477, "y": 248},
{"x": 425, "y": 207},
{"x": 402, "y": 155},
{"x": 369, "y": 196},
{"x": 350, "y": 148},
{"x": 539, "y": 222},
{"x": 594, "y": 235},
{"x": 582, "y": 185},
{"x": 633, "y": 200}
],
"links": [
{"source": 0, "target": 1},
{"source": 1, "target": 2},
{"source": 2, "target": 0},
{"source": 1, "target": 3},
{"source": 3, "target": 2},
{"source": 3, "target": 4},
{"source": 4, "target": 5},
{"source": 5, "target": 6},
{"source": 5, "target": 7},
{"source": 6, "target": 7},
{"source": 6, "target": 8},
{"source": 7, "target": 8},
{"source": 9, "target": 4},
{"source": 9, "target": 11},
{"source": 9, "target": 10},
{"source": 10, "target": 11},
{"source": 11, "target": 12},
{"source": 12, "target": 10}
]
}
var width = 960,
height = 500;
var force = d3.layout.force()
.size([width, height])
.charge(-400)
.linkDistance(40)
.on("tick", tick);
var drag = force.drag()
.on("dragstart", dragstart);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
var link = svg.selectAll(".link"),
node = svg.selectAll(".node");
//d3.json("graph.json", function(error, graph) {
// if (error) throw error;
force
.nodes(graph.nodes)
.links(graph.links)
.start();
link = link.data(graph.links)
.enter().append("line")
.attr("class", "link");
node = node.data(graph.nodes)
.enter().append("circle")
.attr("class", "node")
.attr("r", 12)
.on("dblclick", dblclick)
.call(drag);
//});
function tick() {
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 dblclick(d) {
d3.select(this).classed("fixed", d.fixed = false);
}
function dragstart(d) {
d3.select(this).classed("fixed", d.fixed = true);
}
function positionnodes(){
var move = graph.nodes[1], // the node to move around
duration = 1000, // duration of the movement
finalPos = { x: 100, y: 100 },
interpolateX = d3.interpolateNumber(move.x, finalPos.x),
interpolateY = d3.interpolateNumber(move.y, finalPos.y);
// We don't want the force layout to mess with our node.
move.fixed = true;
// Move the node by repeatedly determining its position.
d3.timer(function(elapsed) {
// Because the node should remain fixed, the previous position (.px, .py)
// needs to be set to the same value as the new position (.x, .y). This way
// the node will not have any inherent movement.
move.x = move.px = interpolateX(elapsed / duration);
move.y = move.py = interpolateY(elapsed / duration);
// Re-calculate the force layout. This will also invoke tick()
// which will take care of the rendering.
force.start();
// Terminate the timer when the desired duration has elapsed.
return elapsed >= duration;
});
}
.link {
stroke: #000;
stroke-width: 1.5px;
}
.node {
cursor: move;
fill: #ccc;
stroke: #000;
stroke-width: 1.5px;
}
.node.fixed {
fill: #f00;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<button onclick = 'positionnodes()'> click me</button>
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句