桑基图(Sankey)实现
桑基图的基本概念
桑基图是一种特定类型的流图,用于描述能量、物质或成本在不同节点间的流动情况。图中节点由带箭头的线连接,线条宽度与流量成比例。这种图表最早由爱尔兰工程师Matthew Henry Phineas Riall Sankey于1898年提出,用于展示蒸汽机的能量效率。
在数据可视化领域,桑基图特别适合展示:
- 能源系统中的能量流动
- 资金在不同部门间的转移
- 网站用户在不同页面间的跳转路径
- 产品生产过程中的物料流动
ECharts中的桑基图实现
ECharts提供了完善的桑基图支持,通过简单的配置即可创建复杂的流动关系图。基本实现需要定义nodes(节点)和links(连接)两个核心数据结构:
option = {
series: [{
type: 'sankey',
layout: 'none',
data: [{
name: '节点1'
}, {
name: '节点2'
}],
links: [{
source: '节点1',
target: '节点2',
value: 10
}]
}]
};
节点与连接配置
节点高级配置
节点支持丰富的样式定制,包括颜色、标签显示等:
data: [
{
name: '原料采购',
itemStyle: {
color: '#FF9F43'
},
label: {
color: '#333',
fontWeight: 'bold'
}
},
{
name: '生产加工',
itemStyle: {
color: '#28C76F'
}
}
]
连接线样式控制
连接线可以设置渐变色、透明度等视觉效果:
links: [
{
source: '原料采购',
target: '生产加工',
value: 100,
lineStyle: {
color: 'gradient',
opacity: 0.7,
curveness: 0.2
}
}
]
交互功能实现
ECharts桑基图支持丰富的交互功能:
series: [{
type: 'sankey',
// 启用拖拽
draggable: true,
// 节点点击事件
emphasis: {
focus: 'adjacency',
itemStyle: {
borderWidth: 3
}
},
// 鼠标悬停样式
blur: {
itemStyle: {
opacity: 0.1
}
}
}]
复杂数据示例
处理多层级数据时,需要合理组织节点关系:
const data = {
nodes: [
{name: '总预算'},
{name: '市场部'},
{name: '研发部'},
{name: '线上广告'},
{name: '线下活动'},
{name: '产品开发'},
{name: '技术研究'}
],
links: [
{source: '总预算', target: '市场部', value: 300},
{source: '总预算', target: '研发部', value: 500},
{source: '市场部', target: '线上广告', value: 200},
{source: '市场部', target: '线下活动', value: 100},
{source: '研发部', target: '产品开发', value: 350},
{source: '研发部', target: '技术研究', value: 150}
]
};
样式深度定制
全局样式配置
series: [{
type: 'sankey',
nodeWidth: 20,
nodeGap: 10,
layoutIterations: 32,
label: {
position: 'right',
fontSize: 12
},
lineStyle: {
color: 'source',
curveness: 0.5
}
}]
响应式设计
结合ECharts的resize方法实现响应式:
window.addEventListener('resize', function() {
myChart.resize();
});
// 容器样式
#chart-container {
width: 100%;
height: 600px;
min-width: 300px;
}
动态数据更新
实现数据动态加载和更新:
function updateChart(newData) {
const option = myChart.getOption();
option.series[0].data = newData.nodes;
option.series[0].links = newData.links;
myChart.setOption(option);
}
// 模拟异步数据加载
setTimeout(() => {
updateChart({
nodes: [...],
links: [...]
});
}, 2000);
性能优化技巧
处理大规模数据时的优化方案:
series: [{
type: 'sankey',
// 减少布局迭代次数
layoutIterations: 0,
// 简化节点样式
itemStyle: {
borderWidth: 0
},
// 关闭动画
animation: false,
// 使用level配置控制显示层级
levels: [{
depth: 0,
itemStyle: {
color: '#F76258'
}
}, {
depth: 1,
itemStyle: {
color: '#33B5E5'
}
}]
}]
实际应用案例
网站用户行为分析
const userFlowData = {
nodes: [
{name: '首页'},
{name: '产品页'},
{name: '购物车'},
{name: '支付页'},
{name: '注册页'},
{name: '退出'}
],
links: [
{source: '首页', target: '产品页', value: 1560},
{source: '产品页', target: '购物车', value: 870},
{source: '购物车', target: '支付页', value: 490},
{source: '支付页', target: '退出', value: 480},
{source: '首页', target: '注册页', value: 320},
{source: '注册页', target: '退出', value: 300}
]
};
能源流动分析
const energyData = {
nodes: [
{name: '煤炭', itemStyle: {color: '#333'}},
{name: '天然气', itemStyle: {color: '#4E79A7'}},
{name: '发电', itemStyle: {color: '#F28E2B'}},
{name: '工业', itemStyle: {color: '#E15759'}},
{name: '居民', itemStyle: {color: '#76B7B2'}}
],
links: [
{source: '煤炭', target: '发电', value: 50},
{source: '天然气', target: '发电', value: 30},
{source: '发电', target: '工业', value: 45},
{source: '发电', target: '居民', value: 35}
]
};
高级交互功能
实现自定义tooltip和点击事件:
option = {
tooltip: {
trigger: 'item',
formatter: function(params) {
if(params.dataType === 'node') {
return `${params.name}<br/>总流入: ${params.value}`;
} else {
return `${params.source} → ${params.target}<br/>流量: ${params.value}`;
}
}
},
series: [{
type: 'sankey',
// ...
}]
};
myChart.on('click', function(params) {
if(params.dataType === 'node') {
console.log('点击节点:', params.name);
} else if(params.dataType === 'edge') {
console.log(`点击连接: ${params.source}→${params.target}`);
}
});
与其他图表组合
桑基图与饼图组合展示:
option = {
grid: [
{left: '5%', top: '5%', width: '45%', height: '90%'},
{right: '5%', top: '5%', width: '45%', height: '90%'}
],
series: [
{
type: 'sankey',
gridIndex: 0,
// ...桑基图配置
},
{
type: 'pie',
gridIndex: 1,
radius: ['30%', '70%'],
data: [
{value: 335, name: '直接访问'},
{value: 310, name: '邮件营销'}
]
}
]
};
数据格式转换
将原始数据转换为桑基图所需格式:
function convertToSankeyData(rawData) {
const nodes = [];
const links = [];
const nodeMap = new Map();
// 收集所有唯一节点
rawData.forEach(item => {
if(!nodeMap.has(item.source)) {
nodeMap.set(item.source, {name: item.source});
}
if(!nodeMap.has(item.target)) {
nodeMap.set(item.target, {name: item.target});
}
});
// 转换为数组
nodes.push(...nodeMap.values());
// 创建连接
links.push(...rawData.map(item => ({
source: item.source,
target: item.target,
value: item.value
})));
return {nodes, links};
}
// 原始数据示例
const rawData = [
{source: 'A', target: 'B', value: 10},
{source: 'A', target: 'C', value: 20}
];
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn
上一篇:旭日图(Sunburst)实现