如何在D3.js树图中根据问卷数据调整节点间连线宽度
实现D3可折叠树图的比例化连线宽度
1. 扩展数据结构
首先给每个子节点添加代表受访者比例的字段(比如proportion,取值0-1),让D3能读取到比例数据:
{ "name": "Question 1", "children": [ { "name": "Yes", "proportion": 0.65 }, // 65%受访者选Yes { "name": "No", "proportion": 0.35 } // 35%受访者选No ] }
如果是多层嵌套的问卷(比如Yes选项下还有后续问题),每个子节点都需要添加该字段。
2. 创建连线宽度比例尺
用D3的比例尺将0-1的比例转换为合适的像素宽度,可选线性或平方根比例尺(后者能放大高比例的视觉差异):
// 线性比例尺:比例0对应1px,比例1对应10px,可按需调整 const linkWidthScale = d3.scaleLinear() .domain([0, 1]) .range([1, 10]); // 可选:用平方根比例尺强化高比例的视觉表现 // const linkWidthScale = d3.scaleSqrt() // .domain([0, 1]) // .range([1, 15]);
3. 修改连线绘制逻辑
在树图的连线生成代码中,绑定数据后通过stroke-width属性应用比例尺:
// 初始绘制连线 const link = svg.selectAll(".link") .data(links) .enter().append("path") .attr("class", "link") .attr("d", diagonal) // 原树图的对角线生成函数 .attr("stroke-width", d => linkWidthScale(d.target.proportion));
D3树布局生成的links数组中,每个元素包含source(父节点)和target(子节点),所以直接取子节点的proportion值即可。
4. 处理折叠/展开的更新逻辑
当树节点折叠或展开时,需要在连线更新代码中重新设置宽度:
// 在update函数的连线更新部分添加 link.attr("d", diagonal) .attr("stroke-width", d => linkWidthScale(d.target.proportion));
5. 可选:添加比例图例
为让用户直观理解线宽对应的比例,可以在SVG中添加简单图例:
// 在SVG右上角绘制图例 const legend = svg.append("g") .attr("transform", `translate(${width - 120}, 20)`); // 0% 标记 legend.append("line") .attr("x1", 0).attr("x2", 60) .attr("y1", 0).attr("y2", 0) .attr("stroke", "#777") .attr("stroke-width", linkWidthScale(0)); legend.append("text") .attr("x", 70).attr("y", 5) .style("font-size", "12px") .text("0%"); // 50% 标记 legend.append("line") .attr("x1", 0).attr("x2", 60) .attr("y1", 25).attr("y2", 25) .attr("stroke", "#777") .attr("stroke-width", linkWidthScale(0.5)); legend.append("text") .attr("x", 70).attr("y", 30) .style("font-size", "12px") .text("50%"); // 100% 标记 legend.append("line") .attr("x1", 0).attr("x2", 60) .attr("y1", 50).attr("y2", 50) .attr("stroke", "#777") .attr("stroke-width", linkWidthScale(1)); legend.append("text") .attr("x", 70).attr("y", 55) .style("font-size", "12px") .text("100%");
内容的提问来源于stack exchange,提问作者creekjumper
相关产品推荐
相关产品推荐

