您现在的位置是:网站首页 > 桑基图(Sankey)实现文章详情
桑基图(Sankey)实现
陈川
【
ECharts
】
39462人已围观
6358字
桑基图(Sankey)实现
桑基图是一种流图,用于展示能量、物质或成本在不同节点间的流动情况。ECharts提供了强大的桑基图支持,能够直观呈现复杂系统中的流量分配与转化关系。下面从基础配置到高级应用全面解析ECharts桑基图的实现方法。
基础配置
最简单的桑基图需要定义nodes和links两个核心数据项。nodes表示图中的节点,links表示节点间的流向关系。以下是一个基础示例:
option = {
series: [{
type: 'sankey',
layout: 'none',
data: [
{ name: 'A' },
{ name: 'B' },
{ name: 'C' },
{ name: 'D' }
],
links: [
{ source: 'A', target: 'B', value: 10 },
{ source: 'A', target: 'C', value: 5 },
{ source: 'B', target: 'D', value: 8 },
{ source: 'C', target: 'D', value: 4 }
]
}]
};
这段代码创建了四个节点(A/B/C/D)和四条连接线。value属性决定了线条的宽度,数值越大线条越粗。layout参数保持默认'none'即可,ECharts会自动计算节点位置。
样式定制
ECharts桑基图支持多层次的样式定制:
节点样式
series: [{
type: 'sankey',
nodeWidth: 20, // 节点矩形宽度
nodeGap: 10, // 节点纵向间距
nodeAlign: 'justify', // 节点对齐方式
draggable: true, // 允许拖拽节点
label: {
color: '#333',
fontSize: 12,
position: 'right' // 标签显示位置
},
itemStyle: {
borderWidth: 1,
borderColor: '#aaa'
}
}]
连线样式
series: [{
type: 'sankey',
lineStyle: {
color: 'source', // 跟随源节点颜色
curveness: 0.5, // 连线曲度
opacity: 0.8
},
emphasis: { // 高亮样式
lineStyle: {
shadowBlur: 10,
shadowColor: 'rgba(0,0,0,0.5)'
}
}
}]
数据动态更新
桑基图支持动态数据更新,这在展示随时间变化的流量时非常有用:
function updateChart() {
const newNodes = generateRandomNodes();
const newLinks = generateRandomLinks(newNodes);
myChart.setOption({
series: [{
data: newNodes,
links: newLinks
}]
});
}
// 每2秒更新一次
setInterval(updateChart, 2000);
多层桑基图
对于复杂的业务流程,可以构建多层桑基图:
const nodes = [
{ name: '原料采购', depth: 0 },
{ name: '生产加工', depth: 1 },
{ name: '质检', depth: 2 },
{ name: '仓储', depth: 3 },
{ name: '物流', depth: 4 },
{ name: '零售', depth: 5 }
];
const links = [
{ source: '原料采购', target: '生产加工', value: 100 },
{ source: '生产加工', target: '质检', value: 95 },
{ source: '质检', target: '仓储', value: 90 },
{ source: '仓储', target: '物流', value: 88 },
{ source: '物流', target: '零售', value: 85 }
];
通过为节点添加depth属性,可以控制节点的层级位置,构建出清晰的流程视图。
交互功能增强
ECharts桑基图支持丰富的交互功能:
myChart.on('click', function(params) {
if (params.dataType === 'node') {
console.log('点击节点:', params.name);
} else if (params.dataType === 'edge') {
console.log(`点击连线: ${params.data.source} -> ${params.data.target}`);
}
});
// 添加图例交互
legend: {
data: ['类别1', '类别2'],
selectedMode: 'single',
selected: {
'类别1': true,
'类别2': false
}
}
大数据量优化
当处理大规模数据时,需要进行性能优化:
series: [{
type: 'sankey',
layoutIterations: 0, // 减少布局计算次数
nodeWidth: 8, // 减小节点宽度
nodeGap: 6, // 减小节点间距
focusNodeAdjacency: false, // 关闭相邻高亮
silent: true, // 关闭交互事件
progressive: 200, // 渐进式渲染
animation: false // 关闭动画
}]
实际应用案例
能源流动分析
const energyNodes = [
{ name: '煤炭', itemStyle: { color: '#333' }},
{ name: '石油', itemStyle: { color: '#8B0000' }},
{ name: '天然气', itemStyle: { color: '#4682B4' }},
{ name: '发电', itemStyle: { color: '#FFA500' }},
{ name: '工业', itemStyle: { color: '#808080' }},
{ name: '居民', itemStyle: { color: '#228B22' }}
];
const energyLinks = [
{ source: '煤炭', target: '发电', value: 45 },
{ source: '石油', target: '工业', value: 30 },
{ source: '天然气', target: '居民', value: 25 },
{ source: '发电', target: '工业', value: 35 },
{ source: '发电', target: '居民', value: 10 }
];
网站用户行为路径
const userPathNodes = [
{ name: '首页' },
{ name: '产品页' },
{ name: '详情页' },
{ name: '购物车' },
{ name: '支付页' },
{ name: '订单完成' },
{ name: '离开' }
];
const userPathLinks = [
{ source: '首页', target: '产品页', value: 1000 },
{ source: '产品页', target: '详情页', value: 600 },
{ source: '详情页', target: '购物车', value: 300 },
{ source: '购物车', target: '支付页', value: 200 },
{ source: '支付页', target: '订单完成', value: 180 },
{ source: '首页', target: '离开', value: 400 },
{ source: '产品页', target: '离开', value: 300 }
];
高级技巧
自定义节点形状
series: [{
type: 'sankey',
nodeShape: 'roundRect',
nodeStyle: {
radius: 5
},
label: {
formatter: function(params) {
return `${params.name}\n${params.value}吨`;
}
}
}]
混合其他图表类型
series: [
{
type: 'sankey',
// 桑基图配置
},
{
type: 'pie',
center: ['80%', '30%'],
radius: '30%',
data: [
{ value: 45, name: '煤炭' },
{ value: 30, name: '石油' },
{ value: 25, name: '天然气' }
]
}
]
响应式设计
确保桑基图在不同设备上正常显示:
window.addEventListener('resize', function() {
myChart.resize();
});
// 响应式配置
option = {
responsive: true,
media: [
{
query: { maxWidth: 768 },
option: {
series: [{
label: { fontSize: 10 },
nodeWidth: 10
}]
}
}
]
}
数据预处理
对于原始数据通常需要预处理:
function processSankeyData(rawData) {
const nodes = [];
const nodeMap = new Map();
const links = [];
// 提取唯一节点
rawData.forEach(item => {
if (!nodeMap.has(item.source)) {
nodeMap.set(item.source, true);
nodes.push({ name: item.source });
}
if (!nodeMap.has(item.target)) {
nodeMap.set(item.target, true);
nodes.push({ name: item.target });
}
});
// 聚合相同source-target的value
const linkMap = new Map();
rawData.forEach(item => {
const key = `${item.source}|${item.target}`;
if (linkMap.has(key)) {
linkMap.get(key).value += item.value;
} else {
linkMap.set(key, { ...item });
}
});
links.push(...linkMap.values());
return { nodes, links };
}
国际化支持
为桑基图添加多语言支持:
const i18n = {
en: {
title: 'Energy Flow',
tooltip: 'Flow value: {c}'
},
zh: {
title: '能源流动图',
tooltip: '流量值: {c}'
}
};
function setLanguage(lang) {
myChart.setOption({
title: { text: i18n[lang].title },
tooltip: { formatter: i18n[lang].tooltip }
});
}
性能监控
添加图表性能监控:
myChart.on('rendered', function() {
const perfData = myChart.getModel().getPerformanceData();
console.log('渲染耗时:', perfData.renderTime);
});
// 大数据量时显示加载提示
myChart.showLoading({
text: '数据加载中...',
color: '#c23531',
textColor: '#000',
maskColor: 'rgba(255, 255, 255, 0.8)'
});
上一篇: 旭日图(Sunburst)实现
下一篇: 平行坐标系(Parallel)实现