前面我们介绍了复杂渲染引擎中,使用的收集和渲染、以及插件等架构设计。至于底层具体的绘制实现,前面提到的多是 Canvas,实际上我们还可以适配不同的绘制引擎。
# 多渲染方式适配
关于渲染引擎整体架构和插件架构的设计,已在《收集与渲染》、《插件的实现》两篇文章中介绍过,渲染引擎架构如图:
底层渲染引擎由收集器和渲染器组成,其中收集器收集需要渲染的渲染数据,渲染器则负责将收集到的数据进行直接渲染。
本文我们将会介绍渲染器的多种渲染方式的适配,其中常见的就包括:
- Canvas 渲染
- SVG 渲染
- DOM 渲染
- 其他渲染方式(如 WEBGL 渲染等)
# 适配架构设计
对于多种渲染方式的适配,架构设计上还比较简单:
从收集器收集到的数据,通过适配的方式,转换成不同的绘制结果。举个例子,同样是一个单元格内容:
- Canvas 将需要分别绘制单元格背景(矩形)、单元格边框(线段)、单元格内容(文本)
- SVG 与 Canvas 相似,但 SVG 需要注意元素的层级关系,组合成单元格
- DOM 则可以通过一个
<div>
元素或是<table>/<tr>/<td>
等表格元素来绘制
一般来说,我们如果使用多种渲染方式,还需要考虑渲染一致性。渲染一致性是指,使用 Canvas 绘制的结果,需要与 SVG、DOM 绘制渲染的结果保持一致,不能出现太大的跳动或是位置、样式不一致的结果。
因此,我们在进行渲染的时候,根据选择的渲染方式,还需要做不同的兼容适配。
以上几种渲染方式中,DOM 渲染会受浏览器自身的排版引擎影响,这种影响可能是正面的,也可能是负面的。比如,我们 Canvas 排版方式是尽量接近浏览器原生的方式,那么当我们适配 DOM 渲染的时候则比较省力气。但如果说像在线表格这种场景,使用 DOM 进行表格的排版,则可能会遇到比较多的问题。
举个例子,我们都知道 DOM 里的表格元素(<table>
/<tr>
/<td>
等)是最难驾驭的,因为浏览器对它们的处理总是在意料之外,宽高难以控制意味着我们将很难将其与 Canvas/SVG 的渲染效果对齐。因此,我们很可能需要在表格元素里嵌套绝对定位的<div>
元素,来使得表格最终渲染不会被轻易撑开导致偏差。
除此之外,我们还需要注意文字的排版、换行等情况在 Canvas/SVG 和 DOM 渲染中需要尽量保持一致。
# 各种渲染方式的选择
每种渲染方式都有各自的优缺点。
在图表渲染引擎中,最常见的是 Canvas 渲染和 SVG 渲染,我们也可以从 ECharts 官网中找到两者的对比描述:
- 一般来说,Canvas 更适合绘制图形元素数量较多(这一般是由数据量大导致)的图表(如热力图、地理坐标系或平行坐标系上的大规模线图或散点图等),也利于实现某些视觉特效。
- 但在不少场景中,SVG 具有重要的优势:它的内存占用更低(这对移动端尤其重要)、并且用户使用浏览器内置的缩放功能时不会模糊。
选择哪种渲染器,可以根据软硬件环境、数据量、功能需求综合考虑:
- 在软硬件环境较好,数据量不大的场景下,两种渲染器都可以适用,并不需要太多纠结
- 在环境较差,出现性能问题需要优化的场景下,可以通过试验来确定使用哪种渲染器。比如:
- 在需要创建很多 ECharts 实例且浏览器易崩溃的情况下(可能是因为 Canvas 数量多导致内存占用超出手机承受能力),可以使用 SVG 渲染器来进行改善
- 如果图表运行在低端安卓机,或者我们在使用一些特定图表如水球图等,SVG 渲染器可能效果更好
- 数据量较大(经验判断 > 1k)、较多交互时,建议选择 Canvas 渲染器
而在在线表格的场景,我们会发现不同的团队会选择不同的渲染方式:
- 谷歌表格使用了 Canvas/DOM 两种渲染方式,其中 DOM 渲染主要用于首屏直出
- 金山表格使用了 Canvas/SVG 两种渲染方式,其中 SVG 渲染主要用于首屏直出
- 飞书表格使用了 Canvas 渲染
其实我们可以发现,这些团队很多在使用几种渲染方式,原因几乎都是因为使用了 Canvas 绘制作为主要渲染方式。但考虑到首屏渲染的情况,Canvas 则需要一系列的数据计算和渲染过程,不适合首屏直出的方式,因此会适配上 DOM 或者 SVG 进行首屏直出。
实际上,Canvas 渲染有一个比较致命的弱点:交互性很差。比如用户选择某个格子,进行拖拽、调整宽高、右键菜单等操作,在 Canvas 上是很难命中具体的元素的。因为 Canvas 绘制过程中并不像 DOM 和 SVG 一样有层次结构,最终的渲染结果也只是一个图像。因此,在线表格场景下大多数 Canvas 绘制都需要结合 DOM 引擎一起,获取到用户选择的元素、处理用户交互事件,然后进行二次计算和响应。
关于首屏直出,后面有空也可以简单唠唠。
# 结束语
本文介绍了渲染引擎架构中,使用多种渲染方式以及底层渲染器适配的设计。
我们常常说给项目选择最优的解决方案,实际上我们也会发现,正因为往往没有所谓最优解,这些产品才会针对不同的场景下提供了不同的解决办法。比如,考虑到性能问题 ECharts 提供了 Canvas/SVG 两种绘制方式;又比如考虑到首屏直出的效率,各个在线表格的团队分别适配了更合适的渲染方式。