diff --git a/__tests__/integration/snapshots/static/gaugeRoundShape.svg b/__tests__/integration/snapshots/static/gaugeRoundShape.svg new file mode 100644 index 0000000000..b2f1697c8c --- /dev/null +++ b/__tests__/integration/snapshots/static/gaugeRoundShape.svg @@ -0,0 +1,506 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + + + + 100 + + + + + + + 200 + + + + + + + 300 + + + + + + + 400 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 得分:159 + + + 占比:39.75% + + + + + + + + + + + + + + \ No newline at end of file diff --git a/__tests__/plots/static/gauge-round-shape.ts b/__tests__/plots/static/gauge-round-shape.ts new file mode 100644 index 0000000000..025b70bd94 --- /dev/null +++ b/__tests__/plots/static/gauge-round-shape.ts @@ -0,0 +1,29 @@ +import { G2Spec } from '../../../src'; + +export function gaugeRoundShape(): G2Spec { + return { + type: 'gauge', + data: { + value: { + target: 159, + total: 400, + name: 'score', + thresholds: [100, 200, 400], + }, + }, + scale: { + color: { + range: ['#F4664A', '#FAAD14', 'green'], + }, + }, + legend: false, + style: { + arcShape: 'round', + arcLineWidth: 2, + arcStroke: '#fff', + textContent: (target, total) => { + return `得分:${target}\n占比:${(target / total) * 100}%`; + }, + }, + }; +} diff --git a/__tests__/plots/static/index.ts b/__tests__/plots/static/index.ts index 8d3ecfe4fb..cb15d80681 100644 --- a/__tests__/plots/static/index.ts +++ b/__tests__/plots/static/index.ts @@ -216,6 +216,7 @@ export { soldIntervalCustomShape } from './sold-interval-custom-shape'; export { gaugeDefault } from './gauge-default'; export { gaugeCustomColor } from './gauge-custom-color'; export { gaugeCustomShape } from './gauge-custom-shape'; +export { gaugeRoundShape } from './gauge-round-shape'; export { scoreByItemAreaRadarSize } from './score-by-item-area-radar-size'; export { mockPointLogTicks } from './mock-point-log-ticks'; export { alphabetIntervalLabelRotate } from './alphabet-interval-label-rotate'; diff --git a/site/examples/general/gauge/demo/gauge-round.ts b/site/examples/general/gauge/demo/gauge-round.ts new file mode 100644 index 0000000000..a9801ba002 --- /dev/null +++ b/site/examples/general/gauge/demo/gauge-round.ts @@ -0,0 +1,32 @@ +import { Chart } from '@antv/g2'; + +const chart = new Chart({ + container: 'container', + autoFit: true, +}); + +chart + .gauge() + .data({ + value: { + target: 159, + total: 400, + name: 'score', + thresholds: [100, 200, 400], + }, + }) + .scale('color', { + range: ['#F4664A', '#FAAD14', 'green'], + }) + .style({ + arcShape: 'round', + arcLineWidth: 2, + arcStroke: '#fff', + }) + .style( + 'textContent', + (target, total) => `得分:${target}\n占比:${(target / total) * 100}%`, + ) + .legend(false); + +chart.render(); diff --git a/site/examples/general/gauge/demo/meta.json b/site/examples/general/gauge/demo/meta.json index e364babcb5..4ed9dd9ebc 100644 --- a/site/examples/general/gauge/demo/meta.json +++ b/site/examples/general/gauge/demo/meta.json @@ -19,6 +19,14 @@ "en": "Custom Color Gauge Chart" }, "screenshot": "https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*vE7cR6ZHHk0AAAAAAAAAAAAADmJ7AQ/original" + }, + { + "filename": "gauge-round.ts", + "title": { + "zh": "仪表盘内置 round 形状", + "en": "Round Shape Gauge Chart" + }, + "screenshot": "https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*_tUeQ64QNVEAAAAAAAAAAAAADmJ7AQ/original" } ] } diff --git a/src/mark/gauge.ts b/src/mark/gauge.ts index f05b09c961..7abd57804d 100644 --- a/src/mark/gauge.ts +++ b/src/mark/gauge.ts @@ -9,6 +9,7 @@ import { getTransformOptions } from '../utils/coordinate'; import { Radial } from '../coordinate'; import { applyStyle, getOrigin } from '../shape/utils'; import { select } from '../utils/selection'; +import { GaugeRound } from '../shape'; const indicatorShape: SC = (options, context) => { const { shape, radius, ...style } = options; @@ -215,13 +216,16 @@ export const Gauge: CC = (options) => { // pointer + pin const indicatorStyle = filterPrefixObject(style, ['pointer', 'pin']); + const arcStyle = subObject(style, 'arc'); + const shape = arcStyle.shape; + return [ deepMix({}, DEFAULT_OPTIONS, { type: 'interval', transform: [{ type: 'stackY' }], data: totalData, scale: newScale, - style: subObject(style, 'arc'), + style: shape === 'round' ? { ...arcStyle, shape: GaugeRound } : arcStyle, animate: typeof animate === 'object' ? subObject(animate, 'arc') : animate, ...resOptions, diff --git a/src/shape/gauge/round.ts b/src/shape/gauge/round.ts new file mode 100644 index 0000000000..f444e16c4a --- /dev/null +++ b/src/shape/gauge/round.ts @@ -0,0 +1,57 @@ +import { omit } from '@antv/util'; +import type { Vector2, ShapeComponent as SC } from '../../runtime'; + +export type RoundOptions = Record; + +// Get point1 point2 radius. +const getR = (point1, point2) => { + return ( + Math.sqrt( + Math.pow(point1[0] - point2[0], 2) + Math.pow(point1[1] - point2[1], 2), + ) / 2 + ); +}; + +// Gauge round. +export const Round: SC = (options, context) => { + if (!context) return; + const { coordinate } = context; + if (!coordinate?.getCenter) return; + // Get coordinate center point. + const center = coordinate.getCenter() as Vector2; + + return (points, cfg, defaultCfg) => { + const { document } = context.canvas; + const { color, index } = cfg; + + const g = document.createElement('g', {}); + + const minR = getR(points[0], points[1]); + const maxR = getR(points[0], center) * 2; + + /** + * MinR small circle radius, maxR big circle radius. + * Draw four arcs. + * Style lineWidth and stroke for the time being inset. + */ + const roundPath = document.createElement('path', { + style: { + path: [ + ['M', ...points[0]], + ['A', minR, minR, 0, 1, 0, ...points[1]], + ['A', maxR + minR * 2, maxR + minR * 2, 0, 0, 0, ...points[2]], + ['A', minR, minR, 0, 1, index === 0 ? 0 : 1, ...points[3]], + ['A', maxR, maxR, 0, 0, 1, ...points[0]], + ['Z'], + ], + ...defaultCfg, + ...omit(options, ['shape', 'last', 'first']), + fill: color || defaultCfg.color, + }, + }); + + g.appendChild(roundPath); + + return g; + }; +}; diff --git a/src/shape/index.ts b/src/shape/index.ts index 6c466ab5f2..ba161d9457 100644 --- a/src/shape/index.ts +++ b/src/shape/index.ts @@ -58,6 +58,7 @@ export { Rect as RectShape } from './interval/rect'; export { Hollow as RectHollow } from './interval/hollow'; export { Shape as ShapeShape } from './shape/shape'; export { Liquid as LiquidShape } from './liquid/liquid'; +export { Round as GaugeRound } from './gauge/round'; export type { RectOptions as IntervalShapeOptions } from './interval/rect'; export type { HollowOptions as IntervalHollowOptions } from './interval/hollow';