dagre-d3 Usage Guide
Recently, a project required implementing frontend flowchart drawing, so I conducted a systematic technical research including echarts, highcharts, jsPlumbs, jointJS, RaphaelJs, d3, etc. echarts and highcharts don’t have good flowchart models themselves and are more suitable for bar charts, pie charts, etc., so they were directly passed. After comparison, D3 was finally chosen.
Excerpt from a blog post ranking, the author summarized these flowchart libraries, showing that D3 is still very powerful.
After deciding to use D3, writing directly with D3 would be too laborious. After searching, I found a D3-based library - dagre-d3. During use, as I delved deeper, I gained a thorough understanding of this library. Whether searching on Baidu or Google, I found that there’s too little material, especially in Chinese. Here, I’ll summarize it.
dagre-d3 - What You Need to Know
What is it?
Dagre
is a JavaScript
library that can easily create flowcharts on the client side, and dagre-d3 can be understood as the frontend of Dagre, using D3 for rendering.
Project Activity
dagre-d3
has 1K+ stars, indicating this library is still quite popular. However, both dagre and d3-dagre are in an inactive state, and the author himself is no longer maintaining it.
Demo
Here’s a simple demo to illustrate:
View complete source code here
Show me the code
<div class="container">
<div class="col-sm-6">
<svg width=960 height=400>
<g/>
</svg>
</div>
</div>
let svg = d3.select("svg"),
inner = svg.select("g");
// Create the input graph
this.g = new dagreD3.graphlib.Graph({});
// Set an object for the graph label
this.g.setGraph({});
// Default to assigning a new object as a label for each new edge.
this.g.setDefaultEdgeLabel(function () {
return {};
});
this.g.graph().transition = function (selection) {
return selection.transition().duration(500);
};
// Zoom functionality implementation
var zoom = d3.behavior.zoom().on("zoom", function () {
inner.attr("transform", "translate(" + d3.event.translate + ")" +
"scale(" + d3.event.scale + ")");
});
svg.call(zoom);
this.g.setNode(0, {label: 'VVV'});
this.g.setNode(1, {label: "A"});
this.g.setNode(2, {label: "B"});
this.g.setNode(3, {labelType:"html",label: "<i class=\"fa fa-database\"></i>B"});
this.g.setEdge(0, 1);
this.g.setEdge(0, 2);
this.g.setEdge(2, 3);
// Run the renderer. This is what draws the final graph.
this.render(inner, this.g);
this.g.nodes().forEach((v) => {
let node = this.g.node(v);
console.log(`Node ${v}: Label:${node.label},X:${node.x},Y:${node.y}`);
});
//give IDs to each of the nodes so that they can be accessed
svg.selectAll("g.node rect")
.attr("id", function (d) {
return "node" + d;
});
svg.selectAll("g.edgePath path")
.attr("id", function (e) {
return e.v + "-" + e.w;
});
svg.selectAll("g.edgeLabel g")
.attr("id", function (e) {
return 'label_' + e.v + "-" + e.w;
});
this.g.nodes().forEach((v) => {
var node = this.g.node(v);
node.customId = "node" + v;
});
this.g.edges().forEach((e) => {
var edge = this.g.edge(e.v, e.w);
edge.customId = e.v + "-" + e.w
});
// code for drag
function dragstart(d) {
d3.event.sourceEvent.stopPropagation();
}
let dragmover = (currentThis, d) => {
this.dragmove(currentThis, d);
};
function dragmove(d) {
dragmover(this, d)
}
let nodeDrag = d3.behavior.drag()
.on("dragstart", dragstart)
.on("drag", dragmove);
let edgeDrag = d3.behavior.drag()
.on("dragstart", dragstart)
.on('drag', (d) => {
this.translateEdge(this.g.edge(d.v, d.w), d3.event.dx, d3.event.dy);
$('#' + this.g.edge(d.v, d.w).customId).attr('d', this.calcPoints(d));
});
nodeDrag.call(svg.selectAll("g.node"));
edgeDrag.call(svg.selectAll("g.edgePath"));
Main Functions
For usage, I recommend directly looking at the d3-dagre source code to avoid missing anything. Here are the main functions:
- Add node
setNode(v, {label: 'VVV'})
- Add edge
setEdge(v, s)
- Delete node
removeNode(v)
- Delete edge
removeEdge(v,s)
Drag and zoom functionality is actually implemented through D3, methods as above. If you want to implement right-click menus for clicking nodes or edges, you can use JQ, such as this plugin jQuery-contextMenu