VChart 作为一个功能强大的图表库,布局能力也十分强大,内置了紧凑布局模式、grid布局并且支持自定义布局能力。
布局元素
在 VChart 中,并不是所有的图表组成部分都会参与布局,比如tooltip,crosshairs这样的组件就不参与布局。目前 VChart 中参与布局的部分呢有:titie,legned,axes,region,datazoom,scrollBar
但是在 VChart 中布局逻辑却不是按照模块类型来实现的。VChart 有完善的扩展机制,所以可能存在丰富的扩展组件,而这些组件可能也需要参与布局。所以布局能力在设计时使用了布局元素的概念。
基本概念
布局元素不等于图表的模块,它是布局系统使用的一种独立抽象元素。图表的布局模块只针对布局元素进行布局,忽略图表的实际组成组件是什么。

图表模块通过持有一个布局元素,与布局元素通信来参与布局
布局元素属性
布局元素有几个重要属性:
- 布局类型:内置的占位布局通过布局类型给元素不同的布局逻辑
export type ILayoutType = | 'region-relative' // 轴,datazoom这样需要与region贴合,有布局关联的元素, 独立占位,不重合 | 'region-relative-overlap' // 与上面的布局逻辑一致,但是同位置的元素会重叠在一起 | 'region' // 一般只给region元素使用 | 'normal' // 普通布局元素,比如标题。 | 'absolute' // 想要多个元素在同一行布局,比如想并排布局多个图例 | 'normal-inline'; // 绝对定位元素,使用空间信息配置,基于画布左上角进行定位布局
- 空间信息:支持
x,y,lef,top,right,bottom,width,height等属性配置,但是在不同的布局逻辑中,只有部分属性会生效,比如grid布局中,单元格内的元素x,y不会受配置的x,y影响
布局逻辑
VChart内置了2个布局逻辑,并且支持自定义布局
占位布局
这是最常用的布局逻辑。如下图所示

通过逐个将元素放入画布占位,最终形成一个紧凑布局
网格布局
网格布局是将画布按照行列信息划分为一个 n行m列 的网格,然后将图表的元素放到单元格内。
比如官网中下面这个例子,是一个 2列,4行 的网格布局。

网格布局的参数定义
// 网格布局的类型定义 export interface IGridLayoutSpec extends ILayoutSpecBase { /** * 设置布局类型为grid布局 */ type: 'grid'; /** * grid布局的总列数 */ col: number; /** * grid布局的总行数 */ row: number; /** * 可选配置,指定某几列的宽度 */ colWidth?: { /** * 指定列数,序号从 0 开始 */ index: number; /** * 设置指定列的宽度,单位为像素 */ size: number | ((maxSize: number) => number); }[]; /** * 可选配置,指定某几行的高度 */ rowHeight?: { /** * 指定行数,序号从 0 开始 */ index: number; /** * 设置指定行的高度,单位为像素 */ size: number | ((maxSize: number) => number); }[]; /** * * 指定所有图表元素所在位置,图表元素的位置起点和占几行几列,可以占多行多列 * 图表元素位置允许配置重叠。 */ elements: ElementSpec[]; } 网格单元格内模块位置设置 export type ElementSpec = ( | { /** * 组件对应的spec key,如'legends'表示图例 */ modelKey: string; /** * 组件对应的序号 */ modelIndex: number; } | { /** * 组件对应的id */ modelId: string; } ) & { /** * 组件在grid布局中所在的列。从左向右,从 0 开始计数 */ col: number; /** * 组件在grid布局中所在的列跨度,即占了几列,默认值为1 */ colSpan?: number; /** * 组件在grid布局中所在的行。从上向下,从 0 开始计数。 */ row: number; /** * 组件在grid布局中所在的行跨度,即占了几行,默认值为1 */ rowSpan?: number; };
自定义布局
VChart 还提供了自定义布局能力,用户可以任意的对所有布局元素进行空间属性设置。
比如官网的这个例子

将12个饼绘制到时钟的十二个方位。不需要给这些饼图任何额外属性配置,布局逻辑也只有10行左右
const vchart = new VChart(spec, {
dom: CONTAINER_ID,
// 通过 option 传入自定义布局
layout: (chart, item, chartLayoutRect, chartViewBox) => {
/**
* chart 是图表对象
* item 是参与布局的图表模块
* chartLayoutRect 是图表减去padding后的可用布局空间
* chartViewBox 是图表在画布中的位置,包含图表的padding。
*/
const radius = Math.min(chartLayoutRect.width / 2, chartLayoutRect.height / 2);
const center = { x: chartLayoutRect.width / 2, y: chartLayoutRect.height / 2 };
const regionSize = radius * 0.2;
const regionPosRadius = radius - regionSize * 0.5 * 1.415;
// 使用布局元素的属性和提供的方法完成布局
item.forEach((i, index) => {
const angle = (index / 12) * Math.PI * 2;
// 请在布局完成后务必调用
i.setLayoutStartPosition({
x: center.x + Math.sin(angle) * regionPosRadius - regionSize * 0.5,
y: center.y + Math.cos(angle) * regionPosRadius - regionSize * 0.5
});
i.setLayoutRect({ width: regionSize, height: regionSize });
i.updateLayoutAttribute && i.updateLayoutAttribute();
});
}
});