#d3.js
#d3.js
Вопрос:
Я пытаюсь добавить новые узлы в force directed, но когда я вызываю функцию QuickSearch(value)
, она дублирует график.
Кто-нибудь может посоветовать мне, как добавить только новый узел.
// Graph variables
var w = window.innerWidth;
var h = window.innerHeight;
var svg = d3.select("#svgData"),
scheme = ['#e41a1c','#377eb8','#4daf4a','#984ea3','#ff7f00','#ffff33','#a65628','#f781bf','#999999'],
width = svg.attr(w),
height = svg.attr(h),
color = d3.scaleOrdinal(d3.schemeCategory20);
//color = d3.scaleOrdinal(scheme);
var info = {
"nodes": [
{"id": "1", "name": "1", "group": 1},
{"id": "2", "name": "2", "group": 1},
{"id": "3", "name": "3", "group": 1},
{"id": "4", "name": "4", "group": 1},
{"id": "5", "name": "5", "group": 1}
],
"links": [
{"source": "1", "target": "2", "value": 1},
{"source": "1", "target": "3", "value": 1},
{"source": "1", "target": "4", "value": 1},
{"source": "1", "target": "5", "value": 1}
]
}
var marker = d3.select("#svgData").append('defs')
.append('marker')
.attr("id", "Triangle")
.attr('viewBox', '-0 -5 10 10')
.attr("refX", 25)
.attr("refY", 0)
.attr("markerUnits", 'userSpaceOnUse')
.attr("orient", 'auto')
.attr("markerWidth", 13)
.attr("markerHeight", 13)
.attr('xoverflow', 'visible')
.append('path')
.attr("d", 'M 0,-5 L 10 ,0 L 0,5');
function QuickSearch(value) {
var new_node = {};
//console.log(info.nodes);
new_node = {"id": value, "name": value, "group": 1};
info.nodes.findIndex(x => x.id == new_node.id) == -1 ? info.nodes.push(new_node) : console.log("object already exists")
createGraph(info);
};
function createGraph(graph) {
// Zoom
var zoom = d3.zoom()
.scaleExtent([0, 10])
.on("zoom", zoomed);
d3.select("#svgData").call(zoom);
function zoomed() {
const currentTransform = d3.event.transform;
container.attr("transform", currentTransform);
}
// Simulation
var simulation = d3.forceSimulation()
.force("link", d3.forceLink().id(function(d) { return d.id; }).distance(200))
.force("charge", d3.forceManyBody().strength(10).distanceMax(1000))
.force("center", d3.forceCenter(w / 2, h / 2))
.force('collision', d3.forceCollide().radius(30))
var container = svg.append("g");
var link = container.append("g")
.attr("class", "links")
.selectAll("path")
.data(graph.links)
.enter().append("path")
.attr("marker-end", "url(#Triangle)");
var value = d3.select("g.links")
.selectAll("text")
.data(graph.links)
.enter().append("text")
.attr("dy", ".35em")
.attr("text-anchor", "middle")
.text(function(d) {
return d.value;
});
var node = container.append("g")
.attr("class", "nodes")
.selectAll("circle")
.data(graph.nodes)
.enter().append("circle")
.attr('stroke-width', 3)
.attr('stroke', function(d) { return color(d.group); })
.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended))
.on("click", listInfo);
var lable = d3.select(".nodes")
.selectAll("text")
.data(graph.nodes)
.enter().append("text")
.text(function(d) { return d.name; });
simulation
.nodes(graph.nodes)
.on("tick", ticked);
simulation.force("link")
.links(graph.links)
function ticked() {
link
.attr("d", function(d) {
return "M" d.source.x "," d.source.y
"C" d.source.x "," (d.source.y d.target.y) / 2
" " d.target.x "," d.target.y
" " d.target.x "," d.target.y;
})
.attr("stroke-dasharray", function() {
return this.getTotalLength() - 25;
});
value
.attr("x", function(d) { return (d.source.x d.target.x)/2; })
.attr("y", function(d) { return (d.source.y d.target.y)/2; });
node
.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; });
lable
.attr("x", function(d) { return d.x; })
.attr("y", function(d) { return d.y - 30; });
}
function slided(d) {
zoom.scaleTo(svg, d3.select(this).property("value"));
}
function dragstarted(d) {
if (!d3.event.active) simulation.alphaTarget(0.1).restart();
d.fx = d.x;
d.fy = d.y;
d3.select(this).classed("dragging", true);
d.fixed = true;
}
function dragged(d) {
d.fx = d3.event.x;
d.fy = d3.event.y;
//fix_nodes(d);
}
// Preventing other nodes from moving while dragging one node
function fix_nodes(this_node) {
node.each(function(d){
if (this_node != d){
d.fx = d.x;
d.fy = d.y;
}
});
}
function dragended(d) {
if (!d3.event.active) simulation.alphaTarget(0);
d3.select(this).classed("dragging", false);
//d.fixed = true;
}
var nodeText;
function listInfo(d) {
d3.select(this)
.select("text")
.text(function(d) { return d.name;})
var nodeText = d.name;
document.getElementById("nodeId").innerHTML = nodeText;
}
};
Я пробовал несколько способов использования .exit().remove()
option, но у меня это не работает.
Комментарии:
1. Каждый раз, когда вы запускаете createGraph, вы создаете новый g и запускаете с ним цикл ввода / обновления / выхода. Но поскольку этот новый g пуст, выходить или обновлять нечего. Вы вводите все каждый раз. Вместо этого либо не добавляйте этот g, а просто добавляйте все непосредственно в svg, либо добавляйте этот g один раз вне функции createGraph. Я уверен, что есть хороший существующий ответ на этот вопрос, но у меня возникли проблемы с его поиском, если я его найду, я свяжусь с ним здесь.
2. Спасибо, Эндрю, звучит многообещающе. Я попробую переместить g за пределы функции.
3. @Andrew, еще раз спасибо. Я переместил переменную «контейнер» за пределы функции, и она работает.