Echarts折线图渲染优化方案
本章节介绍基于echarts折线图的渲染优化
文章目录
- 系列文章目录
- 前言
- 一、前端优化方案
- 1.Echarts配置
- 1.1 sampling的配置
- 1.2 dataZoom 配置
- 1.3 progressive分批渲染
- 1.4 关闭动画(不抽点,但会减少重绘和交互开销配置项)
- 2.数据处理
- 二、与后端协同优化方案
前言
根据业务需要,后端一次性返回前端所有数据,在X坐标轴展示时间,Y轴坐标展示对应的值;
后端取24小时的数据,前端按照秒级单位来描点连线;24 小时按秒连线大约是 86400 个点。图宽只有大约 1000 像素,每一列像素上会叠几十个点,高低值连在一起就会糊成一块面,这是点数和像素不匹配,导致UI展示不友好,页面鼠标悬浮等动作会延迟卡顿;
技术栈:vue2.6.10版本, echrts5.4.0版本
一、前端优化方案
1. 配置首次渲染稀疏点,让折线图基本走势正常展示;
2. 配置缩放dataZoom,允许当窗口拉大后展示更多的点;
3.处理x轴的坐标,把字符串类型的时间改为date类型;
1.Eharts配置
1.1开启 LTTB 降采样
sampling: 'lttb'会智能保留曲线的“形状特征”(如峰值、谷值、拐点),过滤掉大量冗余的直线段点, 视觉上趋势不变,性能大幅提升;同时记得加上symbol: 'none',避免为每个数据点绘制标记,这也能省下不少渲染开销;
1.2 dataZoom配置
不要让图表一开始就渲染全部时间范围。结合dataZoom设定初始只展示一个较窄的时间窗口;
dataZoom: [ { type: 'inside', start: 0, end: 10 }, { type: 'inside', start: 0, end: 10 } ],1.3 progressive分批渲染
可以让渲染过程更平滑
progressiveThreshold: 3000:点数达到这个值才分帧progressive: 400:每一帧画多少个点progressiveChunkMode:柱状默认'mod'
生效条件:这条 series 的视图实现了incrementalPrepareRender,并且progressive为真、点数不少于阈值。每一帧画progressive个点,画完再画下一帧,避免一次布局卡死主线程;
1.4 不抽样展示,但会减少重绘和交互开销
animationThreshold: 2000(根配置)。单条 series 点数超过它,动画自动关掉。hoverLayerThreshold: 3000(根配置,真正被读取的是这一项)。图形数量超过它,高亮改到单独的 hover 层,避免整图重画。echarts.init(dom, null, { useDirtyRect: true })。只重画变化区域。默认关闭。拖动 dataZoom、十字准星时收益大;整图setOption替换 series 时收益小。setOption(option, { lazyUpdate: true })。把本次更新推迟到下一帧,连续触发时合并成一次渲染。silent: true。这条 series 不响应鼠标,省掉命中检测。showSymbol: false、showAllSymbol。不创建每个点的 symbol。8 万个圆点比折线本身更贵。clip: true。画出坐标范围的部分裁掉,少画可视区域外的路径。blendMode。默认source-over。改成别的混合模式会关掉一些 canvas 加速。- dataZoom 的
throttle。拖动时限制datazoom事件频率,默认约 100ms 量级。realtime: false则松手后才更新图表。
注意:large:true的配置用于散点图、柱状图、路径线图(lines)上,折线视图不生效
2.数据处理
// 删除 xAxisData 相关代码
const start = Date.parse(result.errorTimeS); // 起始时间戳
result?.signals?.forEach((item) => {
const key = this.switchSignalType(item.signalCode, item.signalType);
// 把纯数值数组 改为 [[timestamp, value], ...] 二维数组
seriesData[key] = item.values.map((v, i) => [start + i * 1000, v]);
});
// 调用时不再传 xAxisData
this.initChart(index, seriesData);
原数据格式为字符串数组:[v1, v2, v3, ...]
改后时间戳的二维数组:[[ts1,v1], [ts2,v2], [ts3,v3], ...]
echarts会根据像素密度匹配格式,原来类目配置为:
xAxis: {
type: "category", // category axis, each data point is an independent category
boundaryGap: false,
data: xAxisData, // passes in the pre-formatted time string array
axisLabel: {
formatter: function (value) {
return value.substring(11, 19); // manually extract HH:mm:ss
},
},
}
把type改为:“time”, 优势对比如下:
| 操作 | 改前 | 改后 |
|---|---|---|
moment().format()调用次数 | ~80,000 次 | 0 次 |
| xAxis.data 数组 | 80,000 个字符串 | 无(省内存) |
| 刻度格式化 | 手动 substring | ECharts 内部按需计算(只格式化屏幕可见的十几个标签) |
| dataZoom 缩放时的刻度更新 | 需重新计算 | 自动处理 |
二、与后端协同优化方案
初始加载:前端请求“最近 1 小时”数据,后端按秒级返回(假设 3600 个点)。
前端渲染:ECharts 开启
sampling: 'lttb',symbol: 'none',animation: false,progressive: 2000。将绘制点数可能被降到 1000 左右,流畅。用户缩放:用户将 DataZoom 拖拽到“最近 24 小时”。
增量请求:前端监听到
dataZoom事件,根据新的时间范围向后端请求这 24 小时的数据。后端此时可以按分钟聚合返回(约 1440 个点),而不是秒级。更新图表:前端用新数据
setOption,由于数据量依然可控,且已经关闭了动画,更新过程不会卡顿。
与后端协同方案实现思路:让折现趋势展示完整,当请求为24h的数据时,前端渲染的为小时或分钟级别的描点,只有缩放到了近20分钟内按秒级展示;