Plotly.js桑基图链接添加数值标签的实现疑问
Plotly.js桑基图链接添加数值标签的实现疑问
你好呀!刚看了你的问题和代码,你尝试给桑基图的link对象加label属性来显示链接数值,但没生效——这其实是因为Plotly.js的Sankey组件目前并不支持直接通过link.label来渲染链接上的文本标签,这个属性和node.label的作用不一样,它不会被可视化出来,所以你加了也不会显示~
不过别担心,我们可以用Plotly的annotations(注释功能)来手动实现这个需求,通过计算每个链接的中心位置,把数值(或自定义文本)作为注释添加到对应位置。下面我基于你的代码修改了一个可运行的版本,你可以参考:
window.addEventListener('DOMContentLoaded', () => { const chartDiv = document.getElementById('chartDiv'); const labels = ["Start", "Middle", "Begin", "End", "Final"]; const labelIndex = new Map(labels.map((label, i) => [label, i])); const links = [ { source: "Start", target: "Middle", value: 5, label: "Test" }, { source: "Start", target: "Middle", value: 3, label: "Test2" }, { source: "Middle", target: "Start", value: 1, label: "" }, { source: "Start", target: "End", value: 2, label: "" }, { source: "Begin", target: "Middle", value: 5, label: "Test" }, { source: "Middle", target: "End", value: 3, label: "" }, { source: "Final", target: "Final", value: 0.0001, label: "" } ]; const sources = links.map(link => labelIndex.get(link.source)); const targets = links.map(link => labelIndex.get(link.target)); const values = links.map(link => link.value); const customData = links.map(link => [link.source, link.target, link.value]); const trace = { type: "sankey", orientation: "h", arrangement: "fixed", node: { label: labels, pad: 15, thickness: 20, line: { color: "black", width: 0.5 }, hoverlabel: { bgcolor: "white", bordercolor: "darkgrey", font: { color: "black", family: "Open Sans, Arial", size: 14 } }, hovertemplate: '%{label}<extra></extra>', color: ["#a6cee3", "#1f78b4", "#b2df8a", "#a9b1b9", "#a9b1b9"] }, link: { source: sources, target: targets, value: values, arrowlen: 20, pad: 20, thickness: 20, line: { color: "black", width: 0.2 }, color: sources.map(i => ["#a6cee3", "#1f78b4", "#b2df8a", "#a9b1b9", "#a9b1b9"][i]), customdata: customData, hoverlabel: { bgcolor: "white", bordercolor: "darkgrey", font: { color: "black", family: "Open Sans, Arial", size: 14 } }, hovertemplate: '<b>%{customdata[0]}</b> → <b>%{customdata[1]}</b><br>' + 'Flow Value: <b>%{customdata[2]}</b><extra></extra>' } }; // 计算每个链接的注释内容与位置 const annotations = []; // 固定布局下,节点x坐标均匀分布(0到1的相对位置) const nodeXPositions = labels.map((_, i) => i / (labels.length - 1)); // 水平桑基图中,节点默认在y=0.5的水平线上 const nodeYPositions = Array(labels.length).fill(0.5); links.forEach((link, idx) => { const sourceIdx = labelIndex.get(link.source); const targetIdx = labelIndex.get(link.target); // 跳过极小值的自循环链接,避免无意义标签 if (link.value <= 0.0001 && sourceIdx === targetIdx) return; // 计算链接中点的相对坐标 const midX = (nodeXPositions[sourceIdx] + nodeXPositions[targetIdx]) / 2; const midY = nodeYPositions[sourceIdx]; annotations.push({ x: midX, y: midY, text: link.value.toString(), // 可替换为link.label显示自定义文本 showarrow: false, font: { size: 12, color: '#222' }, xanchor: 'center', yanchor: 'middle' }); }); const layout = { font: { size: 14 }, annotations: annotations // 将注释加入布局 }; Plotly.newPlot(chartDiv, [trace], layout, { responsive: true, displayModeBar: true }) .then((plot) => { chartDiv.on('plotly_click', function(eventData) { console.log(eventData); if (!eventData || !eventData.points || !eventData.points.length) return; const point = eventData.points[0]; if (typeof point.pointIndex === "number") { const nodeLabel = point.label; alert("Node clicked: " + nodeLabel + "\nNode index: " + point.pointIndex); console.log("Node clicked:", point); } else if (typeof point.pointNumber === "number") { const linkIdx = point.pointNumber; const linkData = customData[linkIdx]; alert( "Link clicked: " + linkData[0] + " → " + linkData[1] + "\nValue: " + linkData[2] + "\nLink index: " + linkIdx ); console.log("Link clicked:", point); } else { console.log("Clicked background", point); } }); }); });
关键说明
为什么
link.label不生效?
Plotly的Sankey组件中,link对象的label属性并不是用于渲染可视化文本的,它和node.label的设计逻辑不同,官方文档里也没有把它作为可渲染的属性列出,所以直接设置不会有效果。注释方案的核心逻辑
因为你用了arrangement: "fixed"(固定节点布局),我们可以手动计算每个节点的相对坐标,再通过源节点和目标节点的坐标算出链接中点,把数值(或你定义的link.label)作为无箭头注释放在中点位置。特殊情况处理
代码里跳过了Final→Final这种极小值的自循环链接,你可以根据自己的需求调整过滤条件;如果是垂直方向的桑基图,只需要把x/y坐标的计算逻辑互换即可。
内容来源于stack exchange
相关产品推荐
相关产品推荐

