背景
G2 的 View 是一个容器,它具备有自己的容器大小,容器中可以放置:
- 子 view
- Geometry 图形语法标记
- Component 辅助组件
View 可以构造一个树形的结构。每个节点中可能存在有几何标记 Geometry 和组件 Component。
- Geometry:折线图、柱形图 …
- Component:axis、legend …
布局是对画布空间的管理和划分。包括两部分:
- view 嵌套的布局
- viewBBox:view 的实际绘图大小,除去 padding 之后的
- view 内部组件的布局
- coordinateBBox:Geometry 的绘图大小
- Component 的位置信息
View 嵌套布局
最终计算的是 viewBBox 大小。
view 的嵌套布局会用到的场景:
- 分面
- 手动创建多 view(常在自定义场景,比如:组合饼图 / 字母饼图)
view 的布局主要通过两个机制:
- view region
export interface Region {
readonly start: Point;
readonly end: Point;
}
确定了 view 的大小范围,start、end 中均值 0 ~ 1 的相对父 view 大小的比例。暂时不去支持按照绝对像素划分,后续按情况而定。
例如:
view.createView({
region: {
start: { x: 0, y: 0 },
end: { x: 1, y: 0.5 },
}
})
创建一个子 view,子 view 的占据的位置做父 view 的左半边。
- view padding
默认为 0,指定 view 的内边距,和 dom 的概念是一样的。
view 的大小除去 padding 的大小,剩余的空间用来绘制 view 中的 Component 和 Geometry 组件。
view 布局的过程在 initial 阶段就完成了,结合父 view 的 viewBBox 以及子 view 的 region + padding 进行计算。这个过程不存在什么问题。
组件布局
组件布局的过程是在 viewBBox 的区域内,布局当前 view 中的组件和 Geometry。最终得到的是:
- coordinateBBox:Geometry 的绘图大小
- Component 的位置信息
约束
- 技术概念约束
从技术概念上来看,Geometry 是整个的绘制图形的区域,会占用 Component 之后所有的区域面积。
- Geometry 尽可能占用 Component 之外的所有空间,由 coordinate 来处理空间。
- x Axis 的宽度 = Geometry 的宽度
- y Axis 的高度 = Geometry 的高度
- 视觉设计约束
组件占用区域宽度或者高度不超过整体 viewBBox 的 25%。
过程
整个组件布局过程,是在 layout 阶段执行一个 layoutFunc 完成,这个 layoutFunc 可以由外部进行自定义。G2 会内置一个 defaultLayout。
PS:每个 view 都有自己的 layoutFunc,他们默认都是同一个 defaultLayout,可以按照特殊情况去指定 view 自己特定的 layout。
- 初始化 coordinateBBox = viewBBox
- 计算最大的组件空间位置
- widht = viewBBox.width * 0.25
- height = viewBBox.height * 0.25
- 布局 legend 类型组件
- 根据 direction 方位来判断 top、right、bottom、left 四个方位的组件数量,每个方向的组件平分 width、height 区域作为他的最大宽高,如果有指定宽高,则按照指定的宽高来分配。
- 依次处理 top、right、bottom、left 四个方位的组件
- 根据 coordinateBBox 以及组件的 direction 将组件 move 到对应的 x、y 位置
- 裁剪到组件的空间,形成新的 coordinateBBox
- 布局其他自定义组件(逻辑和 legend 基本类似,都是固定宽高)
- 布局 axis 类型组件
- 如果 x axis 没有指定高度、y axis 没有指定宽度(相当于是 auto),那么根据 axis 不遮挡的原则,计算 x axis 的 height,y axis 的 width。
- 更新 coordinateBBox,除去 x axis 的 height,y axis 的 width,更新 x,y,width,height。
- coordinateBBox.width 作为 x axis 的 width,coordinateBBox.height 作为 y axis 的 height。
- 依据紧贴 coordinateBBox 的区域的原则,更新 axis 的 x,y 位置,并进行 move 操作。
完成。达成:
- 计算出最终的 coordinateBBox;
- 所有的 Component 都有对应的宽高以及精准的 x,y 信息;
View + Facet 布局流程图
@逍为(hustcc) TODO
问题 & 脑洞
- 组件共享 0.25 宽高
如何让多个组件共享 0.25 的最大宽度:
- 都不超出
- 一个超出
- 都超出
且 legend 组件的大小之后在绘制之后才知道具体的高度,如果要做好 0.25 空间的分配,一定会有大量的尝试性重绘。
建议简化逻辑,每个组件最大宽高都是 0.25 区域大小。因为实际情况出现一个方向多个组件的情况并不多。
- 约束布局
view 是一个固定空间的大小的区域,在这个区域中的组件 x、y、width、height 之间互相有关系,这种情况下,非常适合使用约束布局的算法。
可以拿一个开源的 约束布局 算法来测试看看,性能、效果、代码维护上是否符合要求。