之前的布局逻辑中,过分强调参考 DOM 一套,提供强大灵活布局能力,但是忽略有可视化组件和图形的特殊性,以及一些场景的布局易用性。虽然也能达成,但是难用,舍本逐末。

场景

可视化图形中,除了基本单图图形,还有一些其他的场景,也是比较常见的,这些场景概括了来说,需要一个图形对齐的能力:

  1. 混合图形

混合图形的机制,都是通过多个图形叠加,保证位置大小一致的方式来绘制。比如双轴图,线面积混合图(例如:折线表问题趋势,面积表上下限问题趋势)

  1. 分面

根据一个或者多个维度,分面数据,拆分 view 画布空间,然后可以对比不同维值下面不同的指标关系。比如在 rect 分面中,会要求行列反向的图形不受 Axis 的影响,而保证对齐。

  1. 统计元素

使用图形作为统计元素的情况下,一定需要对齐,才有参考意义。

方案

回归到图形的本体上来,图形 Geometry 才是可视化布局中的核心元素。

核心要点:

  1. view padding

依旧保持: view padding 是 Geometry 的 padding,也就是 viewBBox - padding = plotBBox = coordinateBBox。

padding 可以是 ‘auto’、nil、number、number[]。

  1. BBox
  • viewBBox:整个画布大小(包括图形和组件的绘图空间),子 view 通过 region 来拆分父 view 的 plotBBox
  • plotBBox:图形区域的大小,直接等于 coordinateBBox,仅保留一个变量即可。

plotBBox = viewBBox - padding

  1. 布局
  • absolute padding

指定 padding 的情况下,就只是对应方向的剩余空间在 axis、legend 等分配而已。默认都是居中放置

会给组件传入 x、y、maxWidht、maxHeight。组件给出 getLayoutBBox()

  • auto padding

auto 情况下,先根据各个方向的 LayoutBBox,计算出 padding 值。然后走 absolute padding 逻辑。

使用示例

  1. const chart = new Chart({
  2. padding: 48,
  3. });
  4. chart.line('x*y');
  5. const v1 = chart.createView({
  6. region: {
  7. start: { x: 0, y: 1 },
  8. end: { x: 0.5, y: 1 },
  9. },
  10. // padding: 'auto',
  11. });
  12. v1.line('x*y');
  13. v1.point('x*y');
  14. const v2 = chart.createView({
  15. region: {
  16. start: { x: 0.5, y: 1 },
  17. end: { x: 1, y: 1 },
  18. },
  19. });
  20. v1.line('x*y');
  21. v1.area('x*ys');
  22. chart.render();

这个例子中,v1 和 v2 平分 chart 的 plotBBox,然后各自进行 autoPadding,并绘制图形。另外,chart 层也有 x 轴 y 轴,然后放置到 48 的 padding 中。

如果要 v1, v2 对齐,则设置他们两个的 padding 一致即可。

实现

见代码 PR

大概逻辑是在 layout 中:

  1. 增加 padding-cal 方法,用于获取 absolute padding
    1. 如果 padding 不是 auto,那么直接用已有设置的 padding
    2. 否则,通过组件的方位和 width、height,计算 出 padding
  2. 根据 absolute padding 和 viewBBox 计算出 plotBBox(coordinateBBox),更新坐标系
  3. 布局组件,绘制 Geometry