У меня есть VueJS генерация кода d3 принудительно направленный сетевой узел при монтировании с использованием вызова json.Я пытаюсь обновить сетевой график новыми узлами и ссылками, используя тот же вызов json, и после вычисления различий я хочу, чтобы он соответственно добавил / удалил узлы / ссылки, но не могу понять, как это сделать.
Я уже пытался искать примеры динамически обновляемых силовых ориентированных графов d3.Я действительно нашел несколько примеров vue js + d3, но не с динамическими обновлениями, такими как этот , который показывает, как изменить данные графика, и вызывает перезапуск моделирования, но это не то, на что я смотрювыполнить.
Я пытался несколько дней адаптировать правильный код d3 js из примеров в vue js, но безуспешно.
не смогне найти никаких примеров с Vue.js и d3 v5 (найдены некоторые с v4).
new Vue({
el: "#app",
data: function() {
return {
graph: null,
simulation: null,
color: d3.scaleOrdinal(d3.schemeCategory10),
settings: {
strokeColor: "#29B5FF",
width: 100,
svgWidth: 960,
svgHeight: 600
},
radius: 50,
tooltip: d3.select("body").append("div").attr("class", "tooltip2").html(""),
};
},
mounted: function() {
var that = this;
var initData = '{"nodes":[{"id":1,"ip":"100.64.1.118","name":"PCRF","description":"Policy and Charging Rules Function","image":"https://i.imgur.com/mpAqPb4.png"}],"links":[]}';
var initBlob = new Blob([JSON.stringify(initData)], {
type: "application/json"
});
var initUrl = URL.createObjectURL(initBlob);
d3.json(initUrl, function(error, graph) {
console.log(graph);
if (error) throw error;
that.graph = graph;
that.simulation = d3.forceSimulation(that.graph.nodes)
.force("link", d3.forceLink(that.graph.links).distance(300).strength(1))
.force("charge", d3.forceManyBody().strength(-1000))
.force("center", d3.forceCenter(that.settings.svgWidth / 2, that.settings.svgHeight / 2))
.force("xAxis", d3.forceX(that.settings.svgWidth / 2).strength(0.4))
.force("yAxis", d3.forceY(that.settings.svgHeight / 2).strength(0.6))
.force("repelForce", d3.forceManyBody().strength(-5000).distanceMax(300).distanceMin(300));
});
d3.interval(function() {
var interData = '{"nodes":[{"id":1,"ip":"100.64.1.118","name":"PCRF","description":"Allot Policy and Charging Rules Function","image":"https://i.imgur.com/mpAqPb4.png"},{"id":2,"ip":"100.64.1.119","name":"Client","description":"Diameter Client","image":"https://i.imgur.com/NsUvLZb.png"}],"links":[{"id":1,"source":1,"target":2,"type":"OKAY"}]}';
var interBlob = new Blob([JSON.stringify(interData)], {
type: "application/json"
});
var interUrl = URL.createObjectURL(interBlob);
d3.json(interUrl, function(error, graph) {
console.log(graph);
that.updateGraph(graph);
});
}, 5000);
},
methods: {
fixna(x) {
if (isFinite(x)) return x;
return 0;
},
updateGraph(graph) {
var that = this;
that.graph = graph;
//TODO Nodes
var newLinks = graph.links;
newLinks.forEach(function(d) {
d3.select("#l" + d.id)
.attr("class", function(d2) {
if (d.type == 'OKAY') {
return "connected"
} else {
return "disconnected"
}
})
.on("mouseover", function(d2) {
if (d.type == 'OKAY') {
d3.select(this).attr("stroke-width", "3px");
} else {
d3.select(this).attr("stroke-width", "2px");
}
t_text = "<strong>" + d2.source.ip + " <-> " + d2.target.ip + "</strong>";
if (d.type == 'OKAY') {
t_text += '<br>Connection Status: <font color="green">' + d.type + '</font>';
} else {
t_text += '<br>Connection Status: <font color="red">' + d.type + '</font>';
}
that.tooltip.html(t_text)
return that.tooltip.style("visibility", "visible");
})
.on("mousemove", function() {
return that.tooltip.style("top", (event.pageY - 10) + "px").style("left", (event.pageX + 10) + "px");
})
.on("mouseout", function(d2) {
if (d.type == 'OKAY') {
d3.select(this).attr("stroke-width", "2px");
} else {
d3.select(this).attr("stroke-width", "1px");
}
return that.tooltip.style("visibility", "hidden");
});
});
}
},
computed: {
nodes: function() {
var that = this;
if (that.graph) {
var node = d3.select("svg").append("g").attr("class", "nodes").selectAll("g")
.data(that.graph.nodes).enter().append("g")
.call(d3.drag()
.on("start", function dragstarted(d) {
if (!d3.event.active) that.simulation.alphaTarget(0.3).restart();
d.fx = d.x;
d.fy = d.y;
})
.on("drag", function dragged(d) {
d.fx = d3.event.x;
d.fy = d3.event.y;
})
.on("end", function dragended(d) {
if (!d3.event.active) that.simulation.alphaTarget(0);
d.fx = null;
d.fy = null;
})).attr("fill", "white");
var defs = node.append("defs");
defs.append('pattern')
.attr("id", function(d, i) {
return "my_image" + i
})
.attr("width", 1)
.attr("height", 1)
.append("svg:image")
.attr("xlink:href", function(d) {
return d.image
})
.attr("height", that.radius)
.attr("width", that.radius)
.attr("x", 0)
.attr("y", 0);
var circle = node.append("circle")
.attr("r", that.radius / 2)
.attr("fill", function(d, i) {
return "url(#my_image" + i + ")"
})
.attr("id", function(d) {
if (d.id == "p0") {
return "mainNode"
} else {
return d.id
}
})
.attr("stroke", "#494b4d")
.attr("stroke-width", "2px")
.on("mouseover", function(d) {
d3.select(this).attr("stroke-width", "3px");
//sets tooltip. t_text = content in html
t_text = "<strong>" + d.name + "</strong><br>IP Address: " + d.ip
that.tooltip.html(t_text)
return that.tooltip.style("visibility", "visible");
})
.on("mousemove", function() {
return that.tooltip.style("top", (event.pageY - 10) + "px").style("left", (event.pageX + 10) + "px");
})
.on("mouseout", function() {
d3.select(this).attr("stroke-width", "2px");
return that.tooltip.style("visibility", "hidden");
});
node.append("text")
.style("fill", "black")
.attr("dx", 0)
.attr("dy", that.radius - ((that.radius / 10) - 3) * 5)
.attr("text-anchor", "middle")
.text(function(d) {
return d.name + " - " + d.ip;
});
return node;
}
},
links: function() {
var that = this;
if (that.graph) {
return d3.select("svg").append("g")
.attr("class", "links")
.selectAll("line")
.data(that.graph.links)
.enter()
.append("line")
.attr("id", function(d) {
return "l" + d.id
})
.attr("class", function(d) {
if (d.type == 'OKAY') {
return "connected"
} else {
return "disconnected"
}
})
.on("mouseover", function(d) {
if (d.type == 'OKAY') {
d3.select(this).attr("stroke-width", "3px");
} else {
d3.select(this).attr("stroke-width", "2px");
}
t_text = "<strong>" + d.source.ip + " <-> " + d.target.ip + "</strong>";
if (d.type == 'OKAY') {
t_text += '<br>Connection Status: <font color="green">' + d.type + '</font>';
} else {
t_text += '<br>Connection Status: <font color="red">' + d.type + '</font>';
}
that.tooltip.html(t_text)
return that.tooltip.style("visibility", "visible");
})
.on("mousemove", function() {
return that.tooltip.style("top", (event.pageY - 10) + "px").style("left", (event.pageX + 10) + "px");
})
.on("mouseout", function(d) {
if (d.type == 'good') {
d3.select(this).attr("stroke-width", "2px");
} else {
d3.select(this).attr("stroke-width", "1px");
}
return that.tooltip.style("visibility", "hidden");
});
}
},
},
updated: function() {
var that = this;
that.simulation.nodes(that.graph.nodes).on('tick', function ticked() {
that.links
.attr("x1", function(d) {
return that.fixna(d.source.x);
})
.attr("y1", function(d) {
return that.fixna(d.source.y);
})
.attr("x2", function(d) {
return that.fixna(d.target.x);
})
.attr("y2", function(d) {
return that.fixna(d.target.y);
});
that.nodes
.attr("transform", function(d) {
return "translate(" + that.fixna(d.x) + "," + that.fixna(d.y) + ")";
});
});
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.13.0/d3.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
I created <a href="https://jsfiddle.net/tsp48yL3/1/" target="_blank">this fiddle</a>.
<div id="app">
<div class="svg-container" :style="{width: settings.width + '%'}">
<svg id="svg" pointer-events="all" viewBox="0 0 960 600" preserveAspectRatio="xMinYMin meet">
<g :id="links"></g>
<g :id="nodes"></g>
</svg>
</div>
</div>
работает с d3.v4 .Я попытался изменить на v5 , но это не работает (может, кто-то пролил свет).Был бы рад, если бы кто-нибудь мог указать мне правильное направление или помочь мне с реализацией.
Я пытаюсь добавить узлы и удалить их точно так же, как в этой демонстрации здесь .
там вы можете увидеть новые узлы, сгенерированные без перезапуска всей симуляции, что делает его очень гладким.