5分钟掌握Echarts旭日图从零到专业的层级数据可视化实战指南当我们需要展示具有明确层级关系的数据时传统饼图或树状图往往显得力不从心。想象一下这样的场景你需要向管理层展示公司各部门的人员构成从总部到区域再到具体团队同时还要体现各层级的占比关系。这时Echarts的旭日图(Sunburst Chart)就能完美解决这个需求——它像一棵从中心向外生长的树每一圈环代表一个数据层级通过扇形面积直观呈现数据占比。旭日图的核心优势在于它能同时展示层级结构和比例关系。内环代表顶层分类外环则是更细分的子类每个扇区的大小对应其数值大小。这种可视化方式特别适合展示组织架构、销售分类、用户行为路径等具有自然层级的数据。与树状图相比旭日图更强调部分与整体之间的关系与多层饼图相比它的空间利用率更高能清晰展示3-4层的数据关系。1. 快速搭建你的第一个旭日图1.1 基础HTML框架搭建让我们从最基础的HTML结构开始。创建一个新的HTML文件加入以下代码!DOCTYPE html html head meta charsetutf-8 title我的第一个旭日图/title script srchttps://cdn.jsdelivr.net/npm/echarts5.4.3/dist/echarts.min.js/script /head body div idsunburstChart stylewidth:800px;height:800px;/div /body /html这里我们通过CDN引入了Echarts库并创建了一个800×800像素的正方形容器。旭日图建议使用正方形容器因为环形结构在非正方形容器中会变形。1.2 基础数据结构和配置接下来在body标签结束前添加script标签初始化图表并设置基本配置script // 初始化图表实例 const chart echarts.init(document.getElementById(sunburstChart)); // 准备数据 - 以公司部门结构为例 const data [{ name: 总公司, children: [ { name: 技术部, children: [ {name: 前端组, value: 15}, {name: 后端组, value: 20}, {name: 测试组, value: 10} ] }, { name: 市场部, children: [ {name: 数字营销, value: 12}, {name: 品牌推广, value: 8} ] } ] }]; // 基础配置项 const option { series: [{ type: sunburst, data: data, radius: [0, 90%], label: {show: true} }] }; // 渲染图表 chart.setOption(option); /script这个最简单的例子已经包含了旭日图的核心元素type: sunburst指定图表类型data采用嵌套结构父节点包含children叶子节点包含valueradius控制图表的内外半径1.3 实时预览与调试技巧在浏览器中打开这个HTML文件你应该能看到一个基本的旭日图。为了更方便调试推荐以下做法使用Chrome开发者工具(F12)的Console面板可以直接修改option对象chart.setOption({series: [{radius: [0, 80%]}]}, true);添加窗口大小变化时的自适应window.addEventListener(resize, function() { chart.resize(); });使用Echarts的getOption()方法检查当前配置console.log(chart.getOption());提示在开发过程中可以先用少量数据测试基本结构确认无误后再添加完整数据。2. 数据格式深度解析与转换技巧2.1 旭日图的标准数据结构旭日图要求数据采用特定的嵌套格式。一个标准的节点对象可以包含以下属性{ name: 节点名称, // 必填 value: 123, // 叶子节点必填 children: [ // 非叶子节点必填 // 子节点数组 ], itemStyle: { // 可选自定义样式 color: #FF0000 }, label: { // 可选自定义标签 show: true, color: #FFF } }关键规则每个节点必须有name属性叶子节点(没有子节点的节点)必须有value属性非叶子节点必须有children数组(即使为空)同一层级不能混用value和children2.2 常见数据转换场景实际工作中我们的原始数据往往不是这种嵌套格式。以下是几种常见转换场景场景1扁平表格转嵌套结构假设有扁平数据const flatData [ {category: 家电, subcategory: 大家电, product: 冰箱, sales: 500}, {category: 家电, subcategory: 大家电, product: 空调, sales: 800}, {category: 服饰, subcategory: 男装, product: 衬衫, sales: 300} ];转换函数function flatToHierarchy(data) { const root {name: root, children: []}; const categoryMap {}; data.forEach(item { // 处理一级分类 if (!categoryMap[item.category]) { const categoryNode {name: item.category, children: []}; categoryMap[item.category] categoryNode; root.children.push(categoryNode); } // 处理二级分类 let subcategoryNode categoryMap[item.category].children .find(child child.name item.subcategory); if (!subcategoryNode) { subcategoryNode {name: item.subcategory, children: []}; categoryMap[item.category].children.push(subcategoryNode); } // 添加产品节点 subcategoryNode.children.push({ name: item.product, value: item.sales }); }); return root.children; }场景2后端API数据适配后端返回的数据可能需要简单转换// 原始API数据 const apiData { department: 技术部, headcount: 45, teams: [ { teamName: 前端组, members: 15, skills: [React, Vue] }, // 其他团队... ] }; // 转换为旭日图格式 const sunburstData { name: apiData.department, children: apiData.teams.map(team ({ name: team.teamName, value: team.members, // 可以保留原始数据中的其他字段 meta: { skills: team.skills } })) };2.3 大数据量优化策略当数据量较大时(如超过1000个节点)需要考虑性能优化懒加载初始只加载顶层数据点击时再加载子级series: [{ type: sunburst, // ...其他配置 nodeClick: expandAndCollapse, data: loadInitialData(), universalTransition: true }] // 点击时加载数据 chart.on(click, function(params) { if (params.treePathInfo.length 3) { // 只加载到第3层 const newData loadChildrenData(params.name); chart.setOption({ series: [{ data: updateData(params, newData) }] }); } });数据聚合对底层小节点进行合并function aggregateSmallNodes(data, threshold) { return data.map(node { if (node.children) { const total node.children.reduce((sum, child) sum (child.value || 0), 0); if (total threshold) { return { name: node.name, value: total }; } return { ...node, children: aggregateSmallNodes(node.children, threshold) }; } return node; }); }使用level参数控制渲染深度series: [{ type: sunburst, // ...其他配置 levels: [ {r0: 0%, r: 30%}, // 第一层 {r0: 30%, r: 60%}, // 第二层 {r0: 60%, r: 90%} // 第三层 ] }]3. 专业级样式定制技巧3.1 层级化配色方案专业的旭日图应该通过颜色直观区分不同层级。以下是几种实用的配色策略按层级渐变配色itemStyle: { color: function(params) { // 定义各层级的颜色 const levelColors [ #2c3e50, // 第一层 #3498db, // 第二层 #1abc9c, // 第三层 #f1c40f // 第四层 ]; return levelColors[Math.min(params.level, levelColors.length - 1)]; } }按类别配色// 先定义类别到颜色的映射 const categoryColors { 技术部: #3498db, 市场部: #e74c3c, 财务部: #2ecc71 }; itemStyle: { color: function(params) { // 获取当前节点的顶层分类 const topLevelName params.treePathInfo[0]?.name || 其他; return categoryColors[topLevelName] || #95a5a6; } }数值大小配色itemStyle: { color: function(params) { // 根据value值计算颜色深浅 if (!params.value) return #eee; const maxValue 1000; // 根据你的数据调整 const ratio Math.min(params.value / maxValue, 1); const hue 200; // 蓝色系 const saturation 80; const lightness 100 - ratio * 60; return hsl(${hue}, ${saturation}%, ${lightness}%); } }3.2 标签优化与防重叠旭日图的标签重叠是常见问题以下是解决方案基础标签配置label: { show: true, rotate: radial, // 标签沿径向排列 fontSize: 12, color: #333, formatter: function(params) { // 只显示最外层标签 return params.treePathInfo.length 3 ? params.name : ; } }智能标签位置调整label: { position: function(params) { // 根据扇区角度决定标签位置 const startAngle params.startAngle; const endAngle params.endAngle; const midAngle (startAngle endAngle) / 2; // 右侧区域标签放外部左侧放内部 return midAngle 90 midAngle 270 ? inside : outside; }, align: center, padding: [5, 5] }使用视觉引导线label: { position: outside, alignTo: labelLine, edgeDistance: 10%, bleedMargin: 5 }, labelLine: { show: true, length: 10, length2: 15, smooth: true }3.3 交互增强设计优秀的交互设计能显著提升旭日图的用户体验高亮与动画效果emphasis: { focus: ancestor, itemStyle: { shadowBlur: 10, shadowColor: rgba(0, 0, 0, 0.5) }, label: { show: true, fontSize: 14, fontWeight: bold } }, blur: { itemStyle: { opacity: 0.3 } }, selectedMode: single, select: { itemStyle: { borderColor: #000, borderWidth: 2 } }点击展开/收缩series: [{ // ...其他配置 nodeClick: expandAndCollapse, expandAndCollapse: true, initialTreeDepth: 2 // 初始展开到第2层 }]自定义tooltip内容tooltip: { trigger: item, formatter: function(params) { const path params.treePathInfo.map(item item.name).join( ); const percentage (params.percent || 0).toFixed(1); return div stylefont-weight:bold;margin-bottom:5px${path}/div div数值: ${params.value || 0}/div div占比: ${percentage}%/div ${params.data.meta ? div描述: ${params.data.meta.desc}/div : } ; } }4. 高级应用场景与性能优化4.1 动态数据更新旭日图支持平滑的动态数据更新这在实际业务中非常有用基础更新方法// 获取新数据 const newData fetchNewData(); // 更新图表 chart.setOption({ series: [{ data: newData }] });带过渡动画的更新chart.setOption({ series: [{ type: sunburst, data: newData, universalTransition: true, animationDurationUpdate: 1000, animationEasingUpdate: cubicInOut }] });增量更新策略function updatePartialData(nodeName, newChildren) { const currentOption chart.getOption(); const currentData currentOption.series[0].data; // 查找要更新的节点 function findNode(data, name) { for (let i 0; i data.length; i) { if (data[i].name name) return data[i]; if (data[i].children) { const found findNode(data[i].children, name); if (found) return found; } } return null; } const nodeToUpdate findNode(currentData, nodeName); if (nodeToUpdate) { nodeToUpdate.children newChildren; chart.setOption({ series: [{ data: currentData }] }); } }4.2 大数据量性能优化当数据量达到数千节点时需要特别考虑性能渲染优化配置series: [{ type: sunburst, // ...其他配置 progressive: 10, // 渐进式渲染 progressiveThreshold: 500, // 超过500节点启用渐进渲染 animation: false, // 大数据时关闭动画 silent: true, // 静默模式提升性能 levels: [ {r0: 0%, r: 30%, itemStyle: {borderWidth: 0}}, {r0: 30%, r: 60%, itemStyle: {borderWidth: 0}}, {r0: 60%, r: 90%, itemStyle: {borderWidth: 0}} ] }]Web Worker处理数据// 主线程 const worker new Worker(dataProcessor.js); worker.postMessage({action: process, data: rawData}); worker.onmessage function(e) { if (e.data.action processed) { chart.setOption({ series: [{ data: e.data.result }] }); } }; // dataProcessor.js (Web Worker) self.onmessage function(e) { if (e.data.action process) { const result processComplexData(e.data.data); self.postMessage({ action: processed, result: result }); } }; function processComplexData(data) { // 复杂的数据处理逻辑 return processedData; }虚拟渲染技术series: [{ type: sunburst, // ...其他配置 renderLabelForZeroData: false, hoverLayerThreshold: 1000, useUTC: true, large: true, // 启用大数据模式 availableTransformTypes: [plain] }]4.3 与其他图表联动旭日图可以与其他Echarts图表联动创造更丰富的分析体验基础联动示例// 创建两个图表实例 const sunburstChart echarts.init(document.getElementById(sunburst)); const barChart echarts.init(document.getElementById(bar)); // 设置联动 echarts.connect([sunburstChart, barChart]); // 旭日图点击事件 sunburstChart.on(click, function(params) { // 根据点击的节点更新柱状图数据 const barData generateBarData(params); barChart.setOption({ series: [{ data: barData }] }); });跨图表高亮// 在旭日图配置中 emphasis: { focus: self, blurScope: coordinateSystem } // 在柱状图配置中 emphasis: { focus: series }共享tooltiptooltip: { trigger: item, axisPointer: { type: shadow, link: {xAxisIndex: all} } }5. 企业级应用案例解析5.1 组织架构可视化数据结构设计{ name: CEO, title: 首席执行官, value: 1, children: [ { name: CTO, department: 技术部, children: [ { name: 产品研发, team: A组, children: [ {name: 张伟, position: 高级工程师, value: 1}, // 其他成员... ] }, // 其他团队... ] }, // 其他部门... ] }特色配置label: { formatter: function(params) { const node params.data; if (params.level 0) { return {title|${node.name}}\n{position|${node.title}}; } if (params.level 1) { return {dept|${node.name}}\n{position|${node.department}}; } return node.name; }, rich: { title: {fontSize: 18, color: #333, fontWeight: bold}, dept: {fontSize: 14, color: #666}, position: {fontSize: 12, color: #999} } }5.2 销售渠道分析数据结构示例{ name: 总销售额, value: 1000000, children: [ { name: 线上渠道, children: [ { name: 电商平台, children: [ {name: 天猫, value: 300000}, {name: 京东, value: 250000} ] }, // 其他线上渠道... ] }, // 其他渠道... ] }特色分析功能tooltip: { formatter: function(params) { const path params.treePathInfo.map(item item.name).join( ); const value params.value.toLocaleString(); const percentage params.percent.toFixed(1); const growth params.data.growth ? 同比增长: ${params.data.growth}% : ; return div stylemargin-bottom:5pxb${path}/b/div div销售额: ¥${value}/div div占比: ${percentage}%/div ${growth} ; } }5.3 用户行为路径分析数据结构设计{ name: 访问来源, children: [ { name: 搜索引擎, children: [ { name: 百度, children: [ {name: 品牌词, value: 5000}, {name: 通用词, value: 3000} ] }, // 其他搜索引擎... ] }, // 其他来源... ] }路径分析特色series: [{ type: sunburst, // ...其他配置 levels: [ { r0: 0%, r: 20%, label: {rotate: tangential} }, { r0: 20%, r: 45%, label: {align: right} }, { r0: 45%, r: 70%, label: {align: left} }, { r0: 70%, r: 90%, label: {position: outside} } ], itemStyle: { borderWidth: 2, borderColor: #fff } }]在实际项目中我发现旭日图最实用的功能是能够直观展示各层级的占比关系特别是在向非技术人员解释复杂数据结构时。例如在分析销售渠道时内环可以显示线上/线下渠道的总体比例外环则展示具体平台的销售情况这种可视化方式比表格数据更容易获得业务方的认可。