@逍为(hustcc)

View 是 G2 的画布容器,里面会包含 Geometry,组件,交互,事件等;而 Chart 是继承 View,直接暴露给开发者的便捷使用入口。

从几个点去做 View 的详细设计:

  • 类图结构
  • 组件、几何图形的关系
  • 数据处理
  • 事件
  • 布局
  • 生命周期

1. 类图结构

整体上 Chart 继承自 View,功能上 Chart 和 View 区别不大。区别点包括:

  1. Chart 需要接受 dom / selector string 作为参数,然后创建 G.Canvas,生成三层 Group
  2. Chart 需要处理容器变化的时候,自适应 / 手动适应图形(ChangeSize)

类图如下:

image.png

View 主要职责:

  1. 子 view、子组件、子几何图形的管理
  2. 生命周期(initial、render、clear、destroy)
  3. 设置配置的 API(比如数据、组件配置等、scale 配置)
  4. 布局
  5. 一些获取信息的 get API
  6. 事件

2. 生命周期职责

目前整理是四个周期:initial、render、clear、destroy。各个周期的职责和内容包括:

  • initial
  1. 处理 region
  2. 绑定事件
  3. 一些默认的初始配置
  • render
  1. 递归 views 渲染
    1. 数据处理(filter)
    2. 创建 coordinate
    3. 构建画布(通过配置生成组件和 Geometry,geometry 数据处理(initial 阶段)完成)
      1. component
        1. axes
        2. legend
      2. geometry
    4. 布局(layout 执行)
  2. 绘制(canvan.draw)
  • clear

clear 和 destroy 区别在于,clear 之后可以通过 initial、render 方法再次使用。

  1. 清空 views、components、geometries
  2. 取消绑定事件
  3. 配置复原
  • destroy

理论上,只能在动态渲染中,动态增删 view 会用到。

  1. clear
  2. 从 parent 中移除,销毁自己

生命周期的流程图如下:

3. 组件、Geometry 关系

View 管理子 view,子组件、子图形。

每个 view 中可以嵌套多个子 view,且可以包含多个组件和图形,具体这些组件和图形是什么,view 不会具体去关心。

至于说组件放到 View,还是放到顶层 Chart,由 controller 去控制和决定,View Chart 本身 API 不感知这个逻辑。

另外,对于组件 Components,还需要告诉 view 他的层级、方位。这些信息仅仅用于后续去生成实例、布局。

  1. addComponent(component: Component, layer: LAYER = LAYER.MID, direction: DIRECTION = DIRECTION.BOTTOM): void

4. 数据处理

View 负责数据的 API 有两个(后续不排除会增加):

  1. view.data
  2. view.filter

view 中的 data 是原始的数据,经过 filter 处理之后,会传给 Geometry 按照图形语法的方式,进行数据到图形的映射流程。

所以 view 这层提供的数据处理非常简单,目前仅仅只有过滤,可以通过扩展 view api 来扩展数据处理能力。

筛选器结构:

  1. export type FilterCondition = (value: any, datum: Datum) => boolean;

5. 事件

View 在事件上会做两个事情:

  1. 监听他管理的 components、geometries 的事件。比如 legend:itemchange,interval:mouseover 等
  2. 事件冒泡。也就是 chart 上可以监听到所有 components、geometries 的所有事件

name:event 组件事件名的逻辑,会直接包到底层 G 模块中。暂不知道如何实现?

6. 布局

布局:是对 view 中的 Views、Components、Geometries,根据配置(Region、Direction 等信息)计算出他们实际的位置和大小,并保证无遮挡、裁剪等问题。

Layout 是一个单纯的函数(简化概念):

  1. export type Layout = (view: View) => void;

用户可以通过 view.setLayout 来指定自己的 layout 逻辑。G2 会内置一套处理 axis、legend、Geometry、分面 的布局逻辑。

  1. 根据子 view 的配置 range,可以计算出子 view 的位置大小;
  2. 根据 view 的大小位置,以及 legend 的 direction,计算出 legend 的位置 x,y;
  3. 根据 axis 内容不遮挡原则,计算出 yaxis 的 width,xaxis 的 height;
  4. 剩余的位置给 Geometry,确定 Geometry 的 位置大小,然后可以反向计算出 yaxis 的 height、xaxis 的 width;以及他们的位置 x,y 信息;
  5. 递归计算子 views 的布局;

开发者可以基于 G2 的 layout,追加自己的一些调整逻辑。或者完全自定义。