diff --git a/common/changes/@visactor/vchart/feat-soft-min-max_2024-04-15-05-24.json b/common/changes/@visactor/vchart/feat-soft-min-max_2024-04-15-05-24.json new file mode 100644 index 0000000000..a36bdde8dc --- /dev/null +++ b/common/changes/@visactor/vchart/feat-soft-min-max_2024-04-15-05-24.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "feat: support `softMin` and `softMax` in linear-axis, close #2498\n\n", + "type": "none", + "packageName": "@visactor/vchart" + } + ], + "packageName": "@visactor/vchart", + "email": "dingling112@gmail.com" +} \ No newline at end of file diff --git a/docs/assets/option/en/component/axis-common/linear-axis.md b/docs/assets/option/en/component/axis-common/linear-axis.md index 49ccb1f794..c94b80964a 100644 --- a/docs/assets/option/en/component/axis-common/linear-axis.md +++ b/docs/assets/option/en/component/axis-common/linear-axis.md @@ -10,6 +10,22 @@ **Applies only when the axis is a linear or time axis**. Maximum value(time axis only support timeStamp), **takes precedence over zero and nice**. +#${prefix} softMin(number) + +Supported since version **1.11.0** + +Only effective when the axis is a linear or time axis, minimum value (time axis only supports timestamps), only takes effect when this value is less than the minimum data value, **takes precedence over zero and nice**. + +- Note: Not recommended to use together with configuring `min` + +#${prefix} softMax(number) + +Supported since version **1.11.0** + +Only effective when the axis is a linear or time axis, maximum value (time axis only supports timestamps), only takes effect when this value is greater than the maximum data value, **takes precedence over zero and nice**. + +Note: Not recommended to use together with configuring `max` + #${prefix} nice(boolean) = true **Applies only when the axis is a linear or time axis**. Whether to adjust the axis range to a relatively neat value based on the data. When `min` and `max` are configured, this option is invalid. diff --git a/docs/assets/option/zh/component/axis-common/linear-axis.md b/docs/assets/option/zh/component/axis-common/linear-axis.md index 58847d49f9..62487480b4 100644 --- a/docs/assets/option/zh/component/axis-common/linear-axis.md +++ b/docs/assets/option/zh/component/axis-common/linear-axis.md @@ -10,6 +10,22 @@ **仅当轴为线性轴或时间轴时生效**,最大值(时间轴仅支持时间戳),**优先级高于 zero,nice**。 +#${prefix} softMin(number) + +自**1.11.0**版本开始支持 + +**仅当轴为线性轴或时间轴时生效**,最小值(时间轴仅支持时间戳),当且仅当该值小于数据最小值时,才能生效,**优先级高于 zero,nice**。 + +- 注意:不建议和配置`min`一起使用 + +#${prefix} softMmax(number) + +自**1.11.0**版本开始支持 + +**仅当轴为线性轴或时间轴时生效**,最大值(时间轴仅支持时间戳),当且仅当该值大于数据最大值时,才能生效,**优先级高于 zero,nice**。 + +注意:不建议和配置`max`一起使用 + #${prefix} nice(boolean) = true **仅当轴为线性轴或时间轴时生效**,是否根据数据将轴范围调整到相对规整的数值,当配置了 `min` 和 `max`,该配置项失效。 diff --git a/packages/vchart/src/component/axis/interface/spec.ts b/packages/vchart/src/component/axis/interface/spec.ts index 61f19a10d5..bcd33ac9b2 100644 --- a/packages/vchart/src/component/axis/interface/spec.ts +++ b/packages/vchart/src/component/axis/interface/spec.ts @@ -80,6 +80,19 @@ export interface ILinearAxisSpec { /** 最大值,**优先级高于 zero,nice** */ max?: number; + /** + * 最小值,当且仅当该值小于数据最小值时,才能生效 + * 注意:不建议和配置`min`一起使用 + * @since 1.11.0 + */ + softMin?: number | ((domain: number[]) => number); + /** + * 最大值,当且仅当该值大于数据最大值时,才能生效 + * 注意:不建议和配置`max`一起使用 + * @since 1.11.0 + */ + softMax?: number | ((domain: number[]) => number); + /** @deparated 线性轴数值范围配置(已弃用,请使用外层 min/max) */ range?: { /** @deparated 最小值 */ diff --git a/packages/vchart/src/component/axis/mixin/band-axis-mixin.ts b/packages/vchart/src/component/axis/mixin/band-axis-mixin.ts index 873f750e54..6133c79a1d 100644 --- a/packages/vchart/src/component/axis/mixin/band-axis-mixin.ts +++ b/packages/vchart/src/component/axis/mixin/band-axis-mixin.ts @@ -160,7 +160,7 @@ export class BandAxisMixin { this._scales[i].domain(userDomain); } else { const data = this.collectData(i); - const domain = this.computeDomain(data); + const domain = this.computeBandDomain(data); this._scales[i].domain(domain.sort((a, b) => this._rawDomainIndex[i][a] - this._rawDomainIndex[i][b])); } } @@ -232,7 +232,7 @@ export class BandAxisMixin { this._scales[i].domain(userDomain); } else { const data = this.collectData(i, true); - const domain = this.computeDomain(data); + const domain = this.computeBandDomain(data); this._rawDomainIndex[i] = {}; domain.forEach((d, _i) => (this._rawDomainIndex[i][d] = _i)); } diff --git a/packages/vchart/src/component/axis/mixin/linear-axis-mixin.ts b/packages/vchart/src/component/axis/mixin/linear-axis-mixin.ts index db751c1400..8107065041 100644 --- a/packages/vchart/src/component/axis/mixin/linear-axis-mixin.ts +++ b/packages/vchart/src/component/axis/mixin/linear-axis-mixin.ts @@ -30,6 +30,8 @@ export interface LinearAxisMixin { * 用于在插件(如0值对齐等功能)中使用 */ _domainAfterSpec: number[]; + _softMinValue?: number; + _softMaxValue?: number; _expand?: { max?: number; min?: number }; _tick: ITick | undefined; isSeriesDataEnable: any; @@ -91,11 +93,11 @@ export class LinearAxisMixin { tickCount = Math.max(DEFAULT_TICK_COUNT, tickCount); } const { min, max } = this._domain ?? {}; - if (isNil(min) && isNil(max)) { + if (isNil(min) && isNil(max) && isNil(this._softMaxValue) && isNil(this._softMinValue)) { return this._scale.nice(tickCount); - } else if (isValid(min) && isNil(max)) { + } else if ((isValid(min) || isValid(this._softMinValue)) && isNil(max) && isNil(this._softMaxValue)) { return this._scale.niceMax(tickCount); - } else if (isNil(min) && isValid(max)) { + } else if (isNil(min) && isNil(this._softMinValue) && (isValid(max) || isValid(this._softMaxValue))) { return this._scale.niceMin(tickCount); } @@ -108,11 +110,11 @@ export class LinearAxisMixin { } const { min, max } = this._domain ?? {}; - if (isNil(min) && isNil(max)) { + if (isNil(min) && isNil(max) && isNil(this._softMaxValue) && isNil(this._softMinValue)) { return this._scale.nice(); - } else if (isValid(min) && isNil(max)) { + } else if ((isValid(min) || isValid(this._softMinValue)) && isNil(max) && isNil(this._softMaxValue)) { return this._scale.niceMax(); - } else if (isNil(min) && isValid(max)) { + } else if (isNil(min) && isNil(this._softMinValue) && (isValid(max) || isValid(this._softMaxValue))) { return this._scale.niceMin(); } @@ -148,6 +150,7 @@ export class LinearAxisMixin { domain[0] = 0; domain[1] = 0; } + this.setSoftDomainMinMax(domain); this.expandDomain(domain); this.includeZero(domain); this.setDomainMinMax(domain); @@ -268,6 +271,37 @@ export class LinearAxisMixin { isValid(max) && (domain[1] = max); } + protected setSoftDomainMinMax(domain: number[]): void { + const { softMin, softMax } = this._spec; + + if (isValid(softMin)) { + let softMinValue = isFunction(softMin) ? softMin(domain) : (softMin as number); + + if (isNil(softMinValue)) { + softMinValue = domain[0]; + } + + if (softMinValue <= domain[0]) { + domain[0] = softMinValue; + this._softMinValue = softMinValue; + } + } + + if (isValid(softMax)) { + let softMaxValue = isFunction(softMax) ? softMax(domain) : (softMax as number); + + if (isNil(softMaxValue)) { + softMaxValue = domain[1]; + } + + if (softMaxValue >= domain[1]) { + domain[1] = softMaxValue; + } + + this._softMaxValue = softMaxValue; + } + } + setZero(zero: boolean) { if (this._zero !== zero) { this._zero = zero; @@ -283,7 +317,7 @@ export class LinearAxisMixin { return; } const data = this.collectData(); - const domain: number[] = this.computeDomain(data) as number[]; + const domain: number[] = this.computeLinearDomain(data) as number[]; this.updateScaleDomainByModel(domain); }