From 62c90b9f18a8137e557edaf7cd86efeb29fb7189 Mon Sep 17 00:00:00 2001 From: arcsin1 Date: Thu, 23 Jul 2020 15:07:22 +0800 Subject: [PATCH 1/5] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9E=E5=9F=BA?= =?UTF-8?q?=E6=9C=AC=E7=9B=B4=E6=96=B9=E5=9B=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- __tests__/data/histogram-data.ts | 56 +++++++++ __tests__/unit/plots/histogram/index-spec.ts | 91 ++++++++++++++ src/index.ts | 3 + src/plots/histogram/adaptor.ts | 124 +++++++++++++++++++ src/plots/histogram/index.ts | 18 +++ src/plots/histogram/types.ts | 30 +++++ src/plots/histogram/util.ts | 9 ++ 7 files changed, 331 insertions(+) create mode 100644 __tests__/data/histogram-data.ts create mode 100644 __tests__/unit/plots/histogram/index-spec.ts create mode 100644 src/plots/histogram/adaptor.ts create mode 100644 src/plots/histogram/index.ts create mode 100644 src/plots/histogram/types.ts create mode 100644 src/plots/histogram/util.ts diff --git a/__tests__/data/histogram-data.ts b/__tests__/data/histogram-data.ts new file mode 100644 index 0000000000..322b31d5b4 --- /dev/null +++ b/__tests__/data/histogram-data.ts @@ -0,0 +1,56 @@ +export const histogramData = [ + { value: 1.2 }, + { value: 3.4 }, + { value: 3.7 }, + { value: 4.3 }, + { value: 5.2 }, + { value: 5.8 }, + { value: 6.1 }, + { value: 6.5 }, + { value: 6.8 }, + { value: 7.1 }, + { value: 7.3 }, + { value: 7.7 }, + { value: 8.3 }, + { value: 8.6 }, + { value: 8.8 }, + { value: 9.1 }, + { value: 9.2 }, + { value: 9.4 }, + { value: 9.5 }, + { value: 9.7 }, + { value: 10.5 }, + { value: 10.7 }, + { value: 10.8 }, + { value: 11.0 }, + { value: 11.0 }, + { value: 11.1 }, + { value: 11.2 }, + { value: 11.3 }, + { value: 11.4 }, + { value: 11.4 }, + { value: 11.7 }, + { value: 12.0 }, + { value: 12.9 }, + { value: 12.9 }, + { value: 13.3 }, + { value: 13.7 }, + { value: 13.8 }, + { value: 13.9 }, + { value: 14.0 }, + { value: 14.2 }, + { value: 14.5 }, + { value: 15 }, + { value: 15.2 }, + { value: 15.6 }, + { value: 16.0 }, + { value: 16.3 }, + { value: 17.3 }, + { value: 17.5 }, + { value: 17.9 }, + { value: 18.0 }, + { value: 18.0 }, + { value: 20.6 }, + { value: 21 }, + { value: 23.4 }, +]; diff --git a/__tests__/unit/plots/histogram/index-spec.ts b/__tests__/unit/plots/histogram/index-spec.ts new file mode 100644 index 0000000000..7b1bae13b9 --- /dev/null +++ b/__tests__/unit/plots/histogram/index-spec.ts @@ -0,0 +1,91 @@ +import { Histogram } from '../../../../src'; +import { histogramData } from '../../../data/histogram-data'; +import { createDiv } from '../../../utils/dom'; + +describe('histogram', () => { + it('binField', () => { + const histogram = new Histogram(createDiv(), { + width: 400, + height: 300, + appendPadding: 10, + data: histogramData, + binField: 'value', + }); + + histogram.render(); + + const geometry = histogram.chart.geometries[0]; + const elements = geometry.elements; + expect(elements.length); + }); + + it('binField with binWidth', () => { + const histogram = new Histogram(createDiv(), { + width: 400, + height: 300, + appendPadding: 10, + data: histogramData, + binWidth: 2, + binField: 'value', + }); + + histogram.render(); + + const geometry = histogram.chart.geometries[0]; + const elements = geometry.elements; + expect(elements.length); + }); + it('binField with binNumber', () => { + const histogram = new Histogram(createDiv(), { + width: 400, + height: 300, + appendPadding: 10, + data: histogramData, + binNumber: 4, + binField: 'value', + }); + + histogram.render(); + + const geometry = histogram.chart.geometries[0]; + const elements = geometry.elements; + expect(elements.length); + }); + + it('binField with color,binWidth', () => { + const histogram = new Histogram(createDiv(), { + width: 400, + height: 300, + appendPadding: 10, + data: histogramData, + binField: 'value', + binWidth: 2, + color: 'red', + }); + + histogram.render(); + + const geometry = histogram.chart.geometries[0]; + const elements = geometry.elements; + // @ts-ignore + expect(elements[0].getModel().color).toBe('red'); + }); + it('binField with interaction', () => { + const histogram = new Histogram(createDiv(), { + width: 400, + height: 300, + appendPadding: 10, + data: histogramData, + binField: 'value', + binWidth: 2, + interaction: 'element-highlight', + }); + + histogram.render(); + + const geometry = histogram.chart.geometries[0]; + const elements = geometry.elements; + // @ts-ignore + expect(elements.length); + }); +}); diff --git a/src/index.ts b/src/index.ts index 5ce9654c78..9a73aab45c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -20,3 +20,6 @@ export { TinyColumn, TinyColumnOptions } from './plots/tiny-column'; // 迷你面积图及类型定义 export { TinyArea, TinyAreaOptions } from './plots/tiny-area'; + +// 直方图及类型定义 +export { Histogram, HistogramOptions } from './plots/histogram'; diff --git a/src/plots/histogram/adaptor.ts b/src/plots/histogram/adaptor.ts new file mode 100644 index 0000000000..567c468c4c --- /dev/null +++ b/src/plots/histogram/adaptor.ts @@ -0,0 +1,124 @@ +import { Params } from '../../core/adaptor'; +import { flow } from '../../utils'; +import { StatisticBin, StatisticData, HistogramOptions } from './types'; +import { clone, sortBy, valuesOfKey, getRange, each, hasKey } from '@antv/util'; +import { getBinKey, sturges } from './util'; + +export /** + * 字段 + * @param params + */ +function field(params: Params): Params { + const { chart, options } = params; + const { data, binField, binNumber, binWidth, color } = options; + + // 直方图操作逻辑 + const originData_copy = clone(data); + // 根据binField value对源数据进行排序 + sortBy(originData_copy, binField); + + // 获取源数据binField values的range + const values = valuesOfKey(originData_copy, binField); + const range = getRange(values); + const rangeWidth = range.max - range.min; + + // 计算分箱,直方图分箱的计算基于binWidth,如配置了binNumber则将其转为binWidth进行计算 + let _binWidth = binWidth; + if (!binWidth && binNumber) { + _binWidth = rangeWidth / binNumber; + } + // 当binWidth和binNumber都没有指定的情况,采用Sturges formula自动生成binWidth + if (!binWidth && !binNumber) { + const _defaultBinNumber = sturges(values); + _binWidth = rangeWidth / _defaultBinNumber; + } + // 构建key - StatisticData 结构 + const bins: StatisticBin = {}; + + each(originData_copy, (data: any) => { + const value = data[binField]; + const bin = getBinKey(value, _binWidth); + const binKey = `${bin[0]}-${bin[1]}`; + if (!hasKey(bins, binKey)) { + bins[binKey] = { name: binKey, range: bin, count: 0, data: [] }; + } + bins[binKey].data.push(data); + bins[binKey].count += 1; + }); + + // 将分箱数据转换为plotData才是图表所需要的 + const plotData: Array = []; + each(bins, (bin: StatisticData) => { + plotData.push(bin); + }); + + chart.data(plotData); + + const geometry = chart.interval().position('range*count'); + + if (color) { + geometry.color(color); + } + chart.scale({ + count: { + nice: true, + }, + }); + + return params; +} + +/** + * meta 配置 + * @param params + */ +function meta(params: Params): Params { + // TODO + return params; +} + +/** + * legend 配置 + * @param params + */ +function legend(params: Params): Params { + // TODO + return params; +} + +/** + * tooltip 配置 + * @param params + */ +function tooltip(params: Params): Params { + const { chart } = params; + + chart.tooltip({ + showMarkers: false, + }); + + return params; +} + +/** + * tooltip 配置 + * @param params + */ +function interaction(params: Params): Params { + const { chart, options } = params; + const { interaction = 'active-region' } = options; + + chart.interaction(interaction); + + return params; +} + +/** + * 散点图适配器 + * @param chart + * @param options + */ +export function adaptor(params: Params) { + // flow 的方式处理所有的配置到 G2 API + flow(field, meta, legend, tooltip, interaction)(params); +} diff --git a/src/plots/histogram/index.ts b/src/plots/histogram/index.ts new file mode 100644 index 0000000000..70f91dfdfe --- /dev/null +++ b/src/plots/histogram/index.ts @@ -0,0 +1,18 @@ +import { Plot } from '../../core/plot'; +import { HistogramOptions } from './types'; +import { adaptor } from './adaptor'; +import { Adaptor } from '../../core/adaptor'; + +export { HistogramOptions }; + +export class Histogram extends Plot { + /** 图表类型 */ + public type: string = 'histogram'; + + /** + * 获取直方图的适配器 + */ + protected getSchemaAdaptor(): Adaptor { + return adaptor; + } +} diff --git a/src/plots/histogram/types.ts b/src/plots/histogram/types.ts new file mode 100644 index 0000000000..3f4e30d0d6 --- /dev/null +++ b/src/plots/histogram/types.ts @@ -0,0 +1,30 @@ +import { Options } from '../../types'; + +export type StatisticData = { + name: string; + range: Array; + count: number; + data: Array; +}; +export type StatisticBin = { + [key: string]: StatisticData; +}; + +export interface HistogramOptions extends Options { + /** 设置直方图绘制 (进行分箱) 的字段 */ + readonly binField: string; + + /** 设置直方图的分箱宽度,binWidth 影响直方图分成多少箱, + * 不能与binNumber一起使用 + * */ + readonly binWidth?: number; + + /** 设置直方图的分箱数量,binNumber 影响直方图分箱后每个柱子的宽度 */ + readonly binNumber?: number; + + /** 指定直方图柱形颜色 */ + readonly color?: string; + + /** 指定交互形式 ,目前支持2种*/ + readonly interaction?: 'active-region' | 'element-highlight'; +} diff --git a/src/plots/histogram/util.ts b/src/plots/histogram/util.ts new file mode 100644 index 0000000000..91831656de --- /dev/null +++ b/src/plots/histogram/util.ts @@ -0,0 +1,9 @@ +// 进行转换得到值所在的range +export function getBinKey(value: number, binWidth: number): [number, number] { + const index = Math.floor(value / binWidth); + return [binWidth * index, binWidth * (index + 1)]; +} + +export function sturges(values: Array): number { + return Math.ceil(Math.log(values.length) / Math.LN2) + 1; +} From 89a5b853ea6544711b6e5e918d9330c9c3508dd4 Mon Sep 17 00:00:00 2001 From: arcsin1 Date: Thu, 23 Jul 2020 17:17:36 +0800 Subject: [PATCH 2/5] =?UTF-8?q?test:=20=E5=A2=9E=E5=8A=A0=E6=B5=8B?= =?UTF-8?q?=E8=AF=95=E7=94=A8=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 增加测试用例 2. 调整引入结构 3. merge了v2最新代码 --- __tests__/unit/plots/histogram/index-spec.ts | 50 ++++++++------------ src/plots/histogram/adaptor.ts | 6 +-- src/plots/histogram/index.ts | 2 +- 3 files changed, 23 insertions(+), 35 deletions(-) diff --git a/__tests__/unit/plots/histogram/index-spec.ts b/__tests__/unit/plots/histogram/index-spec.ts index 7b1bae13b9..e572c73532 100644 --- a/__tests__/unit/plots/histogram/index-spec.ts +++ b/__tests__/unit/plots/histogram/index-spec.ts @@ -3,56 +3,60 @@ import { histogramData } from '../../../data/histogram-data'; import { createDiv } from '../../../utils/dom'; describe('histogram', () => { - it('binField', () => { + it('binWidth', () => { const histogram = new Histogram(createDiv(), { width: 400, height: 300, appendPadding: 10, data: histogramData, binField: 'value', + binWidth: 2, }); histogram.render(); const geometry = histogram.chart.geometries[0]; - const elements = geometry.elements; - expect(elements.length); + const shapeOrigin = geometry.getShapes()[0].get('origin').data; + + expect(shapeOrigin.range[1] - shapeOrigin.range[0]).toBe(2); }); - it('binField with binWidth', () => { + it('binNumber', () => { const histogram = new Histogram(createDiv(), { width: 400, height: 300, appendPadding: 10, data: histogramData, - binWidth: 2, + binNumber: 4, binField: 'value', }); histogram.render(); const geometry = histogram.chart.geometries[0]; - const elements = geometry.elements; - expect(elements.length); + const shapes = geometry.getShapes(); + + expect(shapes.length - 1).toBe(4); }); - it('binField with binNumber', () => { + + it('automatic calculate binNumber', () => { const histogram = new Histogram(createDiv(), { width: 400, height: 300, appendPadding: 10, data: histogramData, - binNumber: 4, binField: 'value', }); histogram.render(); const geometry = histogram.chart.geometries[0]; - const elements = geometry.elements; - expect(elements.length); + const shapes = geometry.getShapes(); + + expect(shapes.length).toBe(8); }); - it('binField with color,binWidth', () => { + it('color with binWidth', () => { const histogram = new Histogram(createDiv(), { width: 400, height: 300, @@ -67,25 +71,9 @@ describe('histogram', () => { const geometry = histogram.chart.geometries[0]; const elements = geometry.elements; - // @ts-ignore - expect(elements[0].getModel().color).toBe('red'); - }); - it('binField with interaction', () => { - const histogram = new Histogram(createDiv(), { - width: 400, - height: 300, - appendPadding: 10, - data: histogramData, - binField: 'value', - binWidth: 2, - interaction: 'element-highlight', - }); - - histogram.render(); + const shapeOrigin = geometry.getShapes()[0].get('origin').data; - const geometry = histogram.chart.geometries[0]; - const elements = geometry.elements; - // @ts-ignore - expect(elements.length); + expect(shapeOrigin.range[1] - shapeOrigin.range[0]).toBe(2); + expect(elements[0].getModel().color).toBe('red'); }); }); diff --git a/src/plots/histogram/adaptor.ts b/src/plots/histogram/adaptor.ts index 567c468c4c..a68f4521ea 100644 --- a/src/plots/histogram/adaptor.ts +++ b/src/plots/histogram/adaptor.ts @@ -1,11 +1,11 @@ +import { clone, sortBy, valuesOfKey, getRange, each, hasKey } from '@antv/util'; import { Params } from '../../core/adaptor'; import { flow } from '../../utils'; import { StatisticBin, StatisticData, HistogramOptions } from './types'; -import { clone, sortBy, valuesOfKey, getRange, each, hasKey } from '@antv/util'; import { getBinKey, sturges } from './util'; -export /** - * 字段 +/** + * field字段 * @param params */ function field(params: Params): Params { diff --git a/src/plots/histogram/index.ts b/src/plots/histogram/index.ts index 70f91dfdfe..f5667c84a3 100644 --- a/src/plots/histogram/index.ts +++ b/src/plots/histogram/index.ts @@ -1,7 +1,7 @@ import { Plot } from '../../core/plot'; +import { Adaptor } from '../../core/adaptor'; import { HistogramOptions } from './types'; import { adaptor } from './adaptor'; -import { Adaptor } from '../../core/adaptor'; export { HistogramOptions }; From 5ef5d122270881980a852937c06844921b923699 Mon Sep 17 00:00:00 2001 From: arcsin1 Date: Mon, 27 Jul 2020 15:31:16 +0800 Subject: [PATCH 3/5] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=E5=9F=BA?= =?UTF-8?q?=E6=9C=AC=E7=9B=B4=E6=96=B9=E5=9B=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 引入dataset处理数据 2. 增加更多test测试 --- __tests__/unit/plots/histogram/index-spec.ts | 15 +++- .../unit/plots/histogram/interaction-spec.ts | 43 +++++++++++ package.json | 1 + src/plots/histogram/adaptor.ts | 74 ++++++------------- src/plots/histogram/types.ts | 21 ++---- src/plots/histogram/util.ts | 9 --- 6 files changed, 85 insertions(+), 78 deletions(-) create mode 100644 __tests__/unit/plots/histogram/interaction-spec.ts delete mode 100644 src/plots/histogram/util.ts diff --git a/__tests__/unit/plots/histogram/index-spec.ts b/__tests__/unit/plots/histogram/index-spec.ts index e572c73532..56900db563 100644 --- a/__tests__/unit/plots/histogram/index-spec.ts +++ b/__tests__/unit/plots/histogram/index-spec.ts @@ -17,7 +17,6 @@ describe('histogram', () => { const geometry = histogram.chart.geometries[0]; const shapeOrigin = geometry.getShapes()[0].get('origin').data; - expect(shapeOrigin.range[1] - shapeOrigin.range[0]).toBe(2); }); @@ -39,6 +38,10 @@ describe('histogram', () => { expect(shapes.length - 1).toBe(4); }); + /** + * 这个测试dataset处理方式有问题 + * issue已经提过去了 https://github.com/antvis/data-set/issues/90 + */ it('automatic calculate binNumber', () => { const histogram = new Histogram(createDiv(), { width: 400, @@ -51,9 +54,15 @@ describe('histogram', () => { histogram.render(); const geometry = histogram.chart.geometries[0]; - const shapes = geometry.getShapes(); + const shapeOrigin = geometry.getShapes()[0].get('origin').data; + + /** + * 结合dataset的写法是binWidth = histogramData(最大值-最小值)/ 默认值30 + * https://github.com/antvis/data-set/blob/master/src/transform/bin/histogram.ts#L45 + */ + const binWidth = (23.4 - 1.2) / 30; - expect(shapes.length).toBe(8); + expect(shapeOrigin.range[1] - shapeOrigin.range[0]).toBe(binWidth); }); it('color with binWidth', () => { diff --git a/__tests__/unit/plots/histogram/interaction-spec.ts b/__tests__/unit/plots/histogram/interaction-spec.ts new file mode 100644 index 0000000000..87dd17b520 --- /dev/null +++ b/__tests__/unit/plots/histogram/interaction-spec.ts @@ -0,0 +1,43 @@ +import { Histogram } from '../../../../src'; +import { histogramData } from '../../../data/histogram-data'; +import { createDiv } from '../../../utils/dom'; + +describe('Histogram - G2内置interaction', () => { + const histogram = new Histogram(createDiv(), { + width: 400, + height: 300, + appendPadding: 10, + data: histogramData, + binField: 'value', + binWidth: 2, + }); + + histogram.render(); + + it('交互: active-region', () => { + histogram.update({ + ...histogram.options, + interaction: 'active-region', + }); + + expect(histogram.chart.interactions['active-region']).toBeDefined(); + }); + + it('交互:element-highlight', () => { + histogram.update({ + ...histogram.options, + interaction: 'element-highlight', + }); + + expect(histogram.chart.interactions['element-highlight']).toBeDefined(); + }); + + it('交互:element-active', () => { + histogram.update({ + ...histogram.options, + interaction: 'element-active', + }); + + expect(histogram.chart.interactions['element-active']).toBeDefined(); + }); +}); diff --git a/package.json b/package.json index ba2793c232..56b6cc135a 100644 --- a/package.json +++ b/package.json @@ -47,6 +47,7 @@ } }, "dependencies": { + "@antv/data-set": "^0.11.4", "@antv/event-emitter": "^0.1.2", "@antv/g2": "^4.0.12", "size-sensor": "^1.0.1", diff --git a/src/plots/histogram/adaptor.ts b/src/plots/histogram/adaptor.ts index a68f4521ea..78e081f403 100644 --- a/src/plots/histogram/adaptor.ts +++ b/src/plots/histogram/adaptor.ts @@ -1,8 +1,7 @@ -import { clone, sortBy, valuesOfKey, getRange, each, hasKey } from '@antv/util'; +import DataSet from '@antv/data-set'; import { Params } from '../../core/adaptor'; import { flow } from '../../utils'; -import { StatisticBin, StatisticData, HistogramOptions } from './types'; -import { getBinKey, sturges } from './util'; +import { HistogramOptions } from './types'; /** * field字段 @@ -12,58 +11,32 @@ function field(params: Params): Params { const { chart, options } = params; const { data, binField, binNumber, binWidth, color } = options; - // 直方图操作逻辑 - const originData_copy = clone(data); - // 根据binField value对源数据进行排序 - sortBy(originData_copy, binField); + const ds = new DataSet(); + const dv = ds.createView().source(data); - // 获取源数据binField values的range - const values = valuesOfKey(originData_copy, binField); - const range = getRange(values); - const rangeWidth = range.max - range.min; - - // 计算分箱,直方图分箱的计算基于binWidth,如配置了binNumber则将其转为binWidth进行计算 - let _binWidth = binWidth; - if (!binWidth && binNumber) { - _binWidth = rangeWidth / binNumber; - } - // 当binWidth和binNumber都没有指定的情况,采用Sturges formula自动生成binWidth - if (!binWidth && !binNumber) { - const _defaultBinNumber = sturges(values); - _binWidth = rangeWidth / _defaultBinNumber; - } - // 构建key - StatisticData 结构 - const bins: StatisticBin = {}; - - each(originData_copy, (data: any) => { - const value = data[binField]; - const bin = getBinKey(value, _binWidth); - const binKey = `${bin[0]}-${bin[1]}`; - if (!hasKey(bins, binKey)) { - bins[binKey] = { name: binKey, range: bin, count: 0, data: [] }; - } - bins[binKey].data.push(data); - bins[binKey].count += 1; + // dataset处理数据 + dv.transform({ + type: 'bin.histogram', + field: binField, + bins: binNumber, + binWidth: binWidth, + as: ['range', 'count'], }); - // 将分箱数据转换为plotData才是图表所需要的 - const plotData: Array = []; - each(bins, (bin: StatisticData) => { - plotData.push(bin); - }); - - chart.data(plotData); + chart.data(dv.rows); const geometry = chart.interval().position('range*count'); if (color) { geometry.color(color); } - chart.scale({ + // 默认nice y轴 + const scale = { count: { nice: true, }, - }); + }; + chart.scale(scale); return params; } @@ -91,30 +64,29 @@ function legend(params: Params): Params { * @param params */ function tooltip(params: Params): Params { - const { chart } = params; - - chart.tooltip({ - showMarkers: false, - }); - + // TODO return params; } /** - * tooltip 配置 + * interaction 配置用 + * interaction 配合tooltip更友好 * @param params */ function interaction(params: Params): Params { const { chart, options } = params; const { interaction = 'active-region' } = options; + chart.tooltip({ + showMarkers: false, + }); chart.interaction(interaction); return params; } /** - * 散点图适配器 + * 直方图适配器 * @param chart * @param options */ diff --git a/src/plots/histogram/types.ts b/src/plots/histogram/types.ts index 3f4e30d0d6..4a0d150d7e 100644 --- a/src/plots/histogram/types.ts +++ b/src/plots/histogram/types.ts @@ -1,22 +1,13 @@ import { Options } from '../../types'; -export type StatisticData = { - name: string; - range: Array; - count: number; - data: Array; -}; -export type StatisticBin = { - [key: string]: StatisticData; -}; - export interface HistogramOptions extends Options { /** 设置直方图绘制 (进行分箱) 的字段 */ readonly binField: string; - /** 设置直方图的分箱宽度,binWidth 影响直方图分成多少箱, - * 不能与binNumber一起使用 - * */ + /** + * 设置直方图的分箱宽度,binWidth 影响直方图分成多少箱, + * 不能与binNumber一起使用 + */ readonly binWidth?: number; /** 设置直方图的分箱数量,binNumber 影响直方图分箱后每个柱子的宽度 */ @@ -25,6 +16,6 @@ export interface HistogramOptions extends Options { /** 指定直方图柱形颜色 */ readonly color?: string; - /** 指定交互形式 ,目前支持2种*/ - readonly interaction?: 'active-region' | 'element-highlight'; + /** 指定交互形式 ,目前支持这3种比较友好 */ + readonly interaction?: 'active-region' | 'element-highlight' | 'element-active'; } diff --git a/src/plots/histogram/util.ts b/src/plots/histogram/util.ts deleted file mode 100644 index 91831656de..0000000000 --- a/src/plots/histogram/util.ts +++ /dev/null @@ -1,9 +0,0 @@ -// 进行转换得到值所在的range -export function getBinKey(value: number, binWidth: number): [number, number] { - const index = Math.floor(value / binWidth); - return [binWidth * index, binWidth * (index + 1)]; -} - -export function sturges(values: Array): number { - return Math.ceil(Math.log(values.length) / Math.LN2) + 1; -} From a0ee3d8fcba7667771d1a9a867fdb96b1e311816 Mon Sep 17 00:00:00 2001 From: arcsin1 Date: Wed, 29 Jul 2020 11:41:18 +0800 Subject: [PATCH 4/5] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=E6=94=AF?= =?UTF-8?q?=E6=8C=81=E5=B1=82=E5=8F=A0=E7=9B=B4=E6=96=B9=E5=9B=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 增加层叠(堆叠直方图) 2 .增加测试用例 --- __tests__/data/histogram-data.ts | 21 +++++++++ __tests__/unit/plots/histogram/index-spec.ts | 47 +++++++++++++++++-- .../unit/plots/histogram/interaction-spec.ts | 4 +- .../unit/plots/histogram/tooltip-spec.ts | 24 ++++++++++ src/plots/histogram/adaptor.ts | 42 ++++++++++++----- src/plots/histogram/types.ts | 9 ++-- 6 files changed, 128 insertions(+), 19 deletions(-) create mode 100644 __tests__/unit/plots/histogram/tooltip-spec.ts diff --git a/__tests__/data/histogram-data.ts b/__tests__/data/histogram-data.ts index 322b31d5b4..145024b849 100644 --- a/__tests__/data/histogram-data.ts +++ b/__tests__/data/histogram-data.ts @@ -54,3 +54,24 @@ export const histogramData = [ { value: 21 }, { value: 23.4 }, ]; + +export const histogramStackData = [ + { value: 50, type: '数学' }, + { value: 50.1, type: '数学' }, + { value: 50.1, type: '语文' }, + { value: 51.2, type: '语文' }, + { value: 61.3, type: '数学' }, + { value: 61.4, type: '语文' }, + { value: 74, type: '语文' }, + { value: 74, type: '数学' }, + { value: 78, type: '数学' }, + { value: 81.5, type: '数学' }, + { value: 82.3, type: '语文' }, + { value: 85.3, type: '语文' }, + { value: 84.3, type: '数学' }, + { value: 86.3, type: '数学' }, + { value: 90.3, type: '数学' }, + { value: 93.3, type: '语文' }, + { value: 94.3, type: '语文' }, + { value: 94.3, type: '数学' }, +]; diff --git a/__tests__/unit/plots/histogram/index-spec.ts b/__tests__/unit/plots/histogram/index-spec.ts index 56900db563..ce96d4d0b2 100644 --- a/__tests__/unit/plots/histogram/index-spec.ts +++ b/__tests__/unit/plots/histogram/index-spec.ts @@ -1,5 +1,5 @@ import { Histogram } from '../../../../src'; -import { histogramData } from '../../../data/histogram-data'; +import { histogramData, histogramStackData } from '../../../data/histogram-data'; import { createDiv } from '../../../utils/dom'; describe('histogram', () => { @@ -39,7 +39,7 @@ describe('histogram', () => { }); /** - * 这个测试dataset处理方式有问题 + * 这个测试 dataset 处理方式有问题 * issue已经提过去了 https://github.com/antvis/data-set/issues/90 */ it('automatic calculate binNumber', () => { @@ -57,7 +57,7 @@ describe('histogram', () => { const shapeOrigin = geometry.getShapes()[0].get('origin').data; /** - * 结合dataset的写法是binWidth = histogramData(最大值-最小值)/ 默认值30 + * 结合 dataset 的写法是 binWidth = histogramData(最大值-最小值)/ 默认值30 * https://github.com/antvis/data-set/blob/master/src/transform/bin/histogram.ts#L45 */ const binWidth = (23.4 - 1.2) / 30; @@ -85,4 +85,45 @@ describe('histogram', () => { expect(shapeOrigin.range[1] - shapeOrigin.range[0]).toBe(2); expect(elements[0].getModel().color).toBe('red'); }); + + it('stackField: 层叠直方图', () => { + const histogram = new Histogram(createDiv(), { + width: 400, + height: 300, + appendPadding: 10, + data: histogramStackData, + binField: 'value', + binWidth: 4, + stackField: 'type', + }); + + histogram.render(); + + const geometry = histogram.chart.geometries[0]; + + // 如果没有 stackField 是没有adjustNames这个数组 + expect(geometry.getAdjust('stack').adjustNames.length); + }); + + it('stackField with color', () => { + const colors = ['red', 'blue']; + const histogram = new Histogram(createDiv(), { + width: 400, + height: 300, + appendPadding: 10, + data: histogramStackData, + binField: 'value', + binWidth: 4, + stackField: 'type', + color: colors, + }); + + histogram.render(); + + const geometry = histogram.chart.geometries[0]; + const colorAttribute = geometry.getAttribute('color'); + + expect(colorAttribute.values).toEqual(colors); + expect(geometry.getAdjust('stack').adjustNames.length); + }); }); diff --git a/__tests__/unit/plots/histogram/interaction-spec.ts b/__tests__/unit/plots/histogram/interaction-spec.ts index 87dd17b520..ca942ff69a 100644 --- a/__tests__/unit/plots/histogram/interaction-spec.ts +++ b/__tests__/unit/plots/histogram/interaction-spec.ts @@ -23,7 +23,7 @@ describe('Histogram - G2内置interaction', () => { expect(histogram.chart.interactions['active-region']).toBeDefined(); }); - it('交互:element-highlight', () => { + it('交互: element-highlight', () => { histogram.update({ ...histogram.options, interaction: 'element-highlight', @@ -32,7 +32,7 @@ describe('Histogram - G2内置interaction', () => { expect(histogram.chart.interactions['element-highlight']).toBeDefined(); }); - it('交互:element-active', () => { + it('交互: element-active', () => { histogram.update({ ...histogram.options, interaction: 'element-active', diff --git a/__tests__/unit/plots/histogram/tooltip-spec.ts b/__tests__/unit/plots/histogram/tooltip-spec.ts new file mode 100644 index 0000000000..2e579a872a --- /dev/null +++ b/__tests__/unit/plots/histogram/tooltip-spec.ts @@ -0,0 +1,24 @@ +import { Histogram } from '../../../../src'; +import { histogramData } from '../../../data/histogram-data'; +import { createDiv } from '../../../utils/dom'; + +describe('Histogram:tooltip', () => { + const histogram = new Histogram(createDiv(), { + width: 400, + height: 300, + appendPadding: 10, + data: histogramData, + binField: 'value', + binWidth: 2, + tooltip: { + title: 'hello wold!', + }, + }); + + histogram.render(); + + it('tooltip', () => { + // @ts-ignore + expect(histogram.chart.options.tooltip.title).toBe('hello wold!'); + }); +}); diff --git a/src/plots/histogram/adaptor.ts b/src/plots/histogram/adaptor.ts index 78e081f403..b3016ad4d2 100644 --- a/src/plots/histogram/adaptor.ts +++ b/src/plots/histogram/adaptor.ts @@ -1,36 +1,50 @@ import DataSet from '@antv/data-set'; +import { deepMix } from '@antv/util'; import { Params } from '../../core/adaptor'; import { flow } from '../../utils'; import { HistogramOptions } from './types'; /** - * field字段 + * field 字段 * @param params */ function field(params: Params): Params { const { chart, options } = params; - const { data, binField, binNumber, binWidth, color } = options; + const { data, binField, binNumber, binWidth, color, stackField } = options; const ds = new DataSet(); const dv = ds.createView().source(data); - // dataset处理数据 + // dataset 处理数据 dv.transform({ type: 'bin.histogram', field: binField, bins: binNumber, binWidth: binWidth, + groupBy: stackField ? [stackField] : undefined, as: ['range', 'count'], }); - chart.data(dv.rows); const geometry = chart.interval().position('range*count'); - if (color) { + // 基本直方图 color: string + if (color && !Array.isArray(color) && !stackField) { geometry.color(color); } - // 默认nice y轴 + // 层叠直方图需要 color: string[] + if (stackField) { + if (color) { + // 容错处理 color 为 string 的时候 + const _color = Array.isArray(color) ? color : [color]; + geometry.color(stackField, _color); + } else { + // 用 g2 自带颜色 + geometry.color(stackField); + } + geometry.adjust('stack'); + } + // 默认 nice y 轴 const scale = { count: { nice: true, @@ -64,22 +78,28 @@ function legend(params: Params): Params { * @param params */ function tooltip(params: Params): Params { - // TODO + const { chart, options } = params; + const { tooltip } = options; + + const cfg = deepMix({}, tooltip, { + showMarkers: false, + shared: true, + }); + + chart.tooltip(cfg); + return params; } /** * interaction 配置用 - * interaction 配合tooltip更友好 + * interaction 配合 tooltip 更友好 * @param params */ function interaction(params: Params): Params { const { chart, options } = params; const { interaction = 'active-region' } = options; - chart.tooltip({ - showMarkers: false, - }); chart.interaction(interaction); return params; diff --git a/src/plots/histogram/types.ts b/src/plots/histogram/types.ts index 4a0d150d7e..9414e462c1 100644 --- a/src/plots/histogram/types.ts +++ b/src/plots/histogram/types.ts @@ -13,9 +13,12 @@ export interface HistogramOptions extends Options { /** 设置直方图的分箱数量,binNumber 影响直方图分箱后每个柱子的宽度 */ readonly binNumber?: number; - /** 指定直方图柱形颜色 */ - readonly color?: string; + /** 指定直方图柱形颜色,基础直方图为 string ,层叠直方图时为 string[] */ + readonly color?: string | string[]; - /** 指定交互形式 ,目前支持这3种比较友好 */ + /** 指定层叠字段,通过该字段的值,柱子将会被分割为多个部分,通过颜色进行区分 */ + readonly stackField?: string; + + /** 指定交互形式,目前支持这3种比较友好 */ readonly interaction?: 'active-region' | 'element-highlight' | 'element-active'; } From 5e3b54ab723297ccb0867ff15ee7c3902543741c Mon Sep 17 00:00:00 2001 From: arcsin1 Date: Wed, 29 Jul 2020 16:34:41 +0800 Subject: [PATCH 5/5] =?UTF-8?q?feat(v2):=20=20=E6=96=B0=E5=A2=9E=E5=B1=82?= =?UTF-8?q?=E5=8F=A0=E7=9B=B4=E6=96=B9=E5=9B=BE=E5=92=8C=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 调整之前color的问题 2. 完善直方图 3 .新增test测试用例 --- __tests__/unit/plots/histogram/axis-spec.ts | 64 ++++++++++ __tests__/unit/plots/histogram/index-spec.ts | 12 +- .../unit/plots/histogram/interaction-spec.ts | 6 +- __tests__/unit/plots/histogram/label-spec.ts | 61 +++++++++ __tests__/unit/plots/histogram/style-spec.ts | 50 ++++++++ .../unit/plots/histogram/tooltip-spec.ts | 7 ++ src/plots/histogram/adaptor.ts | 119 ++++++++++++------ src/plots/histogram/types.ts | 8 +- 8 files changed, 276 insertions(+), 51 deletions(-) create mode 100644 __tests__/unit/plots/histogram/axis-spec.ts create mode 100644 __tests__/unit/plots/histogram/label-spec.ts create mode 100644 __tests__/unit/plots/histogram/style-spec.ts diff --git a/__tests__/unit/plots/histogram/axis-spec.ts b/__tests__/unit/plots/histogram/axis-spec.ts new file mode 100644 index 0000000000..b98017d16d --- /dev/null +++ b/__tests__/unit/plots/histogram/axis-spec.ts @@ -0,0 +1,64 @@ +import { Histogram } from '../../../../src'; +import { histogramData } from '../../../data/histogram-data'; +import { createDiv } from '../../../utils/dom'; + +describe('Histogram: axis', () => { + it('xAxis', () => { + const histogram = new Histogram(createDiv(), { + width: 400, + height: 300, + appendPadding: 10, + data: histogramData, + binField: 'value', + binWidth: 2, + meta: { + count: { + min: 0, + max: 20, + }, + }, + xAxis: { + label: { + style: { + fill: 'red', + }, + }, + }, + }); + histogram.render(); + + const geometry = histogram.chart.geometries[0]; + + expect(geometry.scales.count.min).toBe(0); + expect(geometry.scales.count.max).toBe(20); + + // @ts-ignore + expect(histogram.chart.options.axes.range.label.style.fill).toBe('red'); + }); + + it('yAxis', () => { + const histogram = new Histogram(createDiv(), { + width: 400, + height: 300, + appendPadding: 10, + data: histogramData, + binField: 'value', + binWidth: 2, + yAxis: { + nice: true, + label: { + style: { + fill: 'red', + }, + }, + }, + }); + + histogram.render(); + + // @ts-ignore + expect(histogram.chart.options.axes.count.nice).toBe(true); + // @ts-ignore + expect(histogram.chart.options.axes.count.label.style.fill).toBe('red'); + }); +}); diff --git a/__tests__/unit/plots/histogram/index-spec.ts b/__tests__/unit/plots/histogram/index-spec.ts index ce96d4d0b2..8e00f332cc 100644 --- a/__tests__/unit/plots/histogram/index-spec.ts +++ b/__tests__/unit/plots/histogram/index-spec.ts @@ -73,7 +73,7 @@ describe('histogram', () => { data: histogramData, binField: 'value', binWidth: 2, - color: 'red', + color: () => 'red', }); histogram.render(); @@ -102,7 +102,10 @@ describe('histogram', () => { const geometry = histogram.chart.geometries[0]; // 如果没有 stackField 是没有adjustNames这个数组 - expect(geometry.getAdjust('stack').adjustNames.length); + expect(geometry.getAdjust('stack')).toMatchObject({ + xField: 'range', + yField: 'count', + }); }); it('stackField with color', () => { @@ -124,6 +127,9 @@ describe('histogram', () => { const colorAttribute = geometry.getAttribute('color'); expect(colorAttribute.values).toEqual(colors); - expect(geometry.getAdjust('stack').adjustNames.length); + expect(geometry.getAdjust('stack')).toMatchObject({ + xField: 'range', + yField: 'count', + }); }); }); diff --git a/__tests__/unit/plots/histogram/interaction-spec.ts b/__tests__/unit/plots/histogram/interaction-spec.ts index ca942ff69a..ce5d16d4d1 100644 --- a/__tests__/unit/plots/histogram/interaction-spec.ts +++ b/__tests__/unit/plots/histogram/interaction-spec.ts @@ -17,7 +17,7 @@ describe('Histogram - G2内置interaction', () => { it('交互: active-region', () => { histogram.update({ ...histogram.options, - interaction: 'active-region', + interactions: [{ name: 'active-region' }], }); expect(histogram.chart.interactions['active-region']).toBeDefined(); @@ -26,7 +26,7 @@ describe('Histogram - G2内置interaction', () => { it('交互: element-highlight', () => { histogram.update({ ...histogram.options, - interaction: 'element-highlight', + interactions: [{ name: 'element-highlight' }], }); expect(histogram.chart.interactions['element-highlight']).toBeDefined(); @@ -35,7 +35,7 @@ describe('Histogram - G2内置interaction', () => { it('交互: element-active', () => { histogram.update({ ...histogram.options, - interaction: 'element-active', + interactions: [{ name: 'element-active' }], }); expect(histogram.chart.interactions['element-active']).toBeDefined(); diff --git a/__tests__/unit/plots/histogram/label-spec.ts b/__tests__/unit/plots/histogram/label-spec.ts new file mode 100644 index 0000000000..55b676755f --- /dev/null +++ b/__tests__/unit/plots/histogram/label-spec.ts @@ -0,0 +1,61 @@ +import { Histogram } from '../../../../src'; +import { histogramData } from '../../../data/histogram-data'; +import { createDiv } from '../../../utils/dom'; + +describe('Histogram: label', () => { + const histogram = new Histogram(createDiv(), { + width: 400, + height: 300, + appendPadding: 10, + data: histogramData, + binField: 'value', + binWidth: 2, + }); + + histogram.render(); + + it('position: top', () => { + histogram.update({ + ...histogram.options, + label: { + position: 'top', + }, + }); + const geometry = histogram.chart.geometries[0]; + + // @ts-ignore + expect(geometry.labelOption.cfg).toEqual({ + position: 'top', + }); + }); + + it('position: middle', () => { + histogram.update({ + ...histogram.options, + label: { + position: 'middle', + }, + }); + const geometry = histogram.chart.geometries[0]; + + // @ts-ignore + expect(geometry.labelOption.cfg).toEqual({ + position: 'middle', + }); + }); + + it('position: bottom', () => { + histogram.update({ + ...histogram.options, + label: { + position: 'bottom', + }, + }); + const geometry = histogram.chart.geometries[0]; + + // @ts-ignore + expect(geometry.labelOption.cfg).toEqual({ + position: 'bottom', + }); + }); +}); diff --git a/__tests__/unit/plots/histogram/style-spec.ts b/__tests__/unit/plots/histogram/style-spec.ts new file mode 100644 index 0000000000..80a4e58983 --- /dev/null +++ b/__tests__/unit/plots/histogram/style-spec.ts @@ -0,0 +1,50 @@ +import { Histogram } from '../../../../src'; +import { histogramData } from '../../../data/histogram-data'; +import { createDiv } from '../../../utils/dom'; + +describe('Histogram: style', () => { + it('style', () => { + const histogram = new Histogram(createDiv(), { + width: 400, + height: 300, + appendPadding: 10, + data: histogramData, + binField: 'value', + binWidth: 2, + columnStyle: { + stroke: 'black', + lineWidth: 2, + }, + }); + histogram.render(); + + const geometry = histogram.chart.geometries[0]; + const elements = geometry.elements; + expect(elements[0].shape.attr('stroke')).toBe('black'); + expect(elements[0].shape.attr('lineWidth')).toBe(2); + }); + + it('style callback', () => { + const histogram = new Histogram(createDiv(), { + width: 400, + height: 300, + appendPadding: 10, + data: histogramData, + binField: 'value', + binWidth: 2, + columnStyle: (x, y) => { + return { + stroke: 'black', + lineWidth: 2, + }; + }, + }); + + histogram.render(); + + const geometry = histogram.chart.geometries[0]; + const elements = geometry.elements; + expect(elements[0].shape.attr('stroke')).toBe('black'); + expect(elements[0].shape.attr('lineWidth')).toBe(2); + }); +}); diff --git a/__tests__/unit/plots/histogram/tooltip-spec.ts b/__tests__/unit/plots/histogram/tooltip-spec.ts index 2e579a872a..0ba0841939 100644 --- a/__tests__/unit/plots/histogram/tooltip-spec.ts +++ b/__tests__/unit/plots/histogram/tooltip-spec.ts @@ -20,5 +20,12 @@ describe('Histogram:tooltip', () => { it('tooltip', () => { // @ts-ignore expect(histogram.chart.options.tooltip.title).toBe('hello wold!'); + histogram.update({ + ...histogram.options, + tooltip: false, + }); + // @ts-ignore + expect(histogram.chart.options.tooltip).toBe(false); + expect(histogram.chart.getComponents().find((co) => co.type === 'tooltip')).toBe(undefined); }); }); diff --git a/src/plots/histogram/adaptor.ts b/src/plots/histogram/adaptor.ts index b3016ad4d2..4f73c880a4 100644 --- a/src/plots/histogram/adaptor.ts +++ b/src/plots/histogram/adaptor.ts @@ -1,7 +1,10 @@ import DataSet from '@antv/data-set'; -import { deepMix } from '@antv/util'; +import { deepMix, isFunction } from '@antv/util'; import { Params } from '../../core/adaptor'; -import { flow } from '../../utils'; +import { tooltip, interaction, animation, theme } from '../../common/adaptor'; +import { findGeometry } from '../../common/helper'; +import { flow, pick } from '../../utils'; +import { AXIS_META_CONFIG_KEYS } from '../../constant'; import { HistogramOptions } from './types'; /** @@ -28,29 +31,14 @@ function field(params: Params): Params { const geometry = chart.interval().position('range*count'); - // 基本直方图 color: string - if (color && !Array.isArray(color) && !stackField) { - geometry.color(color); + if (color && !stackField) { + geometry.color('count', color); } - // 层叠直方图需要 color: string[] + if (stackField) { - if (color) { - // 容错处理 color 为 string 的时候 - const _color = Array.isArray(color) ? color : [color]; - geometry.color(stackField, _color); - } else { - // 用 g2 自带颜色 - geometry.color(stackField); - } + geometry.color(stackField, color); geometry.adjust('stack'); } - // 默认 nice y 轴 - const scale = { - count: { - nice: true, - }, - }; - chart.scale(scale); return params; } @@ -60,48 +48,99 @@ function field(params: Params): Params { * @param params */ function meta(params: Params): Params { - // TODO + const { chart, options } = params; + const { meta, xAxis, yAxis } = options; + + // 上面默认 x 轴 为 range 字段, y 轴字段为 count 字段 + const scales = deepMix({}, meta, { + range: pick(xAxis, AXIS_META_CONFIG_KEYS), + count: pick(yAxis, AXIS_META_CONFIG_KEYS), + }); + + chart.scale(scales); + return params; } /** - * legend 配置 + * axis 配置 * @param params */ -function legend(params: Params): Params { - // TODO +function axis(params: Params): Params { + const { chart, options } = params; + const { xAxis, yAxis } = options; + + // 为 false 则是不显示轴 + if (xAxis === false) { + chart.axis('range', false); + } else { + chart.axis('range', xAxis); + } + + if (yAxis === false) { + chart.axis('count', false); + } else { + chart.axis('count', yAxis); + } + return params; } /** - * tooltip 配置 + * legend 配置 * @param params */ -function tooltip(params: Params): Params { +function legend(params: Params): Params { const { chart, options } = params; - const { tooltip } = options; - - const cfg = deepMix({}, tooltip, { - showMarkers: false, - shared: true, - }); + const { legend, stackField } = options; - chart.tooltip(cfg); + if (legend && stackField) { + chart.legend(stackField, legend); + } return params; } /** - * interaction 配置用 - * interaction 配合 tooltip 更友好 + * label 配置 * @param params */ -function interaction(params: Params): Params { +function label(params: Params): Params { const { chart, options } = params; - const { interaction = 'active-region' } = options; + const { label } = options; + + const geometry = findGeometry(chart, 'interval'); + + if (!label) { + geometry.label(false); + } else { + const { callback, ...cfg } = label; + geometry.label({ + fields: ['count'], + callback, + cfg, + }); + } + + return params; +} - chart.interaction(interaction); +/** + * style 配置 + * @param params + */ +function style(params: Params): Params { + const { chart, options } = params; + const { columnStyle } = options; + const geometry = findGeometry(chart, 'interval'); + if (columnStyle && geometry) { + if (isFunction(columnStyle)) { + geometry.style('range*count', columnStyle); + } else { + geometry.style(columnStyle); + } + } return params; } @@ -112,5 +151,5 @@ function interaction(params: Params): Params */ export function adaptor(params: Params) { // flow 的方式处理所有的配置到 G2 API - flow(field, meta, legend, tooltip, interaction)(params); + flow(field, meta, axis, legend, theme, label, style, tooltip, interaction, animation)(params); } diff --git a/src/plots/histogram/types.ts b/src/plots/histogram/types.ts index 9414e462c1..131c8984ea 100644 --- a/src/plots/histogram/types.ts +++ b/src/plots/histogram/types.ts @@ -1,4 +1,5 @@ import { Options } from '../../types'; +import { ShapeStyle } from '../../types/style'; export interface HistogramOptions extends Options { /** 设置直方图绘制 (进行分箱) 的字段 */ @@ -13,12 +14,9 @@ export interface HistogramOptions extends Options { /** 设置直方图的分箱数量,binNumber 影响直方图分箱后每个柱子的宽度 */ readonly binNumber?: number; - /** 指定直方图柱形颜色,基础直方图为 string ,层叠直方图时为 string[] */ - readonly color?: string | string[]; - /** 指定层叠字段,通过该字段的值,柱子将会被分割为多个部分,通过颜色进行区分 */ readonly stackField?: string; - /** 指定交互形式,目前支持这3种比较友好 */ - readonly interaction?: 'active-region' | 'element-highlight' | 'element-active'; + /** 柱子样式配置,可选 */ + readonly columnStyle?: ShapeStyle | ((x: any, y: any, color?: any) => ShapeStyle); }