在数据可视化项目中,旭日图常用来表达具有明显层级关系的占比数据。但官方默认配置会将全部层级展开,当层级较深或分支较多时,画面会显得杂乱且影响性能。通过监听用户点击行为并动态控制子节点渲染,可以让界面更清爽,也提升操作体验。

一、旭日图基础数据结构
ECharts的旭日图数据是一个嵌套的树形结构,每个节点通过children字段包含下层节点。我们在初始化时,通常会准备类似下面的数据。理解这个结构是后续动态控制的前提,因为隐藏或显示子节点本质上就是操作这些children相关状态。
下面是一段基础数据示例,包含三个一级分类,每个分类下又有若干二级项。实际业务中,这些数据可能来自接口,但格式基本一致。只有清楚每一层对应的字段,点击事件触发后才能准确找到目标节点的子树。
var sunburstData = [
{
name: '前端',
children: [
{ name: 'JavaScript', value: 30 },
{ name: 'CSS', value: 20 },
{ name: 'HTML', value: 10 }
]
},
{
name: '后端',
children: [
{ name: 'Java', value: 25 },
{ name: 'Python', value: 15 }
]
},
{
name: '运维',
children: [
{ name: 'Linux', value: 12 },
{ name: 'Docker', value: 8 }
]
}
];
二、点击父节点隐藏子节点的实现思路
要实现点击父节点切换子节点显隐,关键是借助ECharts实例的on方法监听click事件。事件回调中会返回当前点击的数据信息,其中包含节点在数据树中的引用。我们可以在该节点上挂载一个自定义状态,比如collapsed,用来表示当前是否折叠。
当检测到父节点被点击时,遍历它的children,将每个子节点的visible属性置为相反值。随后调用setOption并传入新的series.data,ECharts就会重新渲染。需要注意的是,直接修改原数据并整体setOption比局部更新更不容易出错,特别在层级复杂时。
var chart = echarts.init(document.getElementById('main'));
function render(data) {
chart.setOption({
series: {
type: 'sunburst',
data: data,
radius: [0, '90%']
}
});
}
render(sunburstData);
chart.on('click', function(params) {
var node = params.data;
// 仅当有子节点时才处理
if (node.children && node.children.length > 0) {
node.collapsed = !node.collapsed;
node.children.forEach(function(child) {
// 通过visible控制渲染
child.visible = !node.collapsed;
});
render(sunburstData);
}
});
三、优化交互与视觉反馈
上面的代码已经能实现基本显隐,但用户点击后可能不清楚当前是展开还是折叠状态。我们可以在节点名称前加一个标记,或者在label格式化函数中根据collapsed显示不同文字。这样交互会更直观,也降低误操作概率。
另外,如果数据量很大,每次整体setOption会有一定开销。可以考虑在点击时只更新受影响的series,或者利用notMerge参数控制合并策略。下面示例展示了如何通过label回调给出折叠提示,并避免根节点被误藏。
chart.setOption({
series: {
type: 'sunburst',
data: sunburstData,
radius: [0, '90%'],
label: {
formatter: function(info) {
var d = info.data;
if (d.children && d.children.length > 0) {
return (d.collapsed ? '+' : '-') + d.name;
}
return d.name;
}
}
}
});
chart.on('click', function(params) {
var node = params.data;
// 根层级不隐藏,避免整图消失
if (!node.parent && !node._level) {
return;
}
if (node.children && node.children.length > 0) {
node.collapsed = !node.collapsed;
node.children.forEach(function(child) {
child.visible = !node.collapsed;
});
chart.setOption({
series: { data: sunburstData }
});
}
});
四、常见误区与注意事项
不少人在实现时发现点击没反应,通常是因为把visible写成了show,或者修改了数据却忘了调用setOption。旭日图依赖数据驱动渲染,任何结构变动都必须通知实例重绘,否则视图不会变化。
还有一类问题是递归隐藏深层节点。上面例子只处理了一级子节点,如果子节点本身也有children,需要用递归函数把所有后代的visible同步设置,否则会出现半隐藏的错乱状态。下面给出一个简单的递归工具函数,供复杂层级使用。
function setChildrenVisible(node, visible) {
if (!node.children) return;
node.children.forEach(function(child) {
child.visible = visible;
setChildrenVisible(child, visible);
});
}
chart.on('click', function(params) {
var node = params.data;
if (node.children && node.children.length > 0) {
node.collapsed = !node.collapsed;
setChildrenVisible(node, !node.collapsed);
chart.setOption({
series: { data: sunburstData }
});
}
});