简介
View 是拥有独立数据源,并且能够绘制多个图形的容器。View 加入 G2 是从 G2 2.x 开始的,出发点是为了支持分面、地图等功能,之后 View 的功能一直没有变化。自 3.x 移除了统计函数后,一些之前通过统计函数的图表同未经统计的图表一起显示时也需要 View,G2 对 View 的依赖进一步增强。
G2 4.0 版本开始设计时,考虑到在 BI 场景下的自助分析、多维分析的场景,需要对 View 的功能进一步扩展,希望能够让用户不使用 Chart 而独立使用 View,而这需要对 View 进行一些调整,主要体现在下面几个方面:
- View 的嵌套
- 组件和 Controller
- 分面
- 多 View 交互
View 的嵌套
首先明确一下为什么要要进行 View 的嵌套,View 的嵌套有什么好处,有什么坏处?
现状
我们首先来看一下目前的情况,再来分析支持View 嵌套的优劣,给出 4.x 的结论:
- 当前的版本中只有 Chart(本质也是 View) 和 View 两层,View 不能再有子 View。
- View 可以层叠也可以平铺开:
- 地图的场景是层叠的典型场景;
- 显示原始数据和统计数据的场景是层叠;
- 分面是铺开的一个场景;
- 而地图的分面场景是既有层叠又有铺开。
View 无限嵌套的功能理论上来说,通过一层 view 也能支持,不过在既有层叠又有平铺时存在一些限制。
支持 View 的好处
语法上是完备的,比较易懂的嵌套递归逻辑
-
支持 View 的缺点
增加 G2 的复杂度,配置项要逐层向下传,事件要一级级向上冒泡
- 组件同 View 的关系更加复杂,是否每一级都允许有 legend、tooltip,不同级上显示的相同组件如何关联
- 如果支持统计函数(原始数据和统计数据仅需要一个数据源),View 嵌套的场景少之又少,为了这么少的场景是否划算
组件和 Controller
组件和 View 的关系,需要理顺下面的几个问题:
- 哪些组件仅仅属于 Chart
- 组件是仅对当前 View 生效还是也要考虑子 View 的影响
- 组件的布局问题
- View 的组件的控制权是否开放给用户,一旦开放,如何处理多层嵌套的问题
-
当前组件的所属关系
当前版本下组件的隶属关系:
Legend 和 Tooltip 仅属于 Chart
- guide(annotation) 和 axis 都属于 View
labels 属于 Geometry 这里不做更多讨论 目前 G2 的所有组件是自动生成的,其逻辑如下:
Legend 是 Geometry 上的视觉通道 color, size, shape 决定;
- Axis 是由 Geometry 上的视觉通道 position 决定;
- Tooltip 默认仅仅在 Chart 上生成
labels 受 Geometry 上的 label 方法确定
G2 4.0 的组件所属关系
每个 view 可以有自己的 tooltip,第一次调用时生成,除非设置 tooltip(false),可以既显示自身的信息,也可以显示子 view 的信息,其判定由 interaction 决定。
- 每个 View 也需要有自己的 Legend 但是Legend 是否能够控制子 view 图形还需要梳理
- Geometry 上是否也可以开放 guide,这便于实现 max,min 等同数据密切相关的辅助元素
组件的布局
各组件的布局位置:
- Legend 一般显示在图表绘图区域的外边,保证不与图表的图形、文本相遮挡
- Axis 显示在绘图区域的边缘,栅格线要在图形的下面,文本保持不与 Legend 的遮挡
- Legend 也不能同 Axis 遮挡
- Labels 要保证在图表图形的上面,也不能到画布外面显示不完整
- Guide (annotation) 的布局同图表的绘制区域相关,有些在图形的下面,有些在图形的上面,也得保证不能被画布边缘裁剪。
- Tooltip 默认不显示,显示时跟随鼠标
而用户还有更进一步的需求:
- 要求图表绘图区域和画布的边框自动计算出 padding ,这就需要考虑
- Axis 和 Legend 在边框上的排布
- Labels 和 guide 和边框的重合
- 要求图表绘图区域的文本不能相互遮挡
- Guide 上的文本遮挡问题
- Labels 上的遮挡问题
-
View 的组件的控制权
前面已经讲了组件大多数会自动生成,其生成逻辑同视觉映射相关,非自动组件有:
guide (annotation) 用户整个 view 时,需要指定具体的位置
- 滑动条,用来对数据进行过滤,一般需要用户单独指定
- 其他组件,用户只能控制是否显示(生效)
但是在 G2 4.0 中需要考虑组件的扩展,需要增加更多种的组件类型
View 的事件机制
当事件在 View 上触发时需要解决下面的这些问题:
- 事件是否属于 View,是否需要冒泡抛出
- View 的组件触发事件,是否也要在 View 上抛出
- 当一个事件同时发生在多个 View 中时,如何处理
统一的事件机制,来自图形元素的事件、空白画布处的事件、Element 的事件、View 上的事件、组件上的事件
Controller
看到上面的内容后,你就理解 Controller 是来干什么了,Controller 的功能是:
根据 Geometry 上的映射规则自动生成组件
- 完成组件的布局,防止同画布绘制区域重合
- Chart、View、Geometry 更新时同步更新
处理好 Chart、View、Geometry 等层级的事件
G2 4.0 中的设计
首先确定无论是否支持 View 嵌套都要支持的功能:
组件的自动生成需要在 View/Chart 的 Controller 中实现
- 自适应边框 padding ,仅在 Chart 层考虑,通用的 view 层不支持
- 支持组件指定类型,用户可以通过继承来扩展对应的组件
- Chart 和 View 上没有同数据互动的交互,所有交互由外部的 interaction 实现
-
分面
G2 中默认支持了几种常见的分面:
rect
,list
,tree
,mirror
,matrix
当前分面的问题: 不可用问题:而当前的分面一个分面仅能使用一个 View ,那么多层的地图、点图和回归线、个别双轴图(数据源不一样时)。
- 不好用问题:多个分面使用共同的 Geometry (包括映射语法),依然需要循环多次
- 交互联动问题:显示 tooltip,筛选等操作都受限
- 分面的间距、坐标轴文本的是否显示等等跟布局、遮挡相关的问题
在 G2 4.0 设计时,需要考虑一个问题:分面是否不应该是 G2 默认的功能,而是上层的一个产品?
要解开上面的问题,需要进行下面的一些改造:
- 重新引入统计函数,认为原始数据和统计值是同一份数据源,就不需要拆分多个 View
- 支持无限级 View 的嵌套,度量的统一可以在 View 层确定
- 不好用的问题,可能需要在上层的产品来解决。在 G2 层仅能提供机制,很难做的精细
- 交互联动问题,下面章节讨论
- 分面的间距,同 View 上的组件布局是同一个问题,需要一起解决
多View 交互
多 View 的交互需要以 View 的事件机制来保证,View 之间进行联动主要有下面几个场景:
- 一起进行数据过滤,图例筛选、框选等
- 同时显示 View 上的数据细节,tooltip 联动
- 一个 View 上交互的图形,其他 View 上的图形同等反馈
由于 G2 不在内部添加任何交互的逻辑,所以需要在 interaction 中实现上面的功能,可以默认实现一些交互,其他的让用户自己进行扩展。
数据过滤
可以定义一个状态量,将筛选的范围放到状态量中,interaction 监听状态量的变化,然后更新相关的 view。
tooltip 联动
定义一个 interaction ,监听鼠标移动事件,直接调用各个 view 上显示 tooltip 的接口
最终的类图
需要实现以下功能:
- chart 上对组件的配置能够影响所有 view, view 上的配置项能够影响所有子 View
- 如果度量需要同步 sync ,那么度量在设置 sync 的那一层创建,所有子 view 共享
- View 可以组件(axis, legend),并可以动态的修改一些配置项
总结
本章讨论了 View 的设计,原先对 View 使用不多的用户不会受到很大影响。如果你是一个图表的开发者,你会发现View 的变化使得你对图表的控制更加精细,如果你是一位多维分析工具的开发者,这将是你梦寐以求的工具。