1. 为什么需要自定义Loading动画?
在大屏数据可视化项目中,我们经常会遇到多个图表同时加载的情况。由于数据来源不同、接口响应速度差异,某些图表可能需要较长时间才能完成数据获取和渲染。这时候如果没有任何提示,用户可能会误以为页面卡顿或者功能失效,体验非常糟糕。
我去年参与过一个智慧城市大屏项目,里面包含12个实时更新的ECharts图表。测试阶段就有用户反馈说"有些图表偶尔会空白几秒钟"。后来我们加入了自定义Loading动画,用户等待时的焦虑感明显降低,项目验收时获得了客户的高度评价。
ECharts自带的showLoading/hideLoading API就是为解决这类问题而生的。但很多人只是简单调用默认效果,其实这个功能远比想象中强大。通过合理配置,你可以实现:
- 品牌视觉统一的加载动画
- 不同数据类型的差异化提示
- 多图表加载的进度反馈
- 网络异常时的友好提示
2. 基础配置与核心参数解析
2.1 快速上手基础用法
先来看最基本的实现方式,这也是大多数开发者最先接触的写法:
const chart = echarts.init(document.getElementById('chart')); chart.showLoading(); // 模拟数据加载 fetch('/api/data').then(data => { chart.setOption(data); chart.hideLoading(); });这种最简单的调用会显示ECharts默认的旋转动画和"loading"文字。但实际项目中,我们通常需要更精细的控制。showLoading方法接受两个参数:
- type:目前仅支持'default'类型
- opts:配置对象,这才是真正的重点
2.2 核心配置参数详解
opts对象支持以下关键属性(以v5.4.0版本为例):
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| text | string | 'loading' | 显示的文字内容 |
| color | string | '#c23531' | 旋转动画颜色 |
| textColor | string | '#000' | 文字颜色 |
| maskColor | string | 'rgba(255,255,255,0.8)' | 遮罩层颜色 |
| zlevel | number | 0 | 图形层级 |
| fontSize | number | 12 | 文字大小 |
| showSpinner | boolean | true | 是否显示旋转动画 |
| spinnerRadius | number | 10 | 旋转动画半径 |
| lineWidth | number | 5 | 旋转动画线宽 |
| fontWeight | string | 'normal' | 文字粗细 |
| fontStyle | string | 'normal' | 文字样式 |
| fontFamily | string | 'sans-serif' | 字体 |
实际项目中我常用的一个配置示例:
chart.showLoading({ text: '数据加载中...', color: '#1890ff', textColor: '#666', maskColor: 'rgba(0, 0, 0, 0.1)', fontSize: 14, spinnerRadius: 12, lineWidth: 4 });3. 高级定制技巧
3.1 多图表协同加载方案
在大屏项目中,经常需要处理多个图表的加载状态。这里分享一个实用的封装方法:
class ChartLoader { constructor(charts) { this.charts = Array.isArray(charts) ? charts : [charts]; this.loadingCount = 0; } show(text) { this.loadingCount++; this.charts.forEach(chart => { chart.showLoading({ text: text || `加载中 (${this.loadingCount}/${this.charts.length})`, maskColor: 'rgba(0, 0, 0, 0.05)' }); }); } hide() { if (--this.loadingCount <= 0) { this.charts.forEach(chart => chart.hideLoading()); this.loadingCount = 0; } } } // 使用示例 const chart1 = echarts.init(document.getElementById('chart1')); const chart2 = echarts.init(document.getElementById('chart2')); const loader = new ChartLoader([chart1, chart2]); // 开始加载 loader.show('数据加载中...'); // 某个图表加载完成 fetch('/api/data1').then(data => { chart1.setOption(data); loader.hide(); }); // 另一个图表加载完成 fetch('/api/data2').then(data => { chart2.setOption(data); loader.hide(); });这个方案实现了:
- 统一的加载状态管理
- 进度提示功能
- 自动化的状态同步
- 防止过早隐藏loading
3.2 自定义SVG动画
虽然ECharts官方只提供了默认的旋转动画,但我们可以通过一些技巧实现自定义SVG动画。这里分享一个实战中验证过的方案:
function showCustomLoading(chart, options = {}) { // 先隐藏默认loading chart.hideLoading(); // 创建自定义容器 const container = chart.getDom(); const loader = document.createElement('div'); loader.className = 'custom-loader'; loader.innerHTML = ` <svg viewBox="0 0 50 50"> <circle cx="25" cy="25" r="20" fill="none" stroke="${options.color || '#1890ff'}" stroke-width="4"></circle> </svg> <div class="loader-text">${options.text || '加载中...'}</div> `; // 添加样式 const style = document.createElement('style'); style.textContent = ` .custom-loader { position: absolute; top: 0; left: 0; right: 0; bottom: 0; display: flex; flex-direction: column; align-items: center; justify-content: center; background: ${options.maskColor || 'rgba(255,255,255,0.7)'}; z-index: 1000; } .custom-loader svg { width: 50px; height: 50px; animation: rotate 2s linear infinite; } .custom-loader circle { stroke-dasharray: 90, 150; stroke-dashoffset: 0; animation: dash 1.5s ease-in-out infinite; } .custom-loader .loader-text { margin-top: 10px; color: ${options.textColor || '#666'}; font-size: ${options.fontSize || 14}px; } @keyframes rotate { 100% { transform: rotate(360deg); } } @keyframes dash { 0% { stroke-dasharray: 1, 150; stroke-dashoffset: 0; } 50% { stroke-dasharray: 90, 150; stroke-dashoffset: -35; } 100% { stroke-dasharray: 90, 150; stroke-dashoffset: -124; } } `; container.appendChild(style); container.appendChild(loader); // 返回隐藏方法 return () => { container.removeChild(loader); container.removeChild(style); }; } // 使用示例 const chart = echarts.init(document.getElementById('chart')); const hideLoading = showCustomLoading(chart, { text: '正在获取最新数据', color: '#ff4d4f' }); // 数据加载完成后 fetch('/api/data').then(data => { chart.setOption(data); hideLoading(); });4. 性能优化与常见问题
4.1 性能优化建议
遮罩层优化:maskColor使用rgba时,alpha值不宜过大。实测发现当设置为0.8以上时,在某些低端设备上会出现渲染性能问题。推荐使用0.1-0.3之间的透明度。
动画性能:如果自定义CSS动画,建议:
- 使用transform和opacity属性(硬件加速)
- 避免使用box-shadow等耗性能的属性
- 简化动画复杂度
内存管理:SPA项目中,一定要在组件销毁时移除loading:
// Vue示例 beforeDestroy() { this.chart && this.chart.hideLoading(); this.chart.dispose(); }4.2 常见问题解决方案
问题1:调用hideLoading后动画仍然可见原因:通常是因为在setOption之前调用了hideLoading解决:
// 正确顺序 chart.setOption(data); chart.hideLoading(); // 错误顺序 chart.hideLoading(); chart.setOption(data);问题2:移动端显示模糊原因:旋转动画使用了非整数像素的lineWidth解决:
chart.showLoading({ lineWidth: Math.floor(devicePixelRatio) // 根据设备像素比调整 });问题3:多图表加载不同步解决:使用第3.1节的ChartLoader方案,或者考虑Promise.all:
Promise.all([ fetch('/api/data1'), fetch('/api/data2') ]).then(([data1, data2]) => { chart1.setOption(data1); chart2.setOption(data2); chart1.hideLoading(); chart2.hideLoading(); });