背景
降低图表开发成本的几大节点
1. D3.js的诞生
D3的核心概念:Data-Driven Documents,以数据去驱动视图的渲染,使开发者摆脱了繁琐的视图渲染细节。不再需要手动增删改具体的svg
节点,以及其内置的优秀布局算法。极大的降低了图表开发的门槛(体现在布局算法层面),以及提高图表的开发效率(不再需要去维护具体视图)。
2. ECharts
ECharts
通过配置式的方式去创建图表并搭配完善的文档,先天性的就会比D3
编码式的图表绘制方式门槛要低、效率要高。并且ECharts
将图表的开发逻辑做了一个倒置,从由图元组合形成图表(绘制点线面、图例、提示框来组合成一个完善的图表),改为了从图表的可配置项中选择一些合适的搭配(选择显示X轴、Y轴图例等)。进一步降低了图表开发的心智负担同时规范了图表样式。
3. 各类图表编辑器的出现
DataV
,easyv
,chartcube
等图表编辑器的出现,将图表的配置项进一步抽象成了一些可见、可读的、GUI化的配置表单。用户通过拖拉拽以及点点点的方式来生成图表。这里将文档阅读、以及编码测试两个流程优化了出去。进一步降低了图表开发的门槛与成本。
4. AI生成
近年来随着AIGC
的大规模应用,以Chat-GPT
为代表的AI
通过其巨大的语料库以及优秀的语言模型,基本已经做到了的通过自然语言来生成绘制图表的代码,将图表的生产方式从UI交互提升到了自然语言描述。但由于AI
的不稳定性,由AI
生成的图表普遍不能直接应用于生产,仍然需要一定量的微调,这就又对开发者的编程技巧有了一定的要求。
各类图表编辑器的一些困境
灵活性
这里以ChartCube
中的饼图举例,虽然图例提供了众多的位置(方位分为上下左右、水平位置分为左中右)。但仍然无法将图例配置在环状图的中心位置。针对于其他的配置点例如:环形图扇区的装饰、整体的自定义背景等没有表单即无法配置。在灵活性上相较于编码式的生成图表始终会有所差距。
学习成本
这里以几个图表类编辑器的配置表单举例,众多的表单配置虽然不至于晦涩难懂,但也让人感觉无从下手,在传统的面向图元的设计思维下,要改某个图元的某个样式,需要解决几个问题:1. 这个图元对应的配置项表单在哪?2.这个图元对应的配置项表单里有没有我需要的那个配置项?本质上均为面对众多的配置项如何快速索引的问题。
这里我们设计的图表编辑器采用了图元拾取的方案,点击图元会将表单做筛选,仅显示拾取的图元所对应的配置项,以解决配置项表单快速索引的问题,进一步降低学习成本。
关于图元拾取的详细背景这里有个调研:ECharts源码解析之图元拾取可供参阅。
本文仅介绍在图表编辑器内部实现图元拾取的具体方案。同时也欢迎体验我们开发的图表编辑器Charts-Studio
图元拾取面临的问题
本节及下文中的图表引擎均为ECharts。
拾取困难
ECharts
作为一个配置的图表引擎,同时默认的渲染器又是Canvas
。在Canvas
上做元素识别涉及到图形的识别具有先天困难度。
图元信息不足
即便采用svg
渲染器来规避图形识别的问题。仍然会有图元信息不足的问题,比如在折线图中,X轴的轴线、轴分割线、刻度线、以及映射数据的折现对应的svg
标签均为path
缺少有效的手段去区分一个path
对应的图元究竟是什么。
图元拾取的解决方案
图元信息标注
这里直接引用ECharts源码解析之图元拾取的结论。
由于图元信息不足,要实现图元拾取对图元原始信息的注入不可避免,改动规模较大,对ECharts
代码进行二次侵入的方式不可行。需ECharts
新增属性的字段来描述图元的原始信息。
我们对ECharts
的图形引擎Zrender
的Element
对象做了扩充,新增了EditorInfo
属性以描述该图元对应的原始信息。
1 | export interface EditorInfo { |
以饼图为例,我们对【待绘制的扇区】标注了图元信息,并且新增了环境变量,只有在__EDITOR__
的情景下才会对图元的原始信息作注入。
1 | if (__EDITOR__) { |
在对绝大多数图元做了标注之后我们提交了PR
这里可以见feat(devInfo): set devInfo into component by sz-p · Pull Request #17698 · apache/echarts (github.com)。
图元信息获取
这里借助Zrender
,在__EDITOR__
模式下将图元信息直接写入了标签的elTarget
属性。点击到具体的图元之后通过解析elTarget
属性以获取图元的原始信息。
1 | <path d="M50 96.6667L112 68.3333L174 195.8333L236 110.8333L298 181.6667L360 125" fill="none" stroke="rgb(24,144,255)" stroke-width="2" stroke-linejoin="bevel" elTarget="{"component":"series","subType":"line","element":"polyline","componentIndex":0}" origin-transition="opacity 0.3s ease-out 0s" style="transition: opacity 0.3s ease-out 0s;" origin-opacity="null" opacity="null"></path> |
表单筛选
这里仅涉及编辑器内部私有状态的调整不再说明。
效果
参考 & 引用
ECharts源码解析之图元拾取 | SZ 博客 (sz-p.cn)