const svg = d3.select("svg"),
diameter = +svg.attr("width"),
g = svg.append("g").attr("transform", "translate(2,2)"),
format = d3.format(",d");
const links = [
{ source: "animate1", target: "animate4" },
{ source: "animate6", target: "animate9" },
{ source: "animate9", target: "animate6" },
{ source: "animate3", target: "animate2" },
{ source: "animate1", target: "animate8" },
{ source: "animate3", target: "animate5" },
{ source: "analytics", target: "analytics2" },
{ source: "analytics1", target: "analytics2" },
{ source: "analytics1", target: "analytics" }
] ;
let root = {
"children": [
{
"name": "analytics",
"children": [
{
"name": "animate1",
"size": 1500
},
{
"name": "animate2",
"size": 1500
},
{
"name": "animate3",
"size": 1500
}
]
},
{
"name": "analytics1",
"children": [
{
"name": "animate4",
"size": 1500
},
{
"name": "animate5",
"size": 1500
},
{
"name": "animate6",
"size": 1500
}
]
},
{
"name": "analytics2",
"children": [
{
"name": "animate7",
"size": 1500
},
{
"name": "animate8",
"size": 1500
},
{
"name": "animate9",
"size": 1500
}
]
}
]
}
svg.append("svg:defs")
.append("svg:marker")
.attr("id", "arrow")
.attr("viewBox", "0 0 10 10")
.attr("refX", 10)
.attr("refY", 5)
.attr("markerUnits", "strokeWidth")
.attr("markerWidth", 6)
.attr("markerHeight", 3)
.attr("orient", "auto")
.append("svg:path")
.style("stroke","none")
.attr("d", "M 0 0 L 10 5 L 0 10 z");
const pack = d3.pack()
.size([diameter/2, diameter/2])
.padding(40)
root = d3.hierarchy(root)
.sum(function(d) { return d.size; })
.sort(function(a, b) { return b.value - a.value; });
const node = g.selectAll(".node")
.data(pack(root).descendants())
.enter()
.filter(function(d){
return !!d.data.name
})
.append("g")
.attr('id', function(d) {
return d.data.name;
})
.attr("class", function(d) { return d.children ? "node" : "leaf node"; })
.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; });
node.append("title")
.text(function(d) { return d.data.name + "n" + format(d.value); });
const arrow = svg.selectAll('path.arrow').data( links, JSON.stringify );
arrow.enter()
.append("path")
.attr("class","arrow")
.attr("x1", function(d){
let translate = getTranslate(d.source);
return translate[0]
})
.attr("x2", function(d){
let translate = getTranslate(d.target);
return translate[0]
})
.attr("y1", function(d){
let translate = getTranslate(d.source);
return translate[1]
})
.attr("y2", function(d){
let translate = getTranslate(d.target);
return translate[1]
})
.attr("d", function(d) {
let source = getTranslate(d.source),
target = getTranslate(d.target),
x1= source[0],
x2= target[0],
y1= source[1],
y2= target[1];
let dx = x1 - x2,
dy = y1 - y2,
dr = Math.sqrt(dx * dx + dy * dy);
// return "M" + x1 + "," + y1 + "A" + dr + "," + dr + " 0 0,1 " + (x2 - 0.6) + "," + (y2 - 0.9);
return "M" + x1 + "," + y1 + "A" + dr + "," + dr + " 0 0,1 " + x2 + "," + y2;
})
.style("stroke", "black")
.style("fill", "none")
.style("stroke-width", 3)
.attr("marker-end", "url(#arrow)");
node.append("circle")
.attr("r", function(d) { return d.r; })
node.filter(function(d) { return !d.children; }).append("text")
.attr("dy", "0.3em")
.text(function(d) { return d.data.name.substring(0, d.r / 3); });
function getTranslate(datum) {
const circle = d3.select('#'+datum);
const string = circle.attr("transform");
const translate = string.substring(string.indexOf("(")+1, string.indexOf(")")).split(",");
return translate;
}
circle {
fill: rgb(31, 119, 180);
fill-opacity: .25;
stroke: rgb(31, 119, 180);
stroke-width: 1px;
}
.leaf circle {
fill: #ff7f0e;
fill-opacity: 1;
}
text {
font: 10px sans-serif;
text-anchor: middle;
}
<svg width="960" height="960"></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>