8000 Feat/issue 328 support `barGapInGroup` for bar series and rangeColumn series, used to adjust the gap between bars in each group, by kkxxkk2019 · Pull Request #383 · VisActor/VChart · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Feat/issue 328 support barGapInGroup for bar series and rangeColumn series, used to adjust the gap between bars in each group, #383

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Aug 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"changes": [
{
"packageName": "@visactor/vchart",
"comment": "fix: optimize the type definition related to padding on the bandAxis, and it only takes effect on the first layer of scale",
"type": "patch"
}
],
"packageName": "@visactor/vchart"
}
8000
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"changes": [
{
"packageName": "@visactor/vchart",
"comment": "feat: support `barGapInGroup` for bar series and rangeColumn series, used to set the spacing between bars within a group, relate #328",
"type": "minor"
}
],
"packageName": "@visactor/vchart"
}
11 changes: 11 additions & 0 deletions packages/vchart/src/chart/bar/bar-3d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,22 @@ import { CartesianChart } from '../cartesian/cartesian';
import { ChartTypeEnum } from '../interface';
import { VChart } from '../../core/vchart';
import { Bar3dSeries } from '../../series';
import type { IBar3dChartSpec } from './interface';
VChart.useSeries([Bar3dSeries]);

export class Bar3dChart extends CartesianChart {
static readonly type: string = ChartTypeEnum.bar3d;
static readonly view: string = 'singleDefault';
readonly type: string = ChartTypeEnum.bar3d;
readonly seriesType: string = SeriesTypeEnum.bar3d;

protected _getDefaultSeriesSpec(spec: any): any {
return {
...super._getDefaultSeriesSpec(spec),
barWidth: (<IBar3dChartSpec>spec).barWidth,
barMaxWidth: (<IBar3dChartSpec>spec).barMaxWidth,
barMinWidth: (<IBar3dChartSpec>spec).barMinWidth,
barGapInGroup: (<IBar3dChartSpec>spec).barGapInGroup
};
}
}
3 changes: 2 additions & 1 deletion packages/vchart/src/chart/bar/bar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ export class BarChart extends CartesianChart {
...super._getDefaultSeriesSpec(spec),
barWidth: (<IBarChartSpec>spec).barWidth,
barMaxWidth: (<IBarChartSpec>spec).barMaxWidth,
barMinWidth: (<IBarChartSpec>spec).barMinWidth
barMinWidth: (<IBarChartSpec>spec).barMinWidth,
barGapInGroup: (<IBarChartSpec>spec).barGapInGroup
};
}

Expand Down
4 changes: 3 additions & 1 deletion packages/vchart/src/chart/range-column/range-column-3d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { SeriesTypeEnum } from '../../series/interface';
import { Direction } from '../../typings';
import { VChart } from '../../core/vchart';
import { RangeColumn3dSeries } from '../../series';
import type { IRangeColumn3dChartSpec } from './interface';
VChart.useSeries([RangeColumn3dSeries]);

export class RangeColumn3dChart extends CartesianChart {
Expand All @@ -14,7 +15,8 @@ export class RangeColumn3dChart extends CartesianChart {

protected _getDefaultSeriesSpec(spec: any): any {
const series: any = {
...super._getDefaultSeriesSpec(spec)
...super._getDefaultSeriesSpec(spec),
barGapInGroup: (spec as IRangeColumn3dChartSpec).barGapInGroup
};
series.bar3d = spec.bar3d;
if (spec.direction === Direction.horizontal) {
Expand Down
4 changes: 3 additions & 1 deletion packages/vchart/src/chart/range-column/range-column.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { Direction } from '../../typings';
import { setDefaultCrosshairForCartesianChart } from '../util';
import { VChart } from '../../core/vchart';
import { RangeColumnSeries } from '../../series';
import type { IRangeColumnChartSpec } from './interface';
VChart.useSeries([RangeColumnSeries]);

export class RangeColumnChart extends CartesianChart {
Expand All @@ -15,7 +16,8 @@ export class RangeColumnChart extends CartesianChart {

protected _getDefaultSeriesSpec(spec: any): any {
const series: any = {
...super._getDefaultSeriesSpec(spec)
...super._getDefaultSeriesSpec(spec),
barGapInGroup: (spec as IRangeColumnChartSpec).barGapInGroup
};
series.bar = spec.bar;
if (spec.direction === Direction.horizontal) {
Expand Down
14 changes: 12 additions & 2 deletions packages/vchart/src/component/axis/interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -156,11 +156,21 @@ export interface ILinearAxisSpec {

export interface IBandAxisSpec {
/**
* 轴分组之间间隔,数值在(0,1)之间
* @default 0.2
* 同时设置轴的 paddingInner 和 paddingOuter
* **因为有可能存在多层 scale( xField 设置成了数组,即分组场景),所以支持了数组类型,用于多层 scale 的 bandPadding 配置**
*/
bandPadding?: number | number[];
/**
* band 轴的内边距
* ** 因为有可能存在多层 scale( xField 设置成了数组,即分组场景),所以支持了数组类型,用于多层 scale 的 paddingInner 配置**
* @default 0.1
*/
paddingInner?: number | number[];
/**
* band 轴的外边距
* ** 因为有可能存在多层 scale( xField 设置成了数组,即分组场景),所以支持了数组类型,用于多层 scale 的 paddingOuter 配置**
* @default 0.3
*/
paddingOuter?: number | number[];
/**
* 配置离散轴的数值范围
Expand Down
9 changes: 1 addition & 8 deletions packages/vchart/src/component/axis/mixin/band-axis-mixin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ export class BandAxisMixin {
const isBandPaddingArray = isArray(bandPadding);
const isPaddingInnerArray = isArray(paddingInner);
const isPaddingOuterArray = isArray(paddingOuter);

for (let i = 0; i < this._scales.length; i++) {
const _padding = isBandPaddingArray ? bandPadding[i] : bandPadding;
const _paddingInner = isPaddingInnerArray ? paddingInner[i] : paddingInner;
Expand All @@ -64,14 +65,6 @@ export class BandAxisMixin {
}
}
computeBandDomain(data: { min: number; max: number; values: any[] }[]): StringOrNumber[] {
// const values = data.map(d => d.values);
// return Array.from(new Set(values.flat()));

// // 性能优化 old
// const reuslt = {};
// data.forEach(d => d.values.forEach(v => (reuslt[v] = true)));
// return Object.keys(reuslt);

// 性能优化 9.13
const tempSet = new Set();
for (let i = 0; i < data.length; i++) {
Expand Down
77 changes: 55 additions & 22 deletions packages/vchart/src/series/bar/bar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { CartesianSeries } from '../cartesian/cartesian';
import { MarkTypeEnum } from '../../mark/interface';
import { AttributeLevel } from '../../constant';
import { getActualNumValue } from '../util/utils';
import type { Maybe, Datum } from '../../typings';
import type { Maybe, Datum, DirectionType } from '../../typings';
import { merge, valueInScaleRange } from '../../util';
import type { BarAppearPreset, IBarAnimationParams } from './animation';
import { animationConfig, shouldDoMorph, userAnimationConfig } from '../../animation/utils';
Expand All @@ -23,6 +23,7 @@ import { BaseSeries } from '../base/base-series';
import { VChart } from '../../core/vchart';
import { RectMark } from '../../mark/rect';
import { TextMark } from '../../mark/text';
import { array, isValid, last } from '@visactor/vutils';

VChart.useMark([RectMark, TextMark]);

Expand Down Expand Up @@ -121,16 +122,8 @@ export class BarSeries<T extends IBarSeriesSpec = IBarSeriesSpec> extends Cartes
{
x: (datum: Datum) => valueInScaleRange(this.dataToPositionX(datum), xScale),
x1: (datum: Datum) => valueInScaleRange(this.dataToPositionX1(datum), xScale),
y: (datum: Datum) => {
const bandWidth =
this.getYAxisHelper().getBandwidth?.(this._groups ? this._groups.fields.length - 1 : 0) ??
DefaultBandWidth;
const continuous = isContinuous(yScale.type || 'band');
const pos = this.dataToPositionY(datum);
const width = this._rectMark.getAttribute('height', datum) as number;
return pos + (bandWidth - width) * 0.5 + (continuous ? -bandWidth / 2 : 0);
},
height: () => this.getBarWidth(this._yAxisHelper)
y: (datum: Datum) => this._getPosition(this.direction, datum),
height: () => this._getBarWidth(this._yAxisHelper)
},
'normal',
AttributeLevel.Series
Expand All @@ -139,19 +132,11 @@ export class BarSeries<T extends IBarSeriesSpec = IBa 10000 rSeriesSpec> extends Cartes
this.setMarkStyle(
this._rectMark,
{
x: (datum: Datum) => {
const bandWidth =
this.getXAxisHelper().getBandwidth?.(this._groups ? this._groups.fields.length - 1 : 0) ??
DefaultBandWidth;
const width = this._rectMark.getAttribute('width', datum) as number;
const continuous = isContinuous(this.getXAxisHelper().getScale?.(0).type || 'band');
const pos = this.dataToPositionX(datum);
return pos + (bandWidth - width) / 2 + (continuous ? -bandWidth / 2 : 0);
},
x: (datum: Datum) => this._getPosition(this.direction, datum),
y: (datum: Datum) => valueInScaleRange(this.dataToPositionY(datum), yScale),
y1: (datum: Datum) => valueInScaleRange(this.dataToPositionY1(datum), yScale),
width: () => {
return this.getBarWidth(this._xAxisHelper);
return this._getBarWidth(this._xAxisHelper);
}
},
'normal',
Expand Down Expand Up @@ -206,9 +191,10 @@ export class BarSeries<T extends IBarSeriesSpec = IBarSeriesSpec> extends Cartes
);
}

protected getBarWidth(axisHelper: IAxisHelper) {
protected _getBarWidth(axisHelper: IAxisHelper) {
const hasBarWidth = this._spec.barWidth !== undefined;
const bandWidth = axisHelper.getBandwidth?.(this._groups ? this._groups.fields.length - 1 : 0) ?? DefaultBandWidth;

if (hasBarWidth) {
return getActualNumValue(this._spec.barWidth, bandWidth);
}
Expand All @@ -224,6 +210,53 @@ export class BarSeries<T extends IBarSeriesSpec = IBarSeriesSpec> extends Cartes
return width;
}

protected _getPosition(direction: DirectionType, datum: Datum) {
let axisHelper;
let sizeAttribute;
let dataToPosition;
if (direction === Direction.horizontal) {
axisHelper = this.getYAxisHelper();
sizeAttribute = 'height';
dataToPosition = this.dataToPositionY.bind(this);
} else {
axisHelper = this.getXAxisHelper();
sizeAttribute = 'width';
dataToPosition = this.dataToPositionX.bind(this);
}
const scale = axisHelper.getScale(0);
const size = this._rectMark.getAttribute(sizeAttribute, datum) as number;
const bandWidth = axisHelper.getBandwidth?.(this._groups ? this._groups.fields.length - 1 : 0) ?? DefaultBandWidth;
if (this._groups?.fields?.length > 1 && isValid(this._spec.barGapInGroup)) {
// 自里向外计算,沿着第一层分组的中心点进行位置调整
const groupFields = this._groups.fields;
const barInGroup = array(this._spec.barGapInGroup);
let totalWidth: number = 0;
let offSet: number = 0;

for (let index = groupFields.length - 1; index >= 1; index--) {
const groupField = groupFields[index];
const groupValues = this.getViewDataStatistics()?.latestData?.[groupField]?.values ?? [];
const groupCount = groupValues.length;
const gap = getActualNumValue(barInGroup[index - 1] ?? last(barInGroup), bandWidth);
const i = groupValues.indexOf(datum[groupField]);
if (index === groupFields.length - 1) {
totalWidth += groupCount * size + (groupCount - 1) * gap;
offSet += i * (size + gap);
} else {
offSet += i * (totalWidth + gap);
totalWidth += totalWidth + (groupCount - 1) * gap;
}
}

const center = scale.scale(datum[groupFields[0]]) + axisHelper.getBandwidth(0) / 2;
return center - totalWidth / 2 + offSet;
}

const continuous = isContinuous(scale.type || 'band');
const pos = dataToPosition(datum);
return pos + (bandWidth - size) * 0.5 + (continuous ? -bandWidth / 2 : 0);
}

/**
* spec 更新
* @param spec
Expand Down
28 changes: 22 additions & 6 deletions packages/vchart/src/series/bar/interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import type { BarAppearPreset } from './animation';
import type { ILabelSpec } from '../../component/label';
import type { IMarkProgressiveConfig } from '../../mark/interface';
import type { SeriesMarkNameEnum } from '../interface';
import type { MaybeArray } from '../../typings';

type BarMarks = 'bar';

Expand Down Expand Up @@ -37,17 +38,32 @@ export interface IBarSeriesSpec
| 'inside-left';
};
/**
* 柱体宽度
* 柱体宽度,可以设置绝对的像素值,也可以使用百分比(如 '10%')
* 1. number 类型,表示像素值
* 2. string 类型,百分比用法,如 '10%',该值为对应最后一个分组字段对应的 scale 的 bandWidth 占比(因为柱子是等宽的,所以采用最后一层分组的 scale)
*/
barWidth?: number;
barWidth?: number | string;
/**
* 柱体最小宽度
* 柱体最小宽度,可以设置绝对的像素值,也可以使用百分比(如 '10%')
* 1. number 类型,表示像素值
* 2. string 类型,百分比用法,如 '10%',该值为对应最后一个分组字段对应的 scale 的 bandWidth 占比(因为柱子是等宽的,所以采用最后一层分组的 scale)
*/
barMinWidth?: number;
barMinWidth?: number | string;
/**
* 柱体最大宽度
* 柱体最大宽度,可以设置绝对的像素值,也可以使用百分比(如 '10%')
* 1. number 类型,表示像素值
* 2. string 类型,百分比用法,如 '10%',该值为对应最后一个分组字段对应的 scale 的 bandWidth 占比(因为柱子是等宽的,所以采用最后一层分组的 scale)
*/
barMaxWidth?: number;
barMaxWidth?: number | string;
/**
* 分组柱图中各个分组内的柱子间距,可以设置绝对的像素值,也可以使用百分比(如 '10%')。
* 当存在多层分组时,可以使用数组来设置不同层级的间距,如 [10, '20%'],表示第一层分组的间距为 10px,第二层分组的间距为 '20%'。
* 如果 barGapInGroup 的数组个数小于分组层数,则后面的分组间距使用最后一个值。
* 1. number 类型,表示像素值
* 2. string 类型,百分比用法,如 '10%',该值为对应最后一个分组字段对应的 scale 的 bandWidth 占比(因为柱子是等宽的,所以采用最后一层分组的 scale)
* @since 1.2.0
*/
barGapInGroup?: MaybeArray<number | string>;
}

export interface IBarSeriesTheme extends ICartesianSeriesTheme {
Expand Down
40 changes: 5 additions & 35 deletions packages/vchart/src/series/range-column/range-column.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import type { ITextMark } from '../../mark/text';
import { merge, valueInScaleRange } from '../../util';
import { setRectLabelPos } from '../util/label-mark';
import { AttributeLevel } from '../../constant';
import { isContinuous } from '@visactor/vscale';
import { animationConfig, shouldDoMorph, userAnimationConfig } from '../../animation/utils';
import { RangeColumnSeriesTooltipHelper } from './tooltip-helper';
import { DEFAULT_MARK_ANIMATION } from '../../animation/config';
Expand Down 528C Expand Up @@ -67,20 +66,7 @@ export class RangeColumnSeries extends BarSeries {
}

initMarkStyle(): void {
const rectMark = this._rectMark;
if (rectMark) {
this.setMarkStyle(
rectMark,
{
fill: this.getColorAttribute()
},
'normal',
AttributeLevel.Series
);

this._trigger.registerMark(rectMark);
this._tooltipHelper?.activeTriggerSet.mark.add(rectMark);
}
super.initMarkStyle();

const minLabelMark = this._minLabelMark;
const minLabelSpec = this._spec.label?.minLabel;
Expand Down Expand Up @@ -207,16 +193,8 @@ export class RangeColumnSeries extends BarSeries {
}),
xScale
),
y: (datum: Datum) => {
const bandWidth =
this.getYAxisHelper().getBandwidth?.(this._groups ? this._groups.fields.length - 1 : 0) ??
DefaultBandWidth;
const continuous = isCont A3A7 inuous(yScale.type || 'band');
const pos = this.dataToPositionY(datum);
const width = this._rectMark.getAttribute('height', datum) as number;
return pos + (bandWidth - width) * 0.5 + (continuous ? -bandWidth / 2 : 0);
},
height: () => this.getBarWidth(this._yAxisHelper)
y: (datum: Datum) => this._getPosition(this.direction, datum),
height: () => this._getBarWidth(this._yAxisHelper)
},
'normal',
AttributeLevel.Series
Expand All @@ -225,15 +203,7 @@ export class RangeColumnSeries extends BarSeries {
this.setMarkStyle(
this._rectMark,
{
x: (datum: Datum) => {
const bandWidth =
this.getXAxisHelper().getBandwidth?.(this._groups ? this._groups.fields.length - 1 : 0) ??
DefaultBandWidth;
const width = this._rectMark.getAttribute('width', datum) as number;
const continuous = isContinuous(this.getXAxisHelper().getScale?.(0).type || 'band');
const pos = this.dataToPositionX(datum);
return pos + (bandWidth - width) / 2 + (continuous ? -bandWidth / 2 : 0);
},
x: (datum: Datum) => this._getPosition(this.direction, datum),
y: (datum: Datum) =>
valueInScaleRange(
dataToPosition(this.getDatumPositionValues(datum, this._spec.yField[0]), {
Expand All @@ -249,7 +219,7 @@ export class RangeColumnSeries extends BarSeries {
yScale
),
width: () => {
return this.getBarWidth(this._xAxisHelper);
return this._getBarWidth(this._xAxisHelper);
}
},
'normal',
Expand Down
0