jtwang7 / Project-Note

开发小记 - 项目里的一些新奇玩意儿
1 stars 0 forks source link

ECharts 的底层实现概述 #9

Open jtwang7 opened 2 years ago

jtwang7 commented 2 years ago

ECharts 的底层实现概述

参考文章:

ECharts 的声明式可视化设计

每一个Echarts组件,都会包含三种函数: ✅ visual-encoding(数据映射):实现数据驱动的可视化映射,将数据属性和图形属性进行绑定。 ✅ guide(描述信息):提供描述性的信息,如标签、辅助线等 ✅ interaction(数据交互):dataZoom, brushing

Echarts使用完全的单一JSON格式作为配置项来声明可视化,其使用Series来抽象一组图形元素,并将其和数据绑定。
Series可以被理解为是一个图表类型的实例,包含某个图表类的一些定制化的信息。
其他不同图表类通用的组件会被单独拆出来,如Legend,Tooltip等。
Echarts提供一个简单的SetOption方法来创建并更新图表组件,其内置基于键值的diff算法来比对多次Option的差异来进行针对的更新。

ECharts 架构

基于数据流的架构

Echarts内部存在很多复杂的状态,加之其需要对外开放各种能力,这些开放可能需要干预到中间状态(如交互)。这就导致状态管理会非常复杂,缺乏约束。由此,Echarts使用流数据架构,显示的约束计算关系,用户的交互行为只能干预流的上游(如源数据),后续的状态均会自动的响应上游的变化。

渐进式可视化

ECharts 在数据加载、更新或由于交互发生变化的时,整个可视化渲染的过程是渐进式的,而不是一蹴而就的。 对于数据源,会把完整的数据区块划分为多个小区块。每一个区块的数据独立进行布局、映射、渲染。

保证每一个区块都能尽可能在16ms内完成计算,从而防止浏览器的主线程卡顿。

每个区块计算任务完成后,会调用requestAnimationFrame优先绘制和展示当前区块内容,同时暂停接下来的任务,直到下一帧。 如果在这个过程中发生了交互行为,那么所有旧的任务都会被废弃,然后创建新的任务。这样,浏览器的主线程可以不被阻塞,用户可以一直和可视化进行互动,并即时的得到相应。

多线程渲染

当所有的计算任务都发生在主线程上时,Canvas必须等待计算任务完成后才可以绘制,同样的,所有的计算任务必须等待 Canvas 完成绘制后执行,因此整个过程将会以串联的形式执行。

process -> draw -> process -> draw -> ...

为了进一步提升性能,Echarts借助web worker的能力实现了多线程的渲染机制。 ECharts 在浏览器中开启一个计算线程 (Web Worker:专门处理复杂的计算,与 js 线程互不冲突)。 为了可以让Echarts运行在计算线程中,计算线程里会生成一个虚拟的canvas,虚拟canvas会记录所有计算任务最重触发的操作,如改变填充样式,绘制文本等。这些操作会被记录在一个Float32Array 命令数组中。当一个计算任务完成,这个指令数组会被通过postMessage传送到UI主线程。主线程中的canvas只需将最重需要执行的指令数组依次执行即可。与此同时,Worker线程并不需要等待主线程绘制完成,而是可以直接开启下次计算任务。