从用户的角度来看,G2 4.0 要释放的亮点主要有两个:

  1. 图表交互的各种可能性
  2. 全新的图表组件

为了支撑以上两点,G2 4.0 在架构上需要进行的改造主要包括:

  1. Chart、View、Geometry、Shape、Component 概念以及组织结构的梳理,同时引入 Element 概念
  2. 支持数据更新机制
  3. View 的嵌套支持
  4. 图表组件
  5. 事件 && 交互机制
  6. 自定义 Shape

1. 图元结构

在 G2 3.x 架构中,我们设计了如下的概念:

  1. Chart,作为 G2 图表的统一入口,负责画布、View 的创建以及图表整体的渲染
  2. View 视图,包含自己的数据源、坐标系,负责数据的装载、图表组件与几何标记的组装、图表组件的各种配置,以及图表生命周期的管理
  3. Geometry 几何标记,图形语法的核心层,处理数据到图形的映射逻辑。Coordinate、Scale、Attribute、Adjust 模块都是服务于这一映射逻辑的。
  4. Shape 图形,最后绘制到画布的几何图形
  5. Component 图表组件,包含坐标轴 Axis、图例 Legend、提示信息 Tooltip、静态辅助标记 Annotation

而 Chart、View、Geometry 这三个都是抽象概念,只有 Shape 属于真正的实体,从用户的角度出发,这些概念可能因为过于抽象而不好理解,另外因为一条数据对应的图形元素可能会有多个 shape,之前无论是交互还是动画,都是以 shape 为操作单位,所以导致很多交互和动画实现得非常繁琐甚至无法完成,所以在 G2 4.0 中决定将目前这些概念以及组织结构做一层梳理,将 Chart、View、Geometry、Shape、Component 同 G 中 Canvas、Group、Shape 进行统一:

  1. Chart、View、Geometry 仍作为抽象概念,负责画布创建、数据装载、组装、生命周期管理以及数据处理等。
  2. Component 实现为 Group
  3. Geometry 中引入 Element 图形元素的概念,同时 Element 也实现为 Group,即将一份数据对应的所有 shape 都作为 Element,以 Element 作为操作单位

image.png

以下是 G2 3.X 的图层结构,并不以各个 View 来组织 Group,而且每个 group 的创建都是由 View 和 Geometry 实例在内部进行创建 G2 4.0 改造点及方案 - 图2此次 4.0 的重构中,将画布的组织结构调整如下,同时 Geometry 和 Component 也直接作为 Group 子类进行实现,同时加入 Element 概念,将一份数据对应的所有 shape 称为一个 Element。 G2 4.0 改造点及方案 - 图3

2. 数据更新机制

我们可以将引起图表更新的行为概括为以下四种:

  1. 数据源更新 chart.changeData()

    虽然是更新整个数据源,但是数据仍然是同构的,会引起 Component、Element 的更新
    View -> Geometry -> Element

  2. 组件的交互操作

    1. 会引起数据的变化(数据的过滤、排序等操作,比如图例、slider 等组件),进而引起 Component、Element 的更新。这种情况 View 的数据源不会发生变化,但是会在 View 做数据相应的操作(过滤),然后再传入给每个 Geometry,引起每个 Geometry 的数据更新
    2. Element 的状态变化:显示隐藏,交互状态的更新
    3. 组件本身变化:通常还会关联 Element 的变化
  3. 交互行为,如框选、拖拽等。交互行为将会引发:

    1. Component、Element 的图形样式变化
    2. Element 状态变化
    3. Element 数据变化,进而引起 Component、Element 的更新。Element 本身的数据更新引起 view data 的更新
  4. Geometry 等的映射规则变化

    1. mapping 规则的变化
    2. Geometry 类型的变化

从以上行为引起的更新进行总结,我们可以将 G2 图表上的更新分为三大类:

  1. 数据更新:数据源更新、数据过滤、Element 局部数据更新
  2. 图形状态更新:组件、Element 的状态以及样式变化
  3. 映射规则的更新:几何元素的变化和图形属性的映射变化

目前的设计先不考虑第 3 点,针对 1、2 两点,对 Geometry 层的数据流设计如下,相比于现有数据流,只是中间多了一步 Element 的创建。

数据更新机制描述

  1. 第一次初始化绘制的时候维护一份 map 映射,存储每一条数据同 Element 的对应关系
  2. 当发生数据变化时,Geometry 进行更新,对更新后的每条数据根据同样的 id 策略生成一份 id 集
  3. 通过新老 id 的对比,获取发生增、删、改的id:
    1. 增,新生成 Element
    2. 删,删除对应的 Element
    3. 改,更新对应的 Element
  4. 具体的更新逻辑在 Shape 层进行实现,同时变更时的动画也可以在每个具体的 shape 进行实现

image.png
image.png

以上数据流的改造,需要引入 Element 类之外,还需要对自定义 Shape 进行重构

Element

Element 除了负责 Shape 的创建外,还应该支持:

  1. style() 样式的更新
  2. setState() 状态的更新
  3. update() 数据的更新,当 Element 上的数据发生变化时,需要设计一种机制,通知上层 View 进行数据更新,走数据更新逻辑。

🤔🤔🤔讨论点 批量更新 Elements 怎么办?即同时调用多次 element.update()

自定义 Shape

  • draw(cfg: object, element: Element) 绘制 Shape
  • setState(type: string, value: boolean, element: Element) 响应状态量
  • update(cfg: object, element: Element) 更新 Shape
  • remove(element: Element) 用于配置销毁动画(这个待讨论,加这个接口主要用于支持 Shape 销毁动画的配置)

🤔🤔🤔讨论点 因为支持了更新机制,那么完全可以在自定义 Shape 层定义对应的出场、入场、更新动画,状态量发生变更的时候也可以设置动画,但是就需要考虑:

  1. 不同坐标系下的 shape 动画怎么区分,因为我们 Shape 是使用关键点进行绘制的,完全不关心坐标系的概念,但是不同坐标系对应的动画形态是不同的,这个时候就要根据坐标系类型区别对待了,比如柱状图和玫瑰图的动画就不同
  2. 如何支持自定义动画?我们默认会为自定义的 Shape 定制动画,但是如果用户想要自定义怎么办
  3. Geometry 的群组动画怎么支持?

Geometry Label

🤔🤔🤔讨论点 目前 label 和 shape 是属于不同的 group 的,是否要将 label 同 group 保持在同一层?即直接在 Element 上绘制,但是这样的话 Label 的遮挡问题怎么解?另外还有 Label 的动画。

  1. 将 label 放置在 Element?
  2. 添加一个 Text Geometry 专门绘制文本?

3. View 的嵌套设计

image.png
Chart 和 View 的关系与 3.x 保持一致,但是我们会在 4.0 中支持 View 的嵌套设计。对于 View 的嵌套比较纠结点讨论确定如下:

  • 组件的归属

之前比较纠结的点是 View 和 Component 的归属关系,通过讨论确定如下:

  • 每个 View 拥有自己的 Axis、Tooltip、Annotation
  • Legend 由 Chart 来统一管理,我们只支持 80% 的场景(80% 的场景多 View 的数据都是关联的)
  • Auto padding 的计算也由 Chart 层来统一计算,但是也开放 View 层进行 padding 设置,保持概念的统一。

    1. ![image.png](https://cdn.nlark.com/yuque/0/2019/png/98090/1567085574087-19944607-28cf-4047-adb0-20dce59d39c8.png#align=left&display=inline&height=278&name=image.png&originHeight=652&originWidth=1322&size=48769&status=done&width=563)

    另外我们规划的 Component Layout 也自然就放置在 Chart 层。

4. 图表组件

图表组件这一期的改造包括:

  1. 提升默认提供的图表组件的质量,默认提供的图表组件包括:Axis 坐标轴、Tooltip 提示信息、Legend 图例、Annotation 静态标注、Slider 滑块、Timeline 时间轴
  2. 组件的插拔机制,这种机制需要支持:
    1. 装载自定义组件
    2. 组件使用的选择权,即用户在使用 G2 的图表时,可以选择使用哪些组件(G2 默认的和自定义的),生成哪些组件。
  3. 自定义组件

G2 默认提供的图表组件包括:

  • Axis 坐标轴
  • Tooltip 提示信息
  • Legend 图例
  • Annotation 静态标注
  • Slider 滑块
  • Timeline 时间轴

对于默认组件,此次 4.0 必须在功能、体验上都要投入时间做精做优。

在 4.0 的图表组件,涉及到生成、布局、更新、交互四点。

  • 对于生成,首先明确的是组件将实现为纯 GUI,组件就是一个 Group
  • 组件和图表的交互都会通过交互行为实现
  • 组件需要提供更新接口
  • 组件的动画也在组件内部自己实现
  • 组件的布局通过上层的 Component layout 进行统一管理

自定义组件

自定义组件包括两种场景:

  1. 用户定制新类型组件,比如 Title。这类需求通过提供 View 上细粒度的生命周期事件,让用户将自己编写的组件装载进图表
  2. 基于现有组件进行扩展,比如加入一种新的 Axis 类型,这就需要根据现有的 Axis 基类去继承实现了。

5. 事件 && 交互

事件

目前 G 层已经对事件进行了改造,所以事件这块直接就在 Chart 层进行透传就好了,所以事件这层也在 Chart 层进行管理了。

  • 参见 G 4.0 的事件设计:链接

交互

交互的设计详见: https://www.yuque.com/antv/g2-docs/muv9tf#vHiUe