背景
分面(Facet)的核心逻辑是按照数据字段,和分片逻辑将当前 view 切分成几个部分,每个部分会生成一个子 view,配合 view 递归嵌套的能力,可以画布区域进行灵活的分片和布局。
G2 将内置以下分面形式:
- circle
- list
- matrix
- mirror
- rect
- tree
API
API 是很简单的,在 view 上新增 facet API,可以指定使用某一个分面方式去做分面,并传入分面的配置。
view.facet(type: string, cfg: FacetCfg) {};
实现
view
- 增加 facet API
- view 生命周期执行分面逻辑
- render
- changeData
facet 基类
facet 基类确定了 facet 的生命周期,以及后续分面的扩展。
生命周期
initial
[x] 初始化过程,初始化 G 容器
[x] 按照分面字段,将数据分片,形成分面数据 FacetData,这个数据最终用来生成分面子 View
render
[x] 将 FacetData 渲染成子 View,执行 eachView 的逻辑
- 然后根据数据生成 title、axis、legend 等一些辅助 UI
- title
- axis
[ ] legend
destroy
[x] 移除 G 容器
- 关闭事件
- 清空数据,view 等
facet 构造参数
class Facet {
// 构造函数
constructor(view: View, cfg: FacetCfg) {
// ...
}
}
/**
* 默认的基础配置
*/
export interface FacetCfg {
// 布局类型
readonly type?: string;
// view 创建回调
readonly eachView: (innerView: View, facet?: FacetData) => any;
// facet 间距
readonly padding?: number;
// 子 view 的 padding
readonly viewPadding?: number;
}
- 构造函数由两个参数,第一个为父级 view,第二个为 facet 实例的配置
- 基类 facet 配置定义结构为 FacetCfg,子类的配置结构继承于它
布局
分面布局分成为几部分:
- facet 区域布局
- facet 区域其实就是 view 的绘图区大小
- 子 view 的布局
- 按照分面字段,均分区域 region
- padding 字段来决定 view 之间的 padding
- margin 字段决定 view 的 margin
- title axis 等组件布局
- 将 title axis 放置到 margin 区域中
rect
内置的矩形分面。
构造参数
/**
* rect 分面的配置
*/
export interface RectCfg extends FacetCfg {
// column, row
readonly fields: [string, string];
}
在基类配置的基础上,增加 fields 分面字段信息。
- 继承 Facet 基类,实现数据分面
- 实现 title
- 实现 axis
- 实现 legend
渲染和布局过程
在 render 过程中的执行逻辑。分面的 render 是 view 嵌套逻辑中的一个子环节,可以到 布局逻辑 文档查看整体的流程。
在 initial 阶段,已经通过分面字段,计算除了分面数据,分面数据包含:
- 分面 region
- 分面数据
- 分面索引
在 render 阶段做的事情包括:
- 渲染分面组件
- 根据分面索引,获取上右下左四个方向贴边的分面数据
- 根据贴边分面数据,生成组件
- 左、下方生成 axis 组件
- 上、右方生成 title 组件
- 非贴边的分面,生成 x axis 组件
- 所有组件都放置到 FORE layer(view 三层中的顶层)
- 布局分面组件
- 根据四个方向的组件,计算上右下左四个方向 gap
- 更新 view 的 coordinateBBox,去除 gap
- 遍历组件,将组件 move 到 coordinateBBox 的四个方向上
- 渲染分面 views
- 根据分面数据,view.createView 生成子 view
- 设置子 view 数据
- 执行 eachView 逻辑,设置子 view 中的 Geometry
然后在 view 的 render 流程中会递归渲染子 view(也就是分面生成的 view)。