之前的布局逻辑中,过分强调参考 DOM 一套,提供强大灵活布局能力,但是忽略有可视化组件和图形的特殊性,以及一些场景的布局易用性。虽然也能达成,但是难用,舍本逐末。
场景
可视化图形中,除了基本单图图形,还有一些其他的场景,也是比较常见的,这些场景概括了来说,需要一个图形对齐的能力:
- 混合图形
 
混合图形的机制,都是通过多个图形叠加,保证位置大小一致的方式来绘制。比如双轴图,线面积混合图(例如:折线表问题趋势,面积表上下限问题趋势)
- 分面
 
根据一个或者多个维度,分面数据,拆分 view 画布空间,然后可以对比不同维值下面不同的指标关系。比如在 rect 分面中,会要求行列反向的图形不受 Axis 的影响,而保证对齐。
- 统计元素
 
使用图形作为统计元素的情况下,一定需要对齐,才有参考意义。
- …
 
方案
回归到图形的本体上来,图形 Geometry 才是可视化布局中的核心元素。
核心要点:
- view padding
 
依旧保持: view padding 是 Geometry 的 padding,也就是 viewBBox - padding = plotBBox = coordinateBBox。
padding 可以是 ‘auto’、nil、number、number[]。
- BBox
 
- viewBBox:整个画布大小(包括图形和组件的绘图空间),子 view 通过 region 来拆分父 view 的 plotBBox
 - plotBBox:图形区域的大小,直接等于 coordinateBBox,仅保留一个变量即可。
 
plotBBox = viewBBox - padding
- 布局
 
- absolute padding
 
指定 padding 的情况下,就只是对应方向的剩余空间在 axis、legend 等分配而已。默认都是居中放置
会给组件传入 x、y、maxWidht、maxHeight。组件给出 getLayoutBBox()
- auto padding
 
auto 情况下,先根据各个方向的 LayoutBBox,计算出 padding 值。然后走 absolute padding 逻辑。
使用示例
const chart = new Chart({padding: 48,});chart.line('x*y');const v1 = chart.createView({region: {start: { x: 0, y: 1 },end: { x: 0.5, y: 1 },},// padding: 'auto',});v1.line('x*y');v1.point('x*y');const v2 = chart.createView({region: {start: { x: 0.5, y: 1 },end: { x: 1, y: 1 },},});v1.line('x*y');v1.area('x*ys');chart.render();
这个例子中,v1 和 v2 平分 chart 的 plotBBox,然后各自进行 autoPadding,并绘制图形。另外,chart 层也有 x 轴 y 轴,然后放置到 48 的 padding 中。
如果要 v1, v2 对齐,则设置他们两个的 padding 一致即可。
实现
见代码 PR。
大概逻辑是在 layout 中:
- 增加 padding-cal 方法,用于获取 absolute padding
- 如果 padding 不是 auto,那么直接用已有设置的 padding
 - 否则,通过组件的方位和 width、height,计算 出 padding
 
 - 根据 absolute padding 和 viewBBox 计算出 plotBBox(coordinateBBox),更新坐标系
 - 布局组件,绘制 Geometry
 
