@antv/adjust
Version:
The adjust module for G2, F2.
189 lines (158 loc) • 5.28 kB
text/typescript
import * as _ from '@antv/util';
import { DEFAULT_Y } from '../constant';
import { AdjustCfg, Data, Range } from '../interface';
export type AdjustConstructor = new (cfg: any) => Adjust;
export interface DimValuesMapType {
[dim: string]: number[];
}
export default abstract class Adjust {
/** 参与调整的维度 */
public adjustNames: string[];
/** x 维度对应的字段 */
public xField: string;
/** y 维度对应的字段 */
public yField: string;
// Dodge 属性
/** 调整占单位宽度的比例,例如:占 2 个分类间距的 1 / 2 */
public dodgeRatio: number;
/** 调整过程中 2 个数据的间距,以 dodgeRatio 为分母 */
public marginRatio: number;
/** 指定进行 dodge 的字段 */
public dodgeBy: string;
/** 自定义 offset */
public customOffset: ((data: any, range: any) => number) | number;
// Stack 属性
public height: number;
public size: number;
public reverseOrder: boolean;
/** 像素级组间距 */
public intervalPadding: number;
/** 像素级组内间距 */
public dodgePadding: number;
/** x维度长度,计算归一化padding使用 */
public xDimensionLegenth: number;
/** 分组数 */
public groupNum: number;
// 图形宽度相关配置
/** 用户配置宽度 */
public defaultSize: number;
/** 最大宽度约束 */
public maxColumnWidth: number;
/** 最小宽度约束 */
public minColumnWidth: number;
/** 宽度比例 */
public columnWidthRatio: number;
/** 用户自定义的dimValuesMap */
public dimValuesMap: DimValuesMapType;
constructor(cfg: AdjustCfg & { dimValuesMap?: DimValuesMapType }) {
const { xField, yField, adjustNames = ['x', 'y'], dimValuesMap } = cfg;
this.adjustNames = adjustNames;
this.xField = xField;
this.yField = yField;
this.dimValuesMap = dimValuesMap;
}
// 需要各自实现的方法
public abstract process(dataArray: Data[][]): Data[][];
/**
* 查看维度是否是 adjust 字段
* @param dim
*/
public isAdjust(dim: string): boolean {
return this.adjustNames.indexOf(dim) >= 0;
}
protected getAdjustRange(dim: string, dimValue: number, values: number[]): Range {
const { yField } = this;
const index = values.indexOf(dimValue);
const length = values.length;
let pre;
let next;
// 没有 y 字段,但是需要根据 y 调整
if (!yField && this.isAdjust('y')) {
pre = 0;
next = 1;
} else if (length > 1) {
// 如果以其开头,则取之,否则取他前面一个
pre = values[index === 0 ? 0 : index - 1];
// 如果以其结尾,则取之,否则取他后面一个
next = values[index === length - 1 ? length - 1 : index + 1];
if (index !== 0) {
pre += (dimValue - pre) / 2;
} else {
pre -= (next - dimValue) / 2;
}
if (index !== length - 1) {
next -= (next - dimValue) / 2;
} else {
next += (dimValue - values[length - 2]) / 2;
}
} else {
pre = dimValue === 0 ? 0 : dimValue - 0.5;
next = dimValue === 0 ? 1 : dimValue + 0.5;
}
return {
pre,
next,
};
}
protected adjustData(groupedDataArray: Data[][], mergedData: Data[]) {
// 所有调整维度的值数组
const dimValuesMap = this.getDimValues(mergedData);
// 按照每一个分组来进行调整
_.each(groupedDataArray, (dataArray, index) => {
// 遍历所有数据集合
// 每个分组中,分别按照不同的 dim 进行调整
_.each(dimValuesMap, (values: number[], dim: string) => {
// 根据不同的度量分别调整位置
this.adjustDim(dim, values, dataArray, index);
});
});
}
/**
* 对数据进行分组adjustData
* @param data 数据
* @param dim 分组的字段
* @return 分组结果
*/
protected groupData(data: Data[], dim: string): { [dim: string]: Data[] } {
// 补齐数据空数据为默认值
_.each(data, (record: Data) => {
if (record[dim] === undefined) {
record[dim] = DEFAULT_Y;
}
});
// 按照 dim 维度分组
return _.groupBy(data, dim);
}
/** @override */
protected adjustDim(dim: string, values: number[], data: Data[], index?: number): void {}
/**
* 获取可调整度量对应的值
* @param mergedData 数据
* @return 值的映射
*/
private getDimValues(mergedData: Data[]): DimValuesMapType {
const { xField, yField } = this;
const dimValuesMap: DimValuesMapType = _.assign({}, this.dimValuesMap);
// 所有的维度
const dims = [];
if (xField && this.isAdjust('x')) {
dims.push(xField);
}
if (yField && this.isAdjust('y')) {
dims.push(yField);
}
dims.forEach((dim: string): void => {
if (dimValuesMap && dimValuesMap[dim]) {
return;
}
// 在每个维度上,所有的值
dimValuesMap[dim] = _.valuesOfKey(mergedData, dim).sort((v1, v2) => v1 - v2) as number[];
});
// 只有一维的情况下,同时调整 y,赋予默认值
if (!yField && this.isAdjust('y')) {
const dim = 'y';
dimValuesMap[dim] = [DEFAULT_Y, 1]; // 默认分布在 y 轴的 0 与 1 之间
}
return dimValuesMap;
}
}