在数据可视化项目中,旭日图常用来表达具有明显层级关系的占比数据。官方默认配置会将全部层级一次性展开,当层级较深或分支较多时,画面会显得杂乱,同时也会增加渲染压力。通过监听用户的点击行为并动态控制子节点渲染,可以让界面更清爽,也提升操作体验。本文将围绕ECharts旭日图,说明如何实现点击父节点动态显示或隐藏子节点,并介绍相关数据结构、事件处理以及视觉优化技巧。

一、旭日图的数据结构与渲染依据
ECharts的旭日图数据是一个嵌套的树形结构,每个节点通过children字段包含下层节点。理解这个结构是后续动态控制的前提,因为隐藏或显示子节点本质上就是操作这些children相关状态。当数据被传入series后,ECharts会根据层级自动计算角度和半径,并完成绘制。
下面是一段基础数据示例,包含三个一级分类,每个分类下又有若干二级项。实际业务中,这些数据可能来自接口,但格式基本一致。只有清楚每一层对应的字段,点击事件触发后才能准确找到目标节点的子树。
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 }
]
}
];
从示例可以看出,每个叶子节点带有value字段,用于决定扇区大小;而父节点虽然也可以有value,但实际占比通常由子节点汇总而来。对于点击交互来说,我们更关注节点是否含有children,因为只有父节点才需要切换子节点显示状态。理解了这一点,后续的状态控制就有了明确的操作对象。
二、点击父节点切换子节点显隐的核心思路
要实现点击父节点切换子节点显隐,关键是借助ECharts实例的on方法监听click事件。事件回调中会返回当前点击的数据信息,其中params.data就是节点在数据树中的对象引用。我们可以在这个节点上挂载一个自定义状态,比如collapsed,用来表示当前是否折叠。与此同时,通过修改子节点的visible属性来控制渲染,ECharts会跳过visible为false的节点绘制,从而在视觉上实现隐藏。
当检测到父节点被点击时,遍历它的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);
}
});
这段代码的基本流程是:初始化图表、监听点击事件、修改节点状态和子节点可见性、重新设置数据。在简单层级下,它可以满足大部分需求,但如果子节点本身还有后代节点,还需要进一步递归处理,否则会出现部分层级残留的问题。数据驱动渲染是ECharts的核心机制,任何状态变化都要通过重新设置数据来触发重绘。
三、交互反馈与视觉优化
基础的显隐功能虽然可以实现,但用户点击后可能不清楚当前是展开还是折叠状态。我们可以在节点名称前加一个标记,或者在label格式化函数中根据collapsed显示不同文字。这样交互会更直观,也降低误操作概率。ECharts的label.formatter支持函数回调,回调参数中的info.data就是节点数据对象,我们可以根据它决定返回什么文本。例如在折叠时显示加号,展开时显示减号,用户一眼就能看出可点击的父节点状态。
另外,如果数据量很大,每次整体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 }
});
}
});
通过formatter在父节点名称前显示加号或减号,用户可以直观了解当前节点的折叠状态。根节点保护逻辑则避免了点击中心区域后整张图被隐藏的尴尬情况。实际开发中还可以配合tooltip或图例来增强提示效果,例如在鼠标悬停时显示该节点包含的子节点数量,让交互信息更加丰富。
四、复杂层级与常见注意事项
不少人在实现时发现点击没反应,通常是因为把visible写成了show,或者修改了数据却忘了调用setOption。旭日图依赖数据驱动渲染,任何结构变动都必须通知实例重绘,否则视图不会变化。此外,visible是ECharts内部约定的字段,不能随意替换为其他名称,否则ECharts无法识别该属性,节点仍然会被绘制出来。
还有一类问题是递归隐藏深层节点。上面的例子只处理了一级子节点,如果子节点本身也有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 }
});
}
});
递归函数setChildrenVisible会沿着children树逐层设置visible,确保所有后代节点与父节点状态保持一致。这种处理方式在层级较深时尤为重要,可以避免视觉上出现孤立的碎片节点。需要注意的是,在数据规模很大的情况下,频繁整体setOption可能造成短暂卡顿,此时可以考虑对数据进行浅拷贝、只更新变化部分,或者用dispatchAction触发局部更新,但核心思路仍然是保持数据状态与视图同步。此外,如果节点数据来自后端接口,每次点击后应该更新本地缓存数据,避免重新请求接口带来额外延迟。
综上所述,实现点击父节点动态显示或隐藏子节点的关键点包括:理解旭日图的树形数据结构、通过on('click')获取当前节点引用、使用collapsed记录折叠状态、用visible控制渲染,并在复杂层级中递归同步后代节点。配合label.formatter提供清晰视觉反馈,可以让交互更加友好。在实际项目中,还可以将折叠状态保存到后端或本地存储,刷新页面后恢复用户上次的浏览习惯,进一步提升体验。