如何根据用户操作修改路径? 例如:我有一个由三个点A,B和C组成的路径。当用户点击路径(除了现有点之外的其他地方)时,我想在该位置添加一个新点到路径。如何将新点插入到正确位置的路径中?
var nodes = [[30, 130], [250, 250], [400,130]];
var line = d3.svg.line();
var svg = d3.select("body").append("svg")
.attr("width", 500)
.attr("height", 5000);
var path = svg.append("path")
.datum(nodes)
.attr("class", "line")
.call(update);
path.on("click", insertNode);
function update() {
svg.select("path").attr("d", line);
var circle = svg.selectAll("circle")
.data(nodes, function(d) { return d; });
}
function insertNode(data) {
//create the new node:
var newNode = [];
var newNode = d3.mouse(svg.node());
//find coordinates relative to the plotting region
nodes.push(newNode); //add to your nodes array
update();
}
如果单击第一个段(sx - > dx),则会从路径末尾添加一个新段到新节点,因为我在节点数组的末尾添加了新节点。正确的行为是节点[30,130]和[250,250]之间的路径(节点阵列)中的新节点
谢谢! 前!
答案 0 :(得分:3)
您不能简单地将新节点添加到数组中,您必须先确定其位置。这样做的一种方法是计算所有点的角度。当绝对值相同时,您就知道已经找到了插入位置。唯一的障碍是因为线的宽度,它不会完全是180度,所以你必须考虑到这一点。以下代码尝试此操作并将新节点拼接到数组中。
var idx = 0, prevAngle;
nodes.forEach(function(n, i) {
var angle = Math.abs(Math.atan((n[1] - newNode[1]) / (n[0] - newNode[0])));
if(Math.abs(angle - prevAngle) < 0.05) {
idx = i;
}
prevAngle = angle;
});
tmp = nodes.slice(0, idx);
tmp.push(newNode);
nodes = tmp.concat(nodes.slice(idx));
完整示例here。
答案 1 :(得分:0)
链接的数据对象包含源节点数据对象和目标节点数据对象,因此您可以使用该信息将链接拆分为两个,通过新节点连接。
示例代码,假设linkElements
是您对链接<path>
或<line>
元素的d3选择,links
和nodes
是对应的数据数组force.links()
和force.nodes()
:
linkElements.on("click", insertNode);
function insertNode(linkData){ //parameter is the *link's* data object
//create the new node:
var newNode = {};
var clickPoint = d3.mouse(this.parentNode);
//find coordinates relative to the plotting region
newNode.x = xScale.invert(clickPoint[0]);
newNode.y = yScale.invert(clickPoint[1]);
//convert coordinates to data values
nodes.push(newNode); //add to your nodes array
//create a new link for the second half of the old link:
var newLink = {source:newNode, target:linkData.target};
links.push(newLink); //add to your links array
//update the old link to point to the new node:
linkData.target = newNode;
update();
}
这当然只更新节点和链接的数据对象,您的update()
函数必须更新力布局并创建输入的SVG元素。