如何使用D3创建气泡树二元图表
你要实现的气泡树图表可以通过D3原生的d3.pack()布局结合自定义连线逻辑完成,不需要依赖额外的封装资源,具体实现方案如下:
实现步骤
- 准备层级数据结构
源数据采用标准的嵌套JSON格式即可,每个节点包含name(节点名称)、value(控制气泡大小的数值)、children(子节点数组,叶子节点可省略)字段,示例结构如下:
{ "name": "根节点", "value": 100, "children": [ { "name": "一级子节点1", "value": 60, "children": [ {"name": "二级子节点1-1", "value": 30}, {"name": "二级子节点1-2", "value": 30} ] }, { "name": "一级子节点2", "value": 40 } ] }
- 调用pack布局计算气泡坐标
通过d3.pack()布局自动计算每个节点的圆心坐标和半径参数:
// 定义画布尺寸,可根据需求调整 const width = 800, height = 600; // 初始化pack布局,设置画布尺寸和气泡间距 const packLayout = d3.pack() .size([width, height]) .padding(15); // 处理层级数据,按value字段计算气泡大小 const rootNode = d3.hierarchy(yourSourceData) .sum(d => d.value) .sort((a, b) => b.value - a.value); // 生成所有节点的坐标、半径信息 const allNodes = packLayout(rootNode).descendants();
- 绘制父子节点连接线
遍历所有非根节点,生成父节点到当前节点的连线:
const svg = d3.select("svg") .attr("width", width) .attr("height", height); svg.selectAll(".tree-link") .data(allNodes.filter(node => node.parent)) .join("path") .attr("class", "tree-link") .attr("d", node => d3.line()([[node.x, node.y], [node.parent.x, node.parent.y]])) .attr("stroke", "#e2e2e2") .attr("fill", "none") .attr("stroke-width", 1);
- 绘制气泡和文本标签
最后绘制气泡和对应的标签,还可按需添加点击展开收起、悬浮提示等交互:
// 绘制气泡 svg.selectAll(".bubble-node") .data(allNodes) .join("circle") .attr("class", "bubble-node") .attr("cx", node => node.x) .attr("cy", node => node.y) .attr("r", node => node.r) .attr("fill", (_, i) => d3.schemeCategory10[i % 10]) .attr("opacity", 0.7) .attr("stroke", "#fff") .attr("stroke-width", 2); // 绘制节点文本 svg.selectAll(".node-label") .data(allNodes) .join("text") .attr("class", "node-label") .attr("x", node => node.x) .attr("y", node => node.y) .attr("text-anchor", "middle") .attr("dominant-baseline", "middle") .attr("font-size", 12) .text(node => node.data.name);
自定义调整说明
- 若只需要叶子节点显示气泡,只需在绘制气泡的步骤筛选
allNodes.filter(node => !node.children)即可,连接线逻辑无需改动 - 若需要实现画布缩放平移,给外层svg绑定
d3.zoom()事件监听即可适配 - 气泡大小范围、配色、间距都可以通过调整pack布局参数和颜色比例尺自定义
内容的提问来源于stack exchange,提问作者Prasad Sonawane
相关产品推荐
相关产品推荐

