d3-sankey 是最靠谱的 HTML 桑基图方案,需用 SVG 渲染与布局计算;正确导入模块、规范数据结构(nodes + links)、合理设置 nodeWidth/nodePadding、修正 text 锚点、导出时处理 defs 与字体,才能实现精准流向与可读性。

用 D3.js 的 d3-sankey 是最靠谱的 HTML 桑基图方案
纯 HTML/CSS 做不出桑基图——它本质是带流向、权重和弯曲连接线的有向图,必须依赖 SVG 渲染 + 布局计算。D3.js 的 d3-sankey 模块(v6+ 内置在 d3 包中)是当前最稳定、文档最全、社区支持最多的实现路径。
别被“D3 太重”劝退:你不需要学全 D3,只引入 Sankey 相关部分即可,实际代码量往往不到 50 行。
常见错误现象:Sankey is not defined(没正确加载模块)、节点重叠或连线断裂(数据格式错)、文字不显示(append("text") 位置算错)。
实操建议:
立即学习“前端免费学习笔记(深入)”;
- 用 ES 模块方式导入:
import { sankey, sankeyCenter, sankeyJustify } from "d3"; - 数据必须是两层结构:
nodes(数组,每个元素含id) +links(数组,每个含source、target、value,其中source/target是nodes索引或对象引用) - 别手动写
<path></path>:让sankey()返回的生成器自动算出每条 link 的d属性
sankey.nodeWidth 和 sankey.nodePadding 控制视觉节奏
桑基图可读性不取决于颜色,而在于节点宽度、间距与流线粗细的协调。这两个参数直接影响布局结果,且容易被忽略。
nodeWidth 默认是 15,太窄会导致文本挤在一起;设为 24–32 更适合中文标签。nodePadding 默认是 10,值太小会让相邻节点粘连,太大则浪费横向空间。
使用场景:当发现节点堆成一列、流线全部挤在中间,大概率是这两个值没调好。
实操建议:
立即学习“前端免费学习笔记(深入)”;
- 先固定画布宽度(如
width = 960),再按节点数反推合理nodeWidth:nodeWidth: Math.max(24, (width - 120) / nodes.length) -
nodePadding推荐设为8或12,避免用奇数(SVG 渲染可能模糊) - 改完后务必重新调用
sankey(data),否则布局不更新
中文标签截断?给 <text></text> 加 text-anchor="middle" 和 dominant-baseline="middle"
桑基图里节点名常被截掉下半部分(尤其“下”“之”“处”这类字),不是字体问题,是 SVG <text></text> 的锚点默认在左下角,而 D3 的 y 坐标给的是基线位置。
错误写法:
.append("text").attr("x", d => d.x0).attr("y", d => d.y0) → 文字会整体上浮,底部被裁。
实操建议:
立即学习“前端免费学习笔记(深入)”;
- 节点文字统一加:
.attr("text-anchor", "middle")<br>.attr("dominant-baseline", "middle")<br>.attr("x", d => d.x0 + d.width / 2)<br>.attr("y", d => d.y0 + (d.y1 - d.y0) / 2) - 如果文字仍溢出,用
getComputedTextLength()动态缩放,但多数情况加font-size: 12px就够了 - 别用
transform="translate()"替代x/y,会干扰后续点击定位
导出为 PNG 时连线变虚、文字消失?用 canvg 要注意 <defs></defs> 和字体
直接用 html2canvas 截桑基图,大概率出现连线锯齿、渐变丢失、文字空白——因为 SVG 中的 <defs></defs>(比如 <lineargradient></lineargradient>)和外部字体未被正确解析。
性能影响:客户端导出大图(>1600px 宽)易卡顿,服务端渲染更稳,但前端临时导出仍是刚需。
实操建议:
立即学习“前端免费学习笔记(深入)”;
- 优先用
canvg(v4+):const canvas = document.createElement("canvas");<br>await canvg(canvas, svg.outerHTML);<br>canvas.toBlob(blob => {...}); - 确保 SVG 元素内联定义所有样式(别靠 CSS 文件),
<defs></defs>必须在<svg></svg>根节点下 - 中文字体必须用已加载的 web font,或回退到
font-family: sans-serif,别写"PingFang SC"这类系统字体
真正难的不是画出来,而是让每条流的宽度严格正比于 value,且所有节点垂直居中对齐——这得靠反复调 sankey.iterations(32) 和检查 link.value 是否被归一化。没做这步,图看着就“不稳”。



















