diff --git a/common/changes/@visactor/vchart/fix-marker-line-offset_2024-05-27-06-28.json b/common/changes/@visactor/vchart/fix-marker-line-offset_2024-05-27-06-28.json new file mode 100644 index 0000000000..3ca384aece --- /dev/null +++ b/common/changes/@visactor/vchart/fix-marker-line-offset_2024-05-27-06-28.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@visactor/vchart", + "comment": "fix: \\`type-step\\` markLine's label should consider the refX/refY/dx/dy set by user, fixed#2739", + "type": "none" + } + ], + "packageName": "@visactor/vchart" +} \ No newline at end of file diff --git a/docs/assets/examples/en/map-chart/map-with-flying-lines.md b/docs/assets/examples/en/map-chart/map-with-flying-lines.md new file mode 100644 index 0000000000..e98b9525aa --- /dev/null +++ b/docs/assets/examples/en/map-chart/map-with-flying-lines.md @@ -0,0 +1,150 @@ +--- +category: examples +group: map chart +title: Map With Flying Lines +keywords: map,space,polygon,scatter,line +order: 11-2 +cover: /vchart/preview/map-with-flying-lines_1.11.1.png +option: mapChart +--- + +# Map With Flying Lines + +## Key option + +- Specify the chart table type `type: common` as a combined chart. +- The `series` field is used to configure the chart series, in this case a map series, a scatter series, and a line series are configured + - `series[0]` for the map series + - `series[1]` is the scatter series + - `series[2]` is the line series, used for the flying lines + - `seriesField: 'type'` is used to set the series, otherwise the data points will be connected sequentially + +## Demo source + +```javascript livedemo +const response = await fetch('https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/geojson/world.json'); +const geojson = await response.json(); +VChart.registerMap('world', geojson); + +const earthquakeData = { + values: [ + { + lng: 86.38, + lat: 43.82, + time: '2018-12-08 06:39:26', + level: '4.5', + depth: '10', + addr: 'Hutubi, Changji, Xinjiang' + }, + { + lng: 118.68, + lat: 40.21, + time: '2018-12-06 21:07:56', + level: '2.1', + depth: '13', + addr: "Qian'an, Tangshan, Hebei" + }, + { + lng: 99.61, + lat: 23.92, + time: '2018-12-06 06:23:34', + level: '2.9', + depth: '5', + addr: 'Gengma, Lincang, Yunnan' + }, + { + lng: 118.62, + lat: 23.36, + time: '2018-12-05 20:22:51', + level: '3.7', + depth: '15', + addr: 'Taiwan' + }, + { + lng: 77.45, + lat: 40.4, + time: '2018-12-05 13:52:37', + level: '4.1', + depth: '17', + addr: 'Qizilsu or Atushi, Xinjiang' + } + ] +}; +const center = { + lng: 119.2, + lat: 28.5, + addr: 'Jinhua, Zhejiang' +}; + +const spec = { + type: 'common', + region: [ + { + roam: false, + coordinate: 'geo', + longitudeField: 'lng', + latitudeField: 'lat', + projection: { + type: 'equirectangular' + } + } + ], + background: 'rgb(240, 248, 255)', + data: [{ values: earthquakeData.values }], + series: [ + { + type: 'map', + map: 'world', + tooltip: { visible: false }, + defaultFillColor: 'rgb(245,255,250)' + }, + { + type: 'scatter', + xField: 'time', + yField: 'addr', + point: { + style: { + size: datum => Number(datum.depth), + fill: 'green' + } + }, + label: { + visible: true, + position: 'right', + style: { + fill: '#333', + fontWeight: 'bold' + } + } + }, + { + type: 'line', + seriesField: 'type', + line: { style: { stroke: 'green' } }, + point: { visible: false }, + data: { + values: [ + ...earthquakeData.values.map((value, index) => ({ + ...value, + type: index + })), + ...earthquakeData.values.map((_, index) => ({ + ...center, + type: index + })) + ] + } + } + ] +}; + +const vchart = new VChart(spec, { dom: CONTAINER_ID }); +vchart.renderSync(); + +// Just for the convenience of console debugging, DO NOT COPY! +window['vchart'] = vchart; +``` + +## Related Tutorials + +[Map](link) diff --git a/docs/assets/examples/menu.json b/docs/assets/examples/menu.json index e23437f204..079e9e898c 100644 --- a/docs/assets/examples/menu.json +++ b/docs/assets/examples/menu.json @@ -871,6 +871,13 @@ "zh": "散点地图", "en": "Scatter Map" } + }, + { + "path": "map-with-flying-lines", + "title": { + "zh": "带飞线的地图", + "en": "Map With Flying Lines" + } } ] }, @@ -2621,4 +2628,4 @@ ] } ] -} \ No newline at end of file +} diff --git a/docs/assets/examples/zh/map-chart/map-with-flying-lines.md b/docs/assets/examples/zh/map-chart/map-with-flying-lines.md new file mode 100644 index 0000000000..86c427d31a --- /dev/null +++ b/docs/assets/examples/zh/map-chart/map-with-flying-lines.md @@ -0,0 +1,150 @@ +--- +category: examples +group: map chart +title: 带飞线的地图 +keywords: map,space,polygon,scatter,line +order: 11-2 +cover: /vchart/preview/map-with-flying-lines_1.11.1.png +option: mapChart +--- + +# 带飞线的地图 + +## 关键配置 + +- 指定图表表类型`type: common`为组合图 +- `series` 字段用于配置图表系列,本例中配置了一个地图系列、一个散点系列和一个折线系列 + - `series[0]`为地图系列 + - `series[1]`为散点系列 + - `series[2]`为折线系列,用于实现飞线效果 + - `seriesField: 'type'`用于设置系列,否则各数据点会顺序相连 + +## 代码演示 + +```javascript livedemo +const response = await fetch('https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/geojson/world.json'); +const geojson = await response.json(); +VChart.registerMap('world', geojson); + +const earthquakeData = { + values: [ + { + lng: 86.38, + lat: 43.82, + time: '2018-12-08 06:39:26', + level: '4.5', + depth: '10', + addr: '新疆昌吉州呼图壁县' + }, + { + lng: 118.68, + lat: 40.21, + time: '2018-12-06 21:07:56', + level: '2.1', + depth: '13', + addr: '河北唐山市迁安市' + }, + { + lng: 99.61, + lat: 23.92, + time: '2018-12-06 06:23:34', + level: '2.9', + depth: '5', + addr: '云南临沧市耿马县' + }, + { + lng: 118.62, + lat: 23.36, + time: '2018-12-05 20:22:51', + level: '3.7', + depth: '15', + addr: '台湾海峡' + }, + { + lng: 77.45, + lat: 40.4, + time: '2018-12-05 13:52:37', + level: '4.1', + depth: '17', + addr: '新疆克孜勒苏州阿图什市' + } + ] +}; +const center = { + lng: 119.2, + lat: 28.5, + addr: '浙江省金华市' +}; + +const spec = { + type: 'common', + region: [ + { + roam: false, + coordinate: 'geo', + longitudeField: 'lng', + latitudeField: 'lat', + projection: { + type: 'equirectangular' + } + } + ], + background: 'rgb(240, 248, 255)', + data: [{ values: earthquakeData.values }], + series: [ + { + type: 'map', + map: 'world', + tooltip: { visible: false }, + defaultFillColor: 'rgb(245,255,250)' + }, + { + type: 'scatter', + xField: 'time', + yField: 'addr', + point: { + style: { + size: datum => Number(datum.depth), + fill: 'green' + } + }, + label: { + visible: true, + position: 'right', + style: { + fill: '#333', + fontWeight: 'bold' + } + } + }, + { + type: 'line', + seriesField: 'type', + line: { style: { stroke: 'green' } }, + point: { visible: false }, + data: { + values: [ + ...earthquakeData.values.map((value, index) => ({ + ...value, + type: index + })), + ...earthquakeData.values.map((_, index) => ({ + ...center, + type: index + })) + ] + } + } + ] +}; + +const vchart = new VChart(spec, { dom: CONTAINER_ID }); +vchart.renderSync(); + +// Just for the convenience of console debugging, DO NOT COPY! +window['vchart'] = vchart; +``` + +## 相关教程 + +[地图](link) diff --git a/docs/public/vchart/preview/map-with-flying-lines_1.11.1.png b/docs/public/vchart/preview/map-with-flying-lines_1.11.1.png new file mode 100644 index 0000000000..dc5b2eb7d3 Binary files /dev/null and b/docs/public/vchart/preview/map-with-flying-lines_1.11.1.png differ diff --git a/packages/vchart/src/chart/polar/interface.ts b/packages/vchart/src/chart/polar/interface.ts index df0c68e7ac..43a119d6f7 100644 --- a/packages/vchart/src/chart/polar/interface.ts +++ b/packages/vchart/src/chart/polar/interface.ts @@ -1,6 +1,6 @@ import type { IPolarAxisSpec } from '../../component/axis/polar/interface'; import type { IPolarCrosshairSpec } from '../../component/crosshair/interface'; -import type { IChartSpec } from '../../typings'; +import type { IChartSpec, ILayoutRect, IPoint } from '../../typings'; export interface IPolarChartSpec extends IChartSpec { /** @@ -13,4 +13,9 @@ export interface IPolarChartSpec extends IChartSpec { * 十字辅助线配置 */ crosshair?: IPolarCrosshairSpec | IPolarCrosshairSpec[]; + + /** + * @since 1.11.2 + */ + layoutRadius?: 'auto' | number | ((layoutRect: ILayoutRect, center: IPoint) => number); } diff --git a/packages/vchart/src/component/axis/polar/axis.ts b/packages/vchart/src/component/axis/polar/axis.ts index 7114cbfb56..7b97e967e7 100644 --- a/packages/vchart/src/component/axis/polar/axis.ts +++ b/packages/vchart/src/component/axis/polar/axis.ts @@ -2,23 +2,40 @@ import { POLAR_DEFAULT_RADIUS, POLAR_END_RADIAN } from '../../../constant/polar' import type { IBaseScale, BandScale } from '@visactor/vscale'; // eslint-disable-next-line no-duplicate-imports import { isContinuous } from '@visactor/vscale'; -import { ChartEvent, LayoutZIndex, POLAR_START_RADIAN } from '../../../constant'; +import { LayoutZIndex, POLAR_START_RADIAN } from '../../../constant'; import type { IPolarAxis, IPolarAxisCommonSpec } from './interface'; import type { IComponentOption } from '../../interface'; // eslint-disable-next-line no-duplicate-imports import { ComponentTypeEnum } from '../../interface/type'; import { Factory } from '../../../core/factory'; import { eachSeries } from '../../../util/model'; -import { polarToCartesian } from '../../../util/math'; import type { IPolarTickDataOpt } from '@visactor/vrender-components'; // eslint-disable-next-line no-duplicate-imports import type { IPolarSeries } from '../../../series/interface'; -import type { IPoint, IPolarOrientType, IPolarPoint, Datum, StringOrNumber, ILayoutType } from '../../../typings'; +import type { + IPoint, + IPolarOrientType, + IPolarPoint, + Datum, + StringOrNumber, + ILayoutType, + ILayoutNumber +} from '../../../typings'; import { isPolarAxisSeries } from '../../../series/util/utils'; import { getAxisItem, getAxisLabelOffset, isValidPolarAxis } from '../util'; import type { Dict, Maybe } from '@visactor/vutils'; // eslint-disable-next-line no-duplicate-imports -import { PointService, degreeToRadian, isValid, isArray, isValidNumber } from '@visactor/vutils'; +import { + PointService, + degreeToRadian, + isValid, + isArray, + isValidNumber, + isNumber, + isFunction, + calculateMaxRadius, + polarToCartesian +} from '@visactor/vutils'; import type { IEffect, IModelSpecInfo } from '../../../model/interface'; import { AxisComponent } from '../base-axis'; import type { IBandAxisSpec, ITick } from '../interface'; @@ -26,6 +43,7 @@ import { HOOK_EVENT } from '@visactor/vgrammar-core'; import { getPolarAxisInfo } from './util'; // eslint-disable-next-line no-duplicate-imports import { mergeSpec } from '@visactor/vutils-extension'; +import { calcLayoutNumber } from '../../../util/space'; export abstract class PolarAxis extends AxisComponent @@ -45,7 +63,7 @@ export abstract class PolarAxis this.id, getSpec: () => this._spec @@ -323,13 +347,9 @@ export abstract class PolarAxis 0 && height > 0) { + return calculateMaxRadius(layoutRect, this.getCenter(), this._startAngle, this._endAngle); + } + + return Math.min(width / 2, height / 2); + } + private computeLayoutOuterRadius() { /** * 兼容radius旧配置 @@ -512,14 +553,12 @@ export abstract class PolarAxis IPoint; pointToCoord: (point: IPoint) => IPolarPoint; center: () => IPoint; + layoutRadius: () => number; getScale: (depth?: number) => IBaseScale; getBandwidth?: (depth?: number) => number; // band轴特有 diff --git a/packages/vchart/src/component/axis/polar/interface/spec.ts b/packages/vchart/src/component/axis/polar/interface/spec.ts index af5a8ad4ba..3c1382e1f2 100644 --- a/packages/vchart/src/component/axis/polar/interface/spec.ts +++ b/packages/vchart/src/component/axis/polar/interface/spec.ts @@ -1,4 +1,4 @@ -import type { IPoint, IPolarOrientType } from '../../../../typings'; +import type { ILayoutRect, IPoint, IPolarOrientType } from '../../../../typings'; import type { IBandAxisSpec, IDomainLine, ILinearAxisSpec, ITitle, ILabel, ICommonAxisSpec } from '../../interface'; import type { IPolarGrid } from './common'; @@ -6,6 +6,12 @@ import type { IPolarGrid } from './common'; export type IPolarAxisSpec = IPolarLinearAxisSpec | IPolarBandAxisSpec; export type IPolarAxisCommonSpec = Omit & { + /** + * 布局半径,相当于计算内径、外径的基准值 + * 默认值为: region宽度、高度最小值的一般 + * @since 1.11.2 + */ + layoutRadius?: 'auto' | number | ((layoutRect: ILayoutRect, center: IPoint) => number); /** * 当配置了 innerRadius 时,可以通过设置 inside: true,将坐标轴展示在内圆。 * @default false @@ -51,8 +57,9 @@ export type IPolarAxisCommonSpec = Omit & { outerRadius?: number; /** * 中心点 + * @since 1.11.2 x,y 支持百分比的值,如`50%` */ - center?: IPoint; + center?: { x: number | string; y: number | string }; /** * 起始角度 */ diff --git a/packages/vchart/src/component/axis/polar/util/common.ts b/packages/vchart/src/component/axis/polar/util/common.ts index 78b71c0438..5a45d5d35a 100644 --- a/packages/vchart/src/component/axis/polar/util/common.ts +++ b/packages/vchart/src/component/axis/polar/util/common.ts @@ -17,6 +17,7 @@ export const getPolarAxisInfo = (spec: IPolarAxisCommonSpec, chartSpec: any) => endAngle: endAngleFromSpec ?? (isValid(startAngleFromSpec) ? startAngleFromSpec + 360 : POLAR_END_ANGLE), center: chartSpec.center, // 优先使用 outerRadius, 但要兼容 radius - outerRadius: spec.radius ?? chartSpec.outerRadius ?? chartSpec.radius ?? POLAR_DEFAULT_RADIUS + outerRadius: spec.radius ?? chartSpec.outerRadius ?? chartSpec.radius ?? POLAR_DEFAULT_RADIUS, + layoutRadius: chartSpec.layoutRadius }; }; diff --git a/packages/vchart/src/component/marker/mark-line/cartesian-mark-line.ts b/packages/vchart/src/component/marker/mark-line/cartesian-mark-line.ts index a6d9bf5807..f9d0a3749d 100644 --- a/packages/vchart/src/component/marker/mark-line/cartesian-mark-line.ts +++ b/packages/vchart/src/component/marker/mark-line/cartesian-mark-line.ts @@ -14,7 +14,7 @@ import { MarkLine as MarkLineComponent, registerMarkLineAnimate } from '@visactor/vrender-components'; -import { isValid } from '@visactor/vutils'; +import { isValid, isValidNumber } from '@visactor/vutils'; import type { IDataPos, IMarkProcessOptions } from '../interface'; import type { IOptionRegr } from '../../../data/transforms/regression'; import { getInsertPoints, getTextOffset } from './util'; @@ -139,6 +139,19 @@ export class CartesianMarkLine extends BaseMarkLine { refY: 0 }; } + + if (isValidNumber(this._spec.label?.refX)) { + labelPositionAttrs.refX += this._spec.label.refX; + } + if (isValidNumber(this._spec.label?.refY)) { + labelPositionAttrs.refY += this._spec.label.refY; + } + if (isValidNumber(this._spec.label?.dx)) { + labelPositionAttrs.dx = (labelPositionAttrs.dx || 0) + this._spec.label.dx; + } + if (isValidNumber(this._spec.label?.dy)) { + labelPositionAttrs.dy = (labelPositionAttrs.dy || 0) + this._spec.label.dy; + } const markerComponentAttr = this._markerComponent?.attribute ?? {}; this._markerComponent?.setAttributes({ points: multiSegment diff --git a/packages/vchart/src/mark/arc.ts b/packages/vchart/src/mark/arc.ts index c294870620..0182d677dc 100644 --- a/packages/vchart/src/mark/arc.ts +++ b/packages/vchart/src/mark/arc.ts @@ -1,7 +1,6 @@ import { Factory } from './../core/factory'; import { ARC_MIDDLE_ANGLE } from '../constant'; import type { IArcMarkSpec, Datum, StateValueType } from '../typings'; -import { polarToCartesian } from '../util/math'; import type { ExChannelCall } from './base/base-mark'; // eslint-disable-next-line no-duplicate-imports import { BaseMark } from './base/base-mark'; @@ -10,7 +9,7 @@ import type { IMarkOption, IMarkRaw, IMarkStyle } from './interface'; import { MarkTypeEnum } from './interface/type'; import { registerArcGraphic } from '@visactor/vgrammar-core'; import { registerVGrammarArcAnimation } from '../animation/config'; -import { isValid } from '@visactor/vutils'; +import { polarToCartesian } from '@visactor/vutils'; export type IArcMark = IMarkRaw; @@ -64,11 +63,13 @@ export class BaseArcMark extends BaseMark implements opt: any, center: number ) => { - const offset = polarToCartesian({ - angle: datum[ARC_MIDDLE_ANGLE], - radius: this.getAttribute('centerOffset', datum, states, opt) as number - }); - return center + offset[key]; + return ( + polarToCartesian( + { x: 0, y: 0 }, + this.getAttribute('centerOffset', datum, states, opt) as number, + datum[ARC_MIDDLE_ANGLE] + )[key] + center + ); }; } diff --git a/packages/vchart/src/series/pie/animation/centerOffset.ts b/packages/vchart/src/series/pie/animation/centerOffset.ts index 3796f5e1a7..832639cece 100644 --- a/packages/vchart/src/series/pie/animation/centerOffset.ts +++ b/packages/vchart/src/series/pie/animation/centerOffset.ts @@ -1,9 +1,8 @@ import type { IAnimationTimeline, IAnimationTypeConfig } from '@visactor/vgrammar-core'; -import { polarToCartesian } from '../../../util/math'; import { ARC_MIDDLE_ANGLE } from '../../../constant'; import type { IArcMark } from '../../../mark/arc'; import type { Datum } from '../../../typings'; -import { isValidNumber } from '@visactor/vutils'; +import { isValidNumber, polarToCartesian } from '@visactor/vutils'; export type ICenterOffsetAnimationOptions = { distance?: number; @@ -26,10 +25,14 @@ export function centerOffsetConfig(mark: IArcMark, originalConfig: IAnimationTyp from: (datum: Datum) => mark.getAttribute('x', datum), to: (datum: Datum) => { const center = mark.getAttribute('x', datum) as number; - const point = polarToCartesian({ - angle: datum[ARC_MIDDLE_ANGLE], - radius: offset - }); + const point = polarToCartesian( + { + x: 0, + y: 0 + }, + offset, + datum[ARC_MIDDLE_ANGLE] + ); return center + point.x; } }, @@ -37,10 +40,14 @@ export function centerOffsetConfig(mark: IArcMark, originalConfig: IAnimationTyp from: (datum: Datum) => mark.getAttribute('y', datum), to: (datum: Datum) => { const center = mark.getAttribute('y', datum) as number; - const point = polarToCartesian({ - angle: datum[ARC_MIDDLE_ANGLE], - radius: offset - }); + const point = polarToCartesian( + { + x: 0, + y: 0 + }, + offset, + datum[ARC_MIDDLE_ANGLE] + ); return center + point.y; } } @@ -56,10 +63,14 @@ export function centerOffsetConfig(mark: IArcMark, originalConfig: IAnimationTyp to: (datum: Datum) => mark.getAttribute('x', datum), from: (datum: Datum) => { const center = mark.getAttribute('x', datum) as number; - const point = polarToCartesian({ - angle: datum[ARC_MIDDLE_ANGLE], - radius: offset - }); + const point = polarToCartesian( + { + x: 0, + y: 0 + }, + offset, + datum[ARC_MIDDLE_ANGLE] + ); return center + point.x; } }, @@ -67,10 +78,14 @@ export function centerOffsetConfig(mark: IArcMark, originalConfig: IAnimationTyp to: (datum: Datum) => mark.getAttribute('y', datum), from: (datum: Datum) => { const center = mark.getAttribute('y', datum) as number; - const point = polarToCartesian({ - angle: datum[ARC_MIDDLE_ANGLE], - radius: offset - }); + const point = polarToCartesian( + { + x: 0, + y: 0 + }, + offset, + datum[ARC_MIDDLE_ANGLE] + ); return center + point.y; } } diff --git a/packages/vchart/src/series/pie/pie.ts b/packages/vchart/src/series/pie/pie.ts index 8db45a733a..7aa5754b3a 100644 --- a/packages/vchart/src/series/pie/pie.ts +++ b/packages/vchart/src/series/pie/pie.ts @@ -19,7 +19,7 @@ import { DEFAULT_DATA_KEY } from '../../constant'; import type { IPoint, Datum, StateValueType } from '../../typings'; -import { normalizeStartEndAngle, polarToCartesian } from '../../util/math'; +import { normalizeStartEndAngle } from '../../util/math'; import { isSpecValueWithScale } from '../../util/scale'; import { field } from '../../util/object'; import type { IModelLayoutOption } from '../../model/interface'; @@ -47,7 +47,7 @@ import { centerOffsetConfig } from './animation/centerOffset'; import { registerArcMark } from '../../mark/arc'; import { pieSeriesMark } from './constant'; import { Factory } from '../../core/factory'; -import { isNil } from '@visactor/vutils'; +import { isNil, polarToCartesian } from '@visactor/vutils'; import { PieSeriesSpecTransformer } from './pie-transformer'; export class BasePieSeries extends PolarSeries implements IArcSeries { @@ -195,11 +195,11 @@ export class BasePieSeries extends PolarSeries fill: this.getColorAttribute(), outerRadius: isSpecValueWithScale(this._outerRadius) ? this._outerRadius - : () => this.computeLayoutRadius() * this._outerRadius, + : () => this._computeLayoutRadius() * this._outerRadius, innerRadius: isSpecValueWithScale(this._innerRadius) ? this._innerRadius - : () => this.computeLayoutRadius() * this._innerRadius, - cornerRadius: () => this.computeLayoutRadius() * this._cornerRadius, + : () => this._computeLayoutRadius() * this._innerRadius, + cornerRadius: () => this._computeLayoutRadius() * this._cornerRadius, startAngle: datum => this.startAngleScale(datum), endAngle: datum => this.endAngleScale(datum), padAngle: this._padAngle, @@ -289,17 +289,12 @@ export class BasePieSeries extends PolarSeries return; } const style: any = {}; - spec.outerRadius && (style.outerRadius = () => this.computeLayoutRadius() * spec.outerRadius); - spec.innerRadius && (style.innerRadius = () => this.computeLayoutRadius() * spec.innerRadius); - spec.cornerRadius && (style.cornerRadius = () => this.computeLayoutRadius() * spec.cornerRadius); + spec.outerRadius && (style.outerRadius = () => this._computeLayoutRadius() * spec.outerRadius); + spec.innerRadius && (style.innerRadius = () => this._computeLayoutRadius() * spec.innerRadius); + spec.cornerRadius && (style.cornerRadius = () => this._computeLayoutRadius() * spec.cornerRadius); return style; } - protected computeLayoutRadius() { - const { width, height } = this._region.getLayoutRect(); - return Math.min(width / 2, height / 2); - } - computeCenter(datum: Datum): IPoint { return { x: this._pieMark.getAttribute('x', datum, 'normal') as number, @@ -324,11 +319,11 @@ export class BasePieSeries extends PolarSeries } computeRadius(r: number, k?: number): number { - return this.computeLayoutRadius() * r * (isNil(k) ? 1 : k) + this._centerOffset; + return this._computeLayoutRadius() * r * (isNil(k) ? 1 : k) + this._centerOffset; } computeDatumRadius(datum: Datum, state?: string): number { - return this.computeLayoutRadius() * this.getRadius(state) + this._centerOffset; + return this._computeLayoutRadius() * this.getRadius(state) + this._centerOffset; } _compareSpec(spec: T, prevSpec: T, ignoreCheckKeys?: { [key: string]: true }) { @@ -365,7 +360,7 @@ export class BasePieSeries extends PolarSeries } computeDatumInnerRadius(datum: Datum, state?: string): number { - return this.computeLayoutRadius() * this.getInnerRadius(state) + this._centerOffset; + return this._computeLayoutRadius() * this.getInnerRadius(state) + this._centerOffset; } dataToPosition(datum: Datum, checkInViewData?: boolean): IPoint | null { @@ -378,8 +373,8 @@ export class BasePieSeries extends PolarSeries } const radius = this.computeDatumRadius(datum); const center = this.computeCenter(datum); - const delta = polarToCartesian({ angle, radius }); - return { x: center.x + delta.x, y: center.y + delta.y }; + + return polarToCartesian(center, radius, angle); } dataToCentralPosition = (datum: Datum): IPoint | null => { @@ -391,8 +386,8 @@ export class BasePieSeries extends PolarSeries const radius = this.computeDatumRadius(datum); const innerRadius = this.computeDatumInnerRadius(datum); const center = this.computeCenter(datum); - const delta = polarToCartesian({ angle, radius: (radius + innerRadius) / 2 }); - return { x: center.x + delta.x, y: center.y + delta.y }; + + return polarToCartesian(center, (radius + innerRadius) / 2, angle); }; initAnimation() { diff --git a/packages/vchart/src/series/polar/polar.ts b/packages/vchart/src/series/polar/polar.ts index a06790d157..926d5b13f2 100644 --- a/packages/vchart/src/series/polar/polar.ts +++ b/packages/vchart/src/series/polar/polar.ts @@ -214,6 +214,12 @@ export abstract class PolarSeries } protected _computeLayoutRadius() { + const axisHelper = this._angleAxisHelper || this._radiusAxisHelper; + + if (axisHelper) { + return axisHelper.layoutRadius(); + } + const { width, height } = this._region.getLayoutRect(); return Math.min(width / 2, height / 2); } diff --git a/packages/vchart/src/series/sunburst/sunburst.ts b/packages/vchart/src/series/sunburst/sunburst.ts index 5fc3e1f3c9..8e8af09258 100644 --- a/packages/vchart/src/series/sunburst/sunburst.ts +++ b/packages/vchart/src/series/sunburst/sunburst.ts @@ -384,12 +384,10 @@ export class SunburstSeries extends PolarSeries { private _computeRadius(radius: number | number[]) { if (isArray(radius)) { return radius.map(r => { - const { width, height } = this.getRegion().getLayoutRect(); - return Math.min(width / 2, height / 2) * r; + return this._computeLayoutRadius() * r; }); } - const { width, height } = this.getRegion().getLayoutRect(); - return Math.min(width / 2, height / 2) * radius; + return this._computeLayoutRadius() * radius; } private _computeLevel(): { diff --git a/packages/vchart/src/util/math.ts b/packages/vchart/src/util/math.ts index 4e24a9e6e4..56fa6ae439 100644 --- a/packages/vchart/src/util/math.ts +++ b/packages/vchart/src/util/math.ts @@ -1,5 +1,5 @@ import type { IBoundsLike } from '@visactor/vutils'; -import type { IPoint, IPolarPoint, Quadrant, TextAlign, TextBaseLine } from '../typings'; +import type { IPoint, Quadrant, TextAlign, TextBaseLine } from '../typings'; import type { Datum } from '@visactor/vgrammar-core'; import { isValidNumber } from './type'; import { regressionLinear } from '@visactor/vgrammar-util'; @@ -18,21 +18,6 @@ import { angleLabelOrientAttribute } from '@visactor/vrender-components'; export const isClose = isNumberClose; export { isGreater, isLess, normalizeAngle, angleLabelOrientAttribute }; -/** - * 极坐标系 -> 直角坐标系 - * @param point - * @returns - */ -export function polarToCartesian(point: IPolarPoint): IPoint { - if (!point.radius) { - return { x: 0, y: 0 }; - } - return { - x: Math.cos(point.angle) * point.radius, - y: Math.sin(point.angle) * point.radius - }; -} - /** * 根据角度计算象限 * 计算角度所在象限 @@ -96,6 +81,7 @@ export function normalizeStartEndAngle( export function outOfBounds(bounds: IBoundsLike, x: number, y: number) { return bounds.x1 > x || bounds.x2 < x || bounds.y1 > y || bounds.y2 < y; } + export function min(data: any[], field?: string): number { const dataArray: any[] = []; data.forEach(d => { diff --git a/packages/vchart/src/util/space.ts b/packages/vchart/src/util/space.ts index a55c3c8669..77501b882c 100644 --- a/packages/vchart/src/util/space.ts +++ b/packages/vchart/src/util/space.ts @@ -48,7 +48,8 @@ export function isPercentOffset(v: any): v is IPercentOffset { export function calcLayoutNumber( v: ILayoutNumber | undefined, size: number, - callOp?: ILayoutRect //如果是函数类型的话,函数的参数 + callOp?: ILayoutRect, //如果是函数类型的话,函数的参数 + defaultValue: number = 0 ) { if (isNumber(v)) { return v; @@ -62,7 +63,7 @@ export function calcLayoutNumber( if (isObject(v)) { return size * (v.percent ?? 0) + (v.offset ?? 0); } - return 0; + return defaultValue; } export function calcPadding(