熵简技术谈 | 大数据量场景下图表组件的设计与思考

导读:随着信息技术的发展,各种业务场景下数据量、数据维度极速增长,对于如何挖掘数据价值、找出数据之间关联的需求不断增加。BI(商业智能)平台通过用户拖拽等快捷交互,以可视化的方式实现多维度的数据整合和展示,从而辅助用户进行智能分析、业务决策。
本文以大数据场景下图表组件设计为研究对象,详细介绍了熵简科技在数据智能分析场景中,为实现复杂功能交互、大数据量下的高性能渲染、动态样式配置等需求,在图表组件设计及优化中的实践经验。
作者:本文出自熵简科技大前端团队,团队致力于打造世界级的B端产品。主要工作包括搭建先进、统一的前端基础架构,建立严格的产品设计规范,探索前沿技术的业务落地,持续沉淀优秀的前端案例,不断提升各个产品线的客户使用体验及开发效率。

一、背景
随着信息技术的发展,各种业务场景下数据量、数据维度极速增长,对于如何发掘数据的价值,找出数据之间关联的需求不断增加。BI(商业智能)平台通过拖拽,以可视化的方式将一个多维度展示需求通过转换合并,以表格的行或列的形式呈现。并将组合后的第一个维度作为图表轴,根据用户配置生成柱状、散点等图表。
熵简技术谈 | 大数据量场景下图表组件的设计与思考_第1张图片

根据可视化需求生成数据库查询,选择所需子集,经过 聚合、过滤、排序、表计算后,提供给页面展现[1]在我们的BI产品研发过程中,图表组件为其中重要的一环,需要考虑前端组件在大数据量、强交互的场景下,组合图表矩阵实现实时渲染。
熵简技术谈 | 大数据量场景下图表组件的设计与思考_第2张图片
熵简BI产品交互演示基于此需求,我们必须适配以下业务场景:
复杂的功能交互
大数据量
动态配置的样式及展示效果
较高的渲染性能要求
为此我们基于Konva实现一套图表组件,参考G/G2.JS、ZRender/Echarts、Charts.JS等图表及Canvas渲染框架的设计。比较研究了这些领先的图表组件中一些有意思的设计及优化方法,结合我们自身实践经验,在下面与大家分享。
二、渲染流程设计
首先我们来复习一下浏览器渲染流程,浏览器首先会获取、解析HTML文件生成DOMTree,解析CSS文件,生成CSSOMTree。经过Compose阶段,将DOM和CSSOM合并成RenderTree。最后RenderTree 经过回流(绘制骨架)及重汇(渲染样式)完成这整个页面的渲染。
熵简技术谈 | 大数据量场景下图表组件的设计与思考_第3张图片
浏览器渲染流程[2]我们的图表组件渲染过程也模拟了浏览器渲染流程,首先后端会将经过基础计算的原始数据返回过来,我们对基础数据结构进行业务处理,生成整个图表的骨架,类似 DOM Tree 结构。而后我们会根据对图表不同部分的样式的配置,生成每个部分的样式数据,相当于 CSS Tree。最后,会经过一系列数据处理管道将样式和图表基础数据 合并成预渲染数据 renderData。
熵简技术谈 | 大数据量场景下图表组件的设计与思考_第4张图片
三、实现数据处理与渲染的并行
图表渲染主要分为两个阶段:数据处理(DataProcess)和渲染(Render)。Process部分包括上文提到的对图表样式、基础数据及业务逻辑处理。Render部分调用Konva和Canvas原生API进行渲染。一开始渲染及重绘的过程是同步进行的。这样的绘制过程有大量的时间浪费(下次渲染需要等待数据处理),我们参考了ECharts对渲染过程的处理3,通过WebWorker实现计算和渲染过程并行处理,进一步优化图表响应速度:
熵简技术谈 | 大数据量场景下图表组件的设计与思考_第5张图片
四、事件机制优化
在开发图表框架的时候,对事件的处理是不可忽视的一环。由于在 Canvas 中绘制一个元素相当于使用 Immutable 模式 绘制一个 Bitmap —— 元素全部变成像素,没有元素的概念。既然没有原生的事件响应,常见的 Canvas 库中一般有两种元素事件的实现方式:
基于数学计算
基于元素颜色
4.1 基于数学计算
给出每一个元素的计算方法,基于对元素形状的数学计算,根据点击位置找到对应元素。例如在 Lavrton 的文章中4 识别一个圆形的例子:
熵简技术谈 | 大数据量场景下图表组件的设计与思考_第6张图片
4.2 基于颜色
我们可以从点击事件中获取点击位置像素的颜色,通过像素颜色可以找到对应元素,但如果有两个元素颜色一样怎么办?Konva中通过增加一层HitCanvas将正常的SceneCanvas上的元素按照原有位置摆放好,但是给每一个元素都设置了不同的颜色。点击的时候,通过点击事件获取到对应颜色,即可找到对应元素。
4.3 两种方式的比较
两种对事件处理的实现各有各的优缺点,在阿里的 G.js (G2、G6 的底层渲染框架) 是通过数学计算的方式实现。这种基于数学计算的模式对于元素的 拾取效率比较高[8]。但实现起来相对复杂,需要适配每一种图形,针对复杂模型,计算效率也不会很高。在 Konva 等框架中则是通过第二种方式,通过识别颜色,增加一层 HitCanvas 实现事件机制。这种模式的优点在于实现、理解起来简单,但效率较数学计算低,因为增加了一次渲染。这也是为什么 Konva 在官方文档的 优化方案 中提到,对于不需要事件响应的元素可以通过 listening(false) 不将其加入 HitCanvas 以提高渲染效率。

五、渐进式图表
对于渐进式图表的渲染,我们可以先回忆一下 React Fiber5 中对渲染流程的优化:React Fiber 将一次大的渲染任务拆分为一系列小的渲染任务,每一个任务可以在浏览器的一帧 16ms 中执行完毕,并将任务分为高优先级(用户事件等)和 低优先级任务(普通渲染任务)分别处理。
熵简技术谈 | 大数据量场景下图表组件的设计与思考_第7张图片
React Fiber [6]
在针对 Canvas 的图表中,我们也可以实现类似的机制以提高渲染性能,将一次大的图表渲染按照渲染元素的数量拆分为多个任务,要解决的问题与 React Fiber 一致 [6]:
分拆任务以保证每个任务能在1帧内执行完
防止对主进程占用太长时间影响页面动画、交互
可以动态对任务进行调整,暂停、删除、终止任务
对不同类型的任务区分高低优先级
在 G.js(分片渲染) 和 ECharts(渐进式图表) 中都实现类似的渲染机制分拆大数据量下的渲染任务,在 G.js 中对数据的分拆 与 局部渲染结合到了一起:
熵简技术谈 | 大数据量场景下图表组件的设计与思考_第8张图片
在 Echarts 中将大型任务拆分成一个个chunk,虽然整体渲染时间有所增加,但由于持续渲染避免了卡顿现象:
熵简技术谈 | 大数据量场景下图表组件的设计与思考_第9张图片
六、局部渲染
上文有提到 Canvas 是基于 Bitmap 的,其中没有元素概念,只有绘制上去的一个个像素点。即不能响应单个元素事件,也不能针对单个元素做删除和修改。但如果一次简单改动的就需要对整个画布进行重绘,性能开销巨大。在 G.js 对局部渲染的处理 中,通过一些 Canvas 局部处理的 API,我们可以实现在某一区域内进行擦除和重新绘制。
ctx.clearRect(x, y, width, height) 擦除区域内的像素点
ctx.fillRect(x, y, width, height) 对执行区域进行绘制
G. js 局部渲染的实现:
熵简技术谈 | 大数据量场景下图表组件的设计与思考_第10张图片
6.1 渲染可视区域
对于大型图表,有很大一部分元素是在滚动条之外不可见的,如果我们一口气把全部图表都渲染出来,消耗大量性能的同时做了很多无用的工作,所以我们将图表的原生滚动条禁用,手动绘制了滚动条,并在 Canvas 中只渲染当前可视部分的部分,在滚动的时候动态对其他部分图形进行渲染。
熵简技术谈 | 大数据量场景下图表组件的设计与思考_第11张图片
更多 Canvas 优化技巧
使用 Canvas 预渲染与离屏渲染,对元素进行缓存
批量渲染 Canvas 元素
避免非必要的 Canvas 状态改变
只重新渲染不同部分,而不是整个 Canvas
避免使用 Shadow
避免使用小数,以防止 Canvas 的锯齿消除操作

七、总结
上面介绍了在图表组件设计的过程中很多有意思的优化点。但同时我们也应该保持对优化的警惕,过度的优化并不会提高渲染性能,反而可能导致其他问题。同时在复杂的业务场景中保持代码可读性也是我们考虑的关键,良好的代码结构是保证框架、业务需求长期稳定的迭代的前提。我们之所以需要图表,其核心在于通过不同形式的组合发掘数据的价值,使用数据驱动业务的变革。上文提到的通过将多维度数据以组合的形式展示在表格的行和列之中。其本质在于将多维度数据降维展现在二维平面上,如果我们能在此组合的基础上基于 WebGL 提供基于三维空间的数据展示,一定能更有效的展现出信息之间的关联关系。提供更好的展示效果,任重而道远。

参考文献
[1] A System for Query, Analysis, and Visualization of Multidimensional Relational Databases 2002
[2] 手把手教你打造一款轻量级canvas渲染引擎
[3] A declarative framework for rapid construction of web-based visualization
[4] Hit Region Detection For HTML5 Canvas And How To Listen To Click Events On Canvas Shapes.
[5] The how and why on React’s usage of linked list in Fiber to walk the component’s tree.
[6] Lin Clark - A Cartoon Intro to Fiber - React Conf 2017.
[7] making-a-silky-smooth-web.
[8] G 渲染改造.

你可能感兴趣的:(熵简技术谈 | 大数据量场景下图表组件的设计与思考)