d3.js – 添加新节点后图形被破坏

我使用以下逻辑构建下面的图形:

  • 鼠标hover节点 – 它是非连接节点及其链接是透明的。
  • 鼠标hover链接 – 所有节点和链接都是透明的,除了触摸的链接(变为粗体),它是两个节点。

这很有效,直到我尝试向图表添加更多节点。 按“ 添加节点”按钮,添加另一个节点,并将其链接到聚会。 问题是,节点逻辑被破坏(链接逻辑仍然有效)。 知道为什么吗?

谢谢! 的jsfiddle

function removeNodePopup() { $("#nodePopup").remove(); } function showNodePopup(node) { removeNodePopup(); if (!node['data']) { return; } var data = node['data']; var htmlStr = ''; htmlStr += '
'; htmlStr += '
'; htmlStr += '
' + data['name'] + '
'; if (data['desc']) { if (data['desc'].startsWith("http")) { htmlStr += ' Go to post..'; } else { htmlStr += '
https://stackoverflow.com/questions/48026059/d3-js-graph-is-broken-after-adding-new-nodes/' + data['desc'] + '
'; } } htmlStr += '
GROUP: ' + data['groupId'] + '
'; htmlStr += '
LEADER: ' + data['leaderId'] + '
'; htmlStr += '
'; htmlStr += '
'; $("body").append(htmlStr); $("#nodePopupCloseButton").click(removeNodePopup); } const LINK_DEFAULT_COLOR = "#ccc"; const NODE_DEFAULT_COLOR = "gray"; const DEFAULT_OPACITY = 1; const BACKGROUND_OPACITY = 0.2; function Graph(elementId) { var svg; var simulation; var mNodesData = []; var mEdgesData = []; var mNode = null; var mLink = null; var elementId; var heightDelta = 100; var width = window.innerWidth; var height = window.innerHeight - heightDelta; return { init: function () { svg = d3.select('#' + elementId) .append("svg") .attr("width", width) .attr("height", height); simulation = d3.forceSimulation() .force(".edge", d3.forceLink()) .force("charge", d3.forceManyBody().strength(-600)) .force("center", d3.forceCenter(width / 2, height / 2)); mLink = svg.selectAll(".edge") .attr("class", "edge") .style("stroke", LINK_DEFAULT_COLOR) .style("stroke-width", function (e) { return 1 /* e.width*/ }); mNode = svg.selectAll(".node") .attr("class", "node"); }, clearGraph: function () { $('#' + this.elementId).empty(); }, getNodes: function () { return mNodesData; }, getEdges: function () { return mEdgesData; }, addNodes: function (nodes) { mNodesData = mNodesData.concat(nodes); }, addEdges: function (edges) { mEdgesData = mEdgesData.concat(edges); }, onMouseOut: function () { // removePopup(); mNode.select("image").style("opacity", DEFAULT_OPACITY); mNode.select("circle").style("stroke", NODE_DEFAULT_COLOR); mLink.style("opacity", DEFAULT_OPACITY).style("stroke", LINK_DEFAULT_COLOR); }, draw: function () { mNode = svg.selectAll(".node") .data(mNodesData) .enter() .append("g") .attr("class", "node"). merge(mNode); mLink = svg.selectAll(".edge") .data(mEdgesData) .enter() .append("line") .attr("class", "edge") .style("stroke", LINK_DEFAULT_COLOR) .style("stroke-width", function (e) { return 2 /* e.width*/ }).merge(mLink).lower(); mNode.call(d3.drag() .on("start", dragstarted) .on("drag", dragged) .on("end", dragended)); mNode.on('mouseover', function (thisNode) { showNodePopup(thisNode); var thisNodeID = thisNode.id; var connectedNodes = mEdgesData.filter(function(d) { return d.source.id === thisNodeID || d.target.id === thisNodeID }).map(function(d) { return d.source.id === thisNodeID ? d.target.id : d.source.id }); mNode.each(function (otherNode, id) { var image = d3.select(this).select("image"); var circle = d3.select(this).select("circle"); if (connectedNodes.indexOf(otherNode.id) > -1 || thisNodeID == otherNode.id) { image.style("opacity", DEFAULT_OPACITY); circle.style("stroke", NODE_DEFAULT_COLOR); } else { image.style("opacity", BACKGROUND_OPACITY); circle.style("stroke", "#f6f6f6"); } }); // var filteredNodes = mNode.filter(function(otherNode) { // return connectedNodes.indexOf(otherNode.id) == -1 // }); // // filteredNodes.select("image").style("opacity", BACKGROUND_OPACITY); // filteredNodes.select("circle").style("stroke", "#f6f6f6"); // // var unfilterdNode = mNode.filter(function (otherNode) { // return connectedNodes.indexOf(otherNode.id) > -1 || thisNodeID == otherNode.id; // }); // unfilterdNode.select("image").style("opacity", DEFAULT_OPACITY); // unfilterdNode.select("circle").style("stroke", NODE_DEFAULT_COLOR); mLink.filter(function (otherLink) { return (thisNode !== otherLink.source && thisNode !== otherLink.target); }).style("opacity", BACKGROUND_OPACITY); mLink.filter(function (otherLink) { return (thisNode == otherLink.source || thisNode == otherLink.target); }).style("opacity", DEFAULT_OPACITY); }) .on('mouseout', this.onMouseOut); mLink.on('mouseover', function (currentLink) { mLink.filter(function (otherLink) { return (currentLink == otherLink); }).style("stroke", "black"); mLink.filter(function (otherLink) { return (currentLink !== otherLink); }).style("opacity", BACKGROUND_OPACITY); mNode.filter(function (otherNode) { return (currentLink.source != otherNode || currentLink.target != otherNode); }).select("image").style("opacity", BACKGROUND_OPACITY); mNode.filter(function (otherNode) { return (currentLink.source != otherNode || currentLink.target != otherNode); }).select("circle").style("stroke", "#f6f6f6"); mNode.filter(function (d1) { return (d1 == currentLink.source || d1 == currentLink.target); }).select("image").style("opacity", DEFAULT_OPACITY); mNode.filter(function (d1) { return (d1 == currentLink.source || d1 == currentLink.target); }).select("circle").style("stroke", NODE_DEFAULT_COLOR); }).on('mouseout', this.onMouseOut); var nodeCircle = mNode.append("circle") .attr("r", function (d) { return 0.5 * Math.max(d.width, d.height) }) .attr("stroke", NODE_DEFAULT_COLOR) .attr("stroke-width", "2px") .attr("fill", "white"); var nodeImage = mNode.append("image") .attr("xlink:href", function (d) { return d.image }) .attr("height", function (d) { return d.height + "" }) .attr("width", function (d) { return d.width + "" }) .attr("x", function (d) { return -0.5 * d.width }) .attr("y", function (d) { return -0.5 * d.height }) .attr("clip-path", function (d) { return "circle(" + (0.48 * Math.max(d.width, d.height)) + "px)" }); simulation.nodes(mNodesData); simulation.force(".edge").links(mEdgesData); simulation.on("tick", function () { mLink.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; }) mNode.attr("transform", function (d) { return "translate(" + dx + "," + dy + ")" }); mNode.attr("cx", function (d) { return dx = Math.max(d.width, Math.min(width - d.width, dx)); }) .attr("cy", function (d) { return dy = Math.max(d.height, Math.min(height - heightDelta - d.height, dy)); }); }); function dragstarted(d) { if (!d3.event.active) simulation.alphaTarget(0.3).restart(); d.fx = dx; d.fy = dy; } function dragged(d) { d.fx = d3.event.x; d.fy = d3.event.y; } function dragended(d) { if (!d3.event.active) simulation.alphaTarget(0); d.fx = null; d.fy = null; } } } } initialData = { "nodes": [{ "id": 0, "image": "images/0.jpg", "height": 40, "width": 40, "data": { "name": "Number0", "groupId": "Bla1", "desc": "Desc1", "leaderId": "123-123" } }, { "id": 1, "image": "images/1.jpeg", "height": 100, "width": 100, "data": { "name": "Number1", "groupId": "Bla2", "desc": "Desc1", "leaderId": "123-123" } }, { "id": 2, "image": "images/2.png", "height": 50, "width": 50, "data": { "name": "Number2", "groupId": "Bla3", "desc": "Desc1", "leaderId": "123-123" } }, { "id": 3, "image": "images/3.jpeg", "height": 40, "width": 40, "data": { "name": "Number3", "groupId": "Bla4", "desc": "Desc1", "leaderId": "123-123" } }], "edges": [{ "source": 0, "target": 1, "width": 5, "data": { "counter": 500 } }, { "source": 0, "target": 2, "width": 10, "data": { "counter": 500 } }, { "source": 0, "target": 3, "width": 1, "data": { "counter": 500 } }] }; var graph = Graph('d3Graph'); graph.init(); graph.addNodes(initialData.nodes); graph.addEdges(initialData.edges); graph.draw(); //add(); function add() { graph.addNodes([{ "id": 4, "image": "images/4.jpg", "height": 20, "width": 20, "data": { "name": "Number4", "groupId": "Bla4", "desc": "Desc4", "leaderId": "1234-1234" } }]); graph.addEdges([{ "source": 4, "target": 3, "width": 1, "data": { "counter": 500 } }]) graph.draw(); }

的jsfiddle

你多次调用draw方法,所以你必须小心对待它。

只有新添加的节点才能添加imagecircle标记,因此您应该更改

 mNode = svg.selectAll(".node") .data(mNodesData) .enter() .append("g") .attr("class", "node"). merge(mNode); 

 var newNodes = svg.selectAll(".node") .data(mNodesData) .enter() .append("g") .attr("class", "node"); mNode=newNodes.merge(mNode); 

并改变

 var nodeCircle = mNode.append("circle") 

 var nodeCircle = newNodes.append("circle") 

并且应该以相同的方式改变image相关的代码。