效果图展示:
实现思路:
使用 D3.js 的 d3.tree()
和 d3.stratify()
来创建树布局,并将其转换为径向布局。
实现步骤
1、svg
html中准备一个svg元素,也可以通过js创建
arduino
<svg width="600" height="600"></svg>
确定画布大小、径向图半径
ini
const svg = d3.select("svg");
const width = +svg.attr("width");
const height = +svg.attr("height");
const radius = Math.min(width, height) / 2;
2、创建树布局
scss
const tree = d3.tree().size([2 * Math.PI, radius - 100]); //角度2PI,半径radius - 100
// 使用 d3.hierarchy 将数据转换为树结构
const root = d3.hierarchy(data);
// 布局树
tree(root);
3、创建一个组元素,控制径向图居中
ini
const g = svg.append("g").attr("transform", `translate(${width / 2},${height / 2})`);
4、绘制连线
javascript
//径向链接生成器类似于笛卡尔链接生成器,只是x和y访问器被角度和半径访问器取代。
//返回具有径向切线的新链接生成器
const linkRadial = d3
.linkRadial()
.angle((d) => d.x)
.radius((d) => d.y)
//绘制连线
const link = g.selectAll(".link")
.data(root.descendants().slice(1)) //过滤掉顶层节点
.enter().append("path")
.attr("class", "link")
.attr("d", d => {
return linkRadial({ source: d, target: d.parent })
});
5、绘制节点
javascript
const node = g.selectAll(".node")
.data(root.descendants())
.enter().append("g")
.attr("class", d => "node" + (d.children ? " node--internal" : " node--leaf"))
.attr("transform", d => `rotate(${(d.x - Math.PI / 2) / Math.PI * 180}) translate(${d.y},0)`);
6、添加节点圆圈
go
node.append("circle")
.attr("r", 4.5);
7、添加节点文本
javascript
node.append("text")
.attr("dy", ".31em")
.attr("x", d => d.x < Math.PI ? 8 : -8)
.attr("text-anchor", d => d.x < Math.PI ? "start" : "end")
.attr("transform", d => d.x >= Math.PI ? "rotate(180)" : "")
.text(d => d.data.name);
8、完整代码
xml
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Radial Tree with D3.js</title>
<script src="https://d3js.org/d3.v7.min.js"></script>
<style>
.node {
cursor: pointer;
}
.node circle {
fill: #999;
}
.node text {
font: 10px sans-serif;
}
.link {
fill: none;
stroke: #555;
stroke-opacity: 0.4;
stroke-width: 1.5px;
}
</style>
</head>
<body>
<svg width="600" height="600"></svg>
<script>
const data = {
name: "Root",
children: [
{
name: "Child 1",
children: [
{ name: "Grandchild 1" },
{ name: "Grandchild 2" },
],
},
{
name: "Child 2",
children: [
{ name: "Grandchild 3" },
{ name: "Grandchild 4" },
],
},
],
};
const svg = d3.select("svg");
const width = +svg.attr("width");
const height = +svg.attr("height");
const radius = Math.min(width, height) / 2;
// 创建树布局
const tree = d3.tree().size([2 * Math.PI, radius - 100]);
// 使用 d3.hierarchy 将数据转换为树结构
const root = d3.hierarchy(data);
// 布局树
tree(root);
// 创建一个组元素,用于平移和缩放
const g = svg.append("g").attr("transform", `translate(${width / 2},${height / 2})`);
// 绘制连接线
const linkRadial = d3
.linkRadial()
.angle((d) => d.x)
.radius((d) => d.y)
const link = g.selectAll(".link")
.data(root.descendants().slice(1)) //过滤掉顶层节点
.enter().append("path")
.attr("class", "link")
.attr("d", d => {
return linkRadial({ source: d, target: d.parent })
});
// 绘制节点
const node = g.selectAll(".node")
.data(root.descendants())
.enter().append("g")
.attr("class", d => "node" + (d.children ? " node--internal" : " node--leaf"))
.attr("transform", d => `rotate(${(d.x - Math.PI / 2) / Math.PI * 180}) translate(${d.y},0)`);
// 添加节点圆圈
node.append("circle")
.attr("r", 4.5);
// 添加节点文本
node.append("text")
.attr("dy", ".31em")
.attr("x", d => d.x < Math.PI ? 8 : -8)
.attr("text-anchor", d => d.x < Math.PI ? "start" : "end")
.attr("transform", d => d.x >= Math.PI ? "rotate(180)" : "")
.text(d => d.data.name);
</script>
</body>
</html>