diff --git a/__tests__/unit/plots/gauge/style-spec.ts b/__tests__/unit/plots/gauge/style-spec.ts new file mode 100644 index 0000000000..392fce18cf --- /dev/null +++ b/__tests__/unit/plots/gauge/style-spec.ts @@ -0,0 +1,56 @@ +import { Gauge } from '../../../../src'; +import { createDiv } from '../../../utils/dom'; + +describe('gauge', () => { + const gauge = new Gauge(createDiv(), { + width: 600, + height: 300, + autoFit: false, + percent: 0.75, + range: { + ticks: [0, 0.2, 0.4, 0.75, 1], + color: ['red', 'yellow', 'green'], + }, + indicator: {}, + gaugeStyle: { + lineCap: 'round', + lineWidth: 2, + stroke: 'red', + }, + }); + + gauge.render(); + + it('gaugeStyle', async () => { + const [, v2] = gauge.chart.views; + expect(v2.geometries[0].type).toBe('interval'); + // v2 gaugeStyle 生效 + expect(v2.geometries[0].elements[0].shape.attr('lineCap')).toBe('round'); + expect(v2.geometries[0].elements[0].shape.attr('lineWidth')).toBe(2); + expect(v2.geometries[0].elements[0].shape.attr('stroke')).toBe('red'); + }); + + it('gaugeStyle, with callback', () => { + gauge.update({ + percent: 0.9, + gaugeStyle: ({ percent }) => { + return { + lineCap: 'round', + lineWidth: 2, + stroke: percent > 0.8 ? 'green' : 'red', + }; + }, + }); + + const [, v2] = gauge.chart.views; + expect(v2.geometries[0].type).toBe('interval'); + // v2 gaugeStyle 生效 + expect(v2.geometries[0].elements[0].shape.attr('lineCap')).toBe('round'); + expect(v2.geometries[0].elements[0].shape.attr('lineWidth')).toBe(2); + expect(v2.geometries[0].elements[0].shape.attr('stroke')).toBe('green'); + }); + + afterAll(() => { + gauge.destroy(); + }); +}); diff --git a/docs/api/plots/gauge.en.md b/docs/api/plots/gauge.en.md index ea0026fb51..91da6bc78b 100644 --- a/docs/api/plots/gauge.en.md +++ b/docs/api/plots/gauge.en.md @@ -73,6 +73,16 @@ It works when `type = 'meter'`. Properties are as followed: gauge +#### gaugeStyle + +**optional** _StyleAttr | Function_ + +Gauge graphic style. + +`markdown:docs/common/shape-style.en.md` + +### Plot Components + #### axis **optional** _object_ diff --git a/docs/api/plots/gauge.zh.md b/docs/api/plots/gauge.zh.md index 74172a6bc6..d3c47e6928 100644 --- a/docs/api/plots/gauge.zh.md +++ b/docs/api/plots/gauge.zh.md @@ -72,6 +72,14 @@ order: 5 gauge +#### gaugeStyle + +**optional** _StyleAttr | Function_ + +仪表盘的样式设置。 + +`markdown:docs/common/shape-style.zh.md` + ### 图表组件 #### axis diff --git a/src/plots/gauge/adaptor.ts b/src/plots/gauge/adaptor.ts index 0129ccb014..9edfc06751 100644 --- a/src/plots/gauge/adaptor.ts +++ b/src/plots/gauge/adaptor.ts @@ -1,8 +1,8 @@ import { isString } from '@antv/util'; import { interaction, animation, theme, scale, annotation } from '../../adaptor/common'; +import { interval } from '../../adaptor/geometries'; import { AXIS_META_CONFIG_KEYS } from '../../constant'; import { Params } from '../../core/adaptor'; -import { Data } from '../../types'; import { deepAssign, flow, pick, renderGaugeStatistic } from '../../utils'; import { RANGE_TYPE, @@ -13,7 +13,7 @@ import { RANGE_VIEW_ID, MASK_VIEW_ID, } from './constants'; -import { GaugeOptions } from './types'; +import { GaugeCustomInfo, GaugeOptions } from './types'; import { getIndicatorData, getRangeData } from './utils'; /** @@ -22,7 +22,7 @@ import { getIndicatorData, getRangeData } from './utils'; */ function geometry(params: Params): Params { const { chart, options } = params; - const { percent, range, radius, innerRadius, startAngle, endAngle, axis, indicator } = options; + const { percent, range, radius, innerRadius, startAngle, endAngle, axis, indicator, gaugeStyle } = options; const { color } = range; // 指标 & 指针 @@ -54,14 +54,30 @@ function geometry(params: Params): Params { } // 辅助 range - // [{ range: 1, type: '0' }] - const rangeData: Data = getRangeData(percent, options.range); + // [{ range: 1, type: '0', percent: 原始进度百分比 }] + const rangeData = getRangeData(percent, options.range); const v2 = chart.createView({ id: RANGE_VIEW_ID }); v2.data(rangeData); const rangeColor = isString(color) ? [color, DEFAULT_COLOR] : color; - v2.interval().position(`1*${RANGE_VALUE}`).color(RANGE_TYPE, rangeColor).adjust('stack'); + interval({ + chart: v2, + options: { + xField: '1', + yField: RANGE_VALUE, + seriesField: RANGE_TYPE, + rawFields: [PERCENT], + isStack: true, + interval: { + color: rangeColor, + style: gaugeStyle, + }, + args: { + zIndexReversed: true, + }, + }, + }); v2.coordinate('polar', { innerRadius, @@ -93,12 +109,8 @@ function meterView(params: Params): Params { const v3 = chart.createView({ id: MASK_VIEW_ID }); v3.data([{ [RANGE_TYPE]: '1', [RANGE_VALUE]: 1 }]); - v3.interval() - .position(`1*${RANGE_VALUE}`) - .color(color) - .adjust('stack') - .shape('meter-gauge') - .customInfo(meter || {}); + const customInfo: GaugeCustomInfo = { meter }; + v3.interval().position(`1*${RANGE_VALUE}`).color(color).adjust('stack').shape('meter-gauge').customInfo(customInfo); v3.coordinate('polar', { innerRadius, radius, startAngle, endAngle }).transpose(); } diff --git a/src/plots/gauge/shapes/meter-gauge.ts b/src/plots/gauge/shapes/meter-gauge.ts index a0c641e550..db1dab0877 100644 --- a/src/plots/gauge/shapes/meter-gauge.ts +++ b/src/plots/gauge/shapes/meter-gauge.ts @@ -1,11 +1,16 @@ import { registerShape, Types, Util } from '@antv/g2'; -import { getSectorPath } from '@antv/g2/lib/util/graphics'; +import { GaugeCustomInfo } from '../types'; + +type ShapeCfg = Omit & { + customInfo: GaugeCustomInfo; +}; // 自定义Shape 部分 registerShape('interval', 'meter-gauge', { - draw(cfg: Types.ShapeInfo, container) { + draw(cfg: ShapeCfg, container) { // 使用 customInfo 传递参数 - const { steps: STEP = 50, stepRatio = 0.5 } = cfg.customInfo; + const { meter = {} } = cfg.customInfo; + const { steps: STEP = 50, stepRatio = 0.5 } = meter; const total = this.coordinate.endAngle - this.coordinate.startAngle; let interval = total / STEP; @@ -30,7 +35,7 @@ registerShape('interval', 'meter-gauge', { for (let i = startAngle, j = 0; i < endAngle && j < 2 * STEP - 1; j++) { const drawn = j % 2; if (drawn) { - const path = getSectorPath( + const path = Util.getSectorPath( center.x, center.y, radius, diff --git a/src/plots/gauge/types.ts b/src/plots/gauge/types.ts index fc84a77e5f..420f9d3ec9 100644 --- a/src/plots/gauge/types.ts +++ b/src/plots/gauge/types.ts @@ -1,5 +1,6 @@ -import { Options, ShapeStyle, Statistic } from '../../types'; +import { Options, ShapeStyle, Statistic, StyleAttr } from '../../types'; import { Axis } from '../../types/axis'; +import { PERCENT, RANGE_TYPE, RANGE_VALUE } from './constants'; /** 指标指标的配置 */ export type Indicator = { @@ -20,6 +21,15 @@ type Range = { readonly color?: string | string[]; }; +/** + * 仪表盘辅助生成的 rangeData + */ +export type GaugeRangeData = { + readonly [RANGE_VALUE]?: number; + readonly [RANGE_TYPE]: string; + readonly [PERCENT]: number; +}[]; + /** 仪表盘配置类型定义 */ export interface GaugeOptions extends Omit { @@ -41,6 +51,8 @@ export interface GaugeOptions readonly indicator?: false | Indicator; /** 统计文本 */ readonly statistic?: Statistic; + /** 仪表盘样式 */ + readonly gaugeStyle?: StyleAttr; // meter gauge 相关配置 /** 仪表盘类型, 可选项: 'meter', default 为空 */ @@ -53,3 +65,11 @@ export interface GaugeOptions readonly stepRatio?: number; }; } + +/** + * 仪表盘 自定义 shape 使用的 customInfo + */ +export type GaugeCustomInfo = { + /** 仪表盘 meter 类型的相关配置 */ + readonly meter?: GaugeOptions['meter']; +}; diff --git a/src/plots/gauge/utils.ts b/src/plots/gauge/utils.ts index fc2d7854b8..e028e3c4bd 100644 --- a/src/plots/gauge/utils.ts +++ b/src/plots/gauge/utils.ts @@ -1,19 +1,20 @@ import { clamp, get, size } from '@antv/util'; import { Data, Datum } from '../../types'; import { RANGE_VALUE, RANGE_TYPE, PERCENT } from './constants'; -import { GaugeOptions } from './types'; +import { GaugeOptions, GaugeRangeData } from './types'; /** * 将 range 生成为 data 数据 * @param range * @param key + * @returns {GaugeRangeData} */ -export function processRangeData(range: number[]): Data { +export function processRangeData(range: number[], percent: GaugeOptions['percent']): GaugeRangeData { return ( range // 映射为 stack 的数据 .map((r: number, idx: number) => { - return { [RANGE_VALUE]: r - (range[idx - 1] || 0), [RANGE_TYPE]: `${idx}` }; + return { [RANGE_VALUE]: r - (range[idx - 1] || 0), [RANGE_TYPE]: `${idx}`, [PERCENT]: percent }; }) // 去掉 0 的数据 .filter((d: Datum) => !!d[RANGE_VALUE]) @@ -33,9 +34,9 @@ export function getIndicatorData(percent: GaugeOptions['percent']): Data { * @param percent * @param range */ -export function getRangeData(percent: GaugeOptions['percent'], range?: GaugeOptions['range']): Data { +export function getRangeData(percent: GaugeOptions['percent'], range?: GaugeOptions['range']): GaugeRangeData { const ticks = get(range, ['ticks'], []); const clampTicks = size(ticks) ? ticks : [0, clamp(percent, 0, 1), 1]; - return processRangeData(clampTicks as number[]); + return processRangeData(clampTicks as number[], percent); }