利用集群布局(cluster)和放射对角线(svg.diagonal.radial)生成
d3.json("city.json", function (err, data) {
const width = 700, height = 800, padding = {top: 60, left: 30};
const svg = d3.select("body").append("svg")
.attr("width", width + padding.left * 2)
.attr("height", height + padding.top * 2);
const container = svg.append("g")
.attr("transform", "translate(350,400)");
// 创建布局器
const cluster = d3.layout.cluster()
// 第一个参数用来生成范围内的x(角度) 第二个参数用来生成范围内的y(半径)
.size([360, width / 2 - 100])
.separation(function (a, b) {
return (a.parent === b.parent ? 1 : 1) / a.depth;
});
// 生成节点
const nodes = cluster.nodes(data);
// 生成连接数组
const links = cluster.links(nodes);
const color = d3.scale.category20();
// 放射性对角线 需要{source:[radius,angle],target:[radius,angle]}
const diagonal = d3.svg.diagonal.radial()
.projection(function (d) {
const radius = d.y,
// 把角度转换成弧长比例
angle = d.x / 180 * Math.PI;
return [radius, angle];
});
container.selectAll(".line")
.data(links)
.enter()
.append("path")
.attr("d", diagonal)
.attr("fill", "none")
.attr("stroke", "purple")
.attr("stroke-width", 2);
const legend = container.selectAll(".legend")
.data(nodes)
.enter()
.append("g")
.attr("class", "legend")
.attr("transform", function (d) {
return "rotate(" + (d.x - 90) + ")" + "translate(" + d.y + ")";
});
legend.append("circle")
.attr("r", 6)
.attr("fill", function (d, i) {
return color(i);
});
legend.append("text")
.attr("text-anchor", function (d, i) {
return d.x < 180 ? "start" : "end"
}).attr("transform", function (d) {
return d.x < 180 ? "translate(8)" : "rotate(180)translate(-8)"
}).text(function (d) {
return d.name;
})
})
结果: