D3力导向图新增节点X/Y值为NaN问题求助
解决D3.js新增节点X/Y显示NaN的问题
我来帮你搞定这个NaN的问题,核心原因是新增节点没给初始坐标,再加上数据处理时没正确关联force布局里的现有节点引用,导致force没法计算位置。下面是具体的修复思路和修改后的代码:
问题根源拆解
- 新节点没有
x/y属性:D3的force布局不会自动给完全新建的节点生成初始坐标,直接就会变成NaN。 - 节点引用不统一:你在
mousedownNode里处理新数据时,创建了全新的节点对象,没有复用现有nodes里的节点,导致force无法识别新节点和旧节点的关联。 restart函数元素绑定错误:每次调用都会给所有节点重复添加circle和text,应该把元素创建逻辑放在enter()回调里。
具体修复步骤
1. 给所有节点加初始坐标
不管是初始节点还是新增节点,都要设置x和y的初始值,比如页面中心或者点击位置,让force有计算的起点。
2. 复用现有节点引用
处理新链接时,先从现有nodes里查找对应ID的节点,存在就直接复用,不存在再创建新节点并赋值初始坐标。
3. 修复restart的元素创建逻辑
把circle、text的添加放在node.enter()的回调里,避免重复创建元素。
修改后的完整代码
<script src="firstloaddata.js"></script> <script src="resources/d3.v3.min.js"></script> <script src="resources/Require/require.js"></script> <script> var nodes = {}; var width = screen.width - 50; var height = 900; var links = []; var nominal_text_size = 12; var link_distance = 100; // 处理初始数据,给节点加初始坐标 processnodes(links_current); var force = d3.layout.force() .nodes(d3.values(nodes)) .links(links) .size([width, height]) .linkDistance(link_distance) .charge(-1700) .friction(0.9); var svg = d3.select("body").append("svg") .attr("width", width) .attr("height", height); force.on("tick", function() { svg.selectAll("line.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; }) .attr("stroke-width", function(d) { return (d.relCol != "grey") ? 3 : 1.5; }) .attr("stroke", function(d) { return d.relCol || "red"; }); svg.selectAll("g.node") .attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; }); }); // 重构processnodes,支持传入链接数据 function processnodes(linkData) { linkData.forEach(function(link) { // 优先复用现有节点,无则创建并加初始坐标 link.source = nodes[link.source] || (nodes[link.source] = { id: link.source, caption: link.sCaption, x: width/2, y: height/2 }); link.target = nodes[link.target] || (nodes[link.target] = { id: link.target, caption: link.tCaption, x: width/2, y: height/2 }); links.push(link); }); } function restart() { // 处理链接 var link = svg.selectAll("line.link").data(links); link.enter().append("line") .style("stroke-width", 2.5) .attr("stroke", "red") .attr("class", "link"); link.exit().remove(); // 处理节点,用d3.values把nodes对象转成数组 var node = svg.selectAll("g.node").data(d3.values(nodes)); // 只给新节点创建元素 var nodeEnter = node.enter().append("g") .attr("class", "node") .call(force.drag) .on("mousedown", mousedownNode); nodeEnter.append("circle") .attr("class", "circle") .attr("r", 8); nodeEnter.append("text") .style("font-size", nominal_text_size) .attr("dy", ".35em") .attr("x", "1.5em") .text(function(d) { return d.caption; }); node.exit().remove(); force.start(); } function mousedownNode(d) { // 获取点击节点的位置,作为新节点的初始坐标 var clickX = d.x; var clickY = d.y; requirejs(['newdata.js?_r=' + Math.random()], function() { links_current.forEach(function(link) { // 复用或创建节点,给新节点加偏移后的初始坐标 link.source = nodes[link.source] || (nodes[link.source] = { id: link.source, caption: link.sCaption, x: clickX, y: clickY }); link.target = nodes[link.target] || (nodes[link.target] = { id: link.target, caption: link.tCaption, x: clickX + 50, y: clickY + 50 }); // 避免重复添加相同链接 var linkExists = links.some(function(existLink) { return existLink.source.id === link.source.id && existLink.target.id === link.target.id; }); if (!linkExists) { links.push(link); } }); restart(); }); } restart(); </script>
额外优化点
- 添加了链接重复检查,避免重复添加相同的链接
- 初始节点和新增节点都设置了明确的初始坐标,从根源避免NaN
- 新节点初始位置基于点击节点偏移,布局更符合直觉
内容的提问来源于stack exchange,提问作者Deepansh Pandey
相关产品推荐
相关产品推荐

