Skip to content

Commit 0276528

Browse files
lxfu1liufu.lf
and
liufu.lf
authored
feat: 双轴图新增 slider (#2287)
* feat: 双轴图新增 slider * feat: 补充 slider 单测 * fix: ci * fix: isBetween 移入 number 中 * fix: reolve conversation * docs: 补充文档 * fix: confict Co-authored-by: liufu.lf <[email protected]>
1 parent 947889b commit 0276528

File tree

12 files changed

+384
-30
lines changed

12 files changed

+384
-30
lines changed

__tests__/data/pv-uv.ts

+31
Original file line numberDiff line numberDiff line change
@@ -72,3 +72,34 @@ export const UV_DATA_MULTI = [
7272
{ date: '0608', uv: 3431, site: 'c' },
7373
{ date: '0609', uv: 4054, site: 'c' },
7474
];
75+
76+
export const uvBillData = [
77+
{ time: '2019-03', value: 350, type: 'uv' },
78+
{ time: '2019-04', value: 900, type: 'uv' },
79+
{ time: '2019-05', value: 300, type: 'uv' },
80+
{ time: '2019-06', value: 450, type: 'uv' },
81+
{ time: '2019-07', value: 470, type: 'uv' },
82+
{ time: '2019-03', value: 220, type: 'bill' },
83+
{ time: '2019-04', value: 300, type: 'bill' },
84+
{ time: '2019-05', value: 250, type: 'bill' },
85+
{ time: '2019-06', value: 220, type: 'bill' },
86+
{ time: '2019-07', value: 362, type: 'bill' },
87+
];
88+
89+
export const transformData = [
90+
{ time: '2019-03', count: 800, name: 'a' },
91+
{ time: '2019-04', count: 600, name: 'a' },
92+
{ time: '2019-05', count: 400, name: 'a' },
93+
{ time: '2019-06', count: 380, name: 'a' },
94+
{ time: '2019-07', count: 220, name: 'a' },
95+
{ time: '2019-03', count: 750, name: 'b' },
96+
{ time: '2019-04', count: 650, name: 'b' },
97+
{ time: '2019-05', count: 450, name: 'b' },
98+
{ time: '2019-06', count: 400, name: 'b' },
99+
{ time: '2019-07', count: 320, name: 'b' },
100+
{ time: '2019-03', count: 900, name: 'c' },
101+
{ time: '2019-04', count: 600, name: 'c' },
102+
{ time: '2019-05', count: 450, name: 'c' },
103+
{ time: '2019-06', count: 300, name: 'c' },
104+
{ time: '2019-07', count: 200, name: 'c' },
105+
];
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
import { DualAxes } from '../../../../src';
2+
import { delay } from '../../../utils/delay';
3+
import { PV_DATA_MULTI, UV_DATA_MULTI, uvBillData, transformData } from '../../../data/pv-uv';
4+
import { createDiv } from '../../../utils/dom';
5+
6+
describe('slider', () => {
7+
it('type cat', async () => {
8+
const dualAxes = new DualAxes(createDiv('test DualAxes doubal line'), {
9+
data: [PV_DATA_MULTI, UV_DATA_MULTI],
10+
xField: 'date',
11+
yField: ['pv', 'uv'],
12+
limitInPlot: false,
13+
meta: {
14+
date: {
15+
sync: false,
16+
},
17+
},
18+
slider: {
19+
start: 0,
20+
end: 0.5,
21+
},
22+
padding: [20, 40, 60, 40],
23+
geometryOptions: [
24+
{
25+
geometry: 'column',
26+
seriesField: 'site',
27+
isStack: true,
28+
},
29+
{
30+
geometry: 'line',
31+
seriesField: 'site',
32+
isStack: true,
33+
},
34+
],
35+
});
36+
37+
dualAxes.render();
38+
expect(dualAxes.chart.views[0].getOptions().slider).toEqual({
39+
start: 0,
40+
end: 0.5,
41+
});
42+
await delay(500);
43+
// 需要去重, 存在双轴数据
44+
expect(Array.from(new Set(dualAxes.chart.views[0].filterData(PV_DATA_MULTI).map((item) => item.date)))).toEqual([
45+
'0601',
46+
'0602',
47+
'0603',
48+
'0604',
49+
'0605',
50+
]);
51+
expect(dualAxes.chart.getController('slider')).toBeDefined();
52+
expect(dualAxes.chart.views[0].getController('slider').getComponents().length).toBe(1);
53+
expect(dualAxes.chart.views[1].getController('slider').getComponents().length).toBe(0);
54+
const [slider] = dualAxes.chart.views[0].getComponents().filter((co) => co.type === 'slider');
55+
expect(slider.component.get('minText')).toBe('0601');
56+
dualAxes.update({
57+
slider: {
58+
start: 0,
59+
end: 1,
60+
},
61+
});
62+
expect(dualAxes.chart.views[0].getOptions().slider).toEqual({
63+
start: 0,
64+
end: 1,
65+
});
66+
expect(Array.from(new Set(dualAxes.chart.views[0].filterData(PV_DATA_MULTI).map((item) => item.date)))).toEqual([
67+
'0601',
68+
'0602',
69+
'0603',
70+
'0604',
71+
'0605',
72+
'0606',
73+
'0607',
74+
'0608',
75+
'0609',
76+
]);
77+
dualAxes.destroy();
78+
});
79+
it('type time', async () => {
80+
const dualAxes = new DualAxes(createDiv('test DualAxes doubal line'), {
81+
data: [uvBillData, transformData],
82+
xField: 'time',
83+
yField: ['value', 'count'],
84+
geometryOptions: [
85+
{
86+
geometry: 'line',
87+
seriesField: 'type',
88+
lineStyle: {
89+
lineWidth: 3,
90+
lineDash: [5, 5],
91+
},
92+
smooth: true,
93+
},
94+
{
95+
geometry: 'line',
96+
seriesField: 'name',
97+
point: {},
98+
},
99+
],
100+
limitInPlot: false,
101+
meta: {
102+
time: {
103+
type: 'time',
104+
sync: false,
105+
},
106+
},
107+
slider: {
108+
start: 0,
109+
end: 1,
110+
},
111+
padding: [20, 40, 60, 40],
112+
});
113+
114+
dualAxes.render();
115+
expect(dualAxes.chart.views[0].getOptions().slider).toEqual({
116+
start: 0,
117+
end: 1,
118+
});
119+
expect(dualAxes.chart.getController('slider')).toBeDefined();
120+
expect(dualAxes.chart.views[0].getController('slider').getComponents().length).toBe(1);
121+
expect(dualAxes.chart.views[1].getController('slider').getComponents().length).toBe(0);
122+
const [slider] = dualAxes.chart.views[0].getComponents().filter((co) => co.type === 'slider');
123+
expect(slider.component.get('minText')).toBe('2019-03');
124+
dualAxes.update({
125+
meta: {
126+
time: {
127+
type: 'timeCat',
128+
sync: false,
129+
},
130+
},
131+
});
132+
133+
expect(dualAxes.chart.views[0].getOptions().slider).toEqual({
134+
start: 0,
135+
end: 1,
136+
});
137+
expect(dualAxes.chart.getController('slider')).toBeDefined();
138+
dualAxes.changeData([uvBillData.slice(2, 8), transformData.slice(2, 8)]);
139+
const [changedSlider] = dualAxes.chart.views[0].getComponents().filter((co) => co.type === 'slider');
140+
expect(changedSlider.component.get('minText')).toBe('2019-05');
141+
dualAxes.update({
142+
slider: false,
143+
});
144+
await delay(500);
145+
expect(dualAxes.chart.views[0].getController('slider').getComponents().length).toBe(0);
146+
dualAxes.destroy();
147+
});
148+
});
+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { isBetween } from '../../../src/utils';
2+
3+
describe('isBetween', () => {
4+
it('isBetween', () => {
5+
expect(isBetween(2, 1, 3)).toBeTruthy();
6+
expect(isBetween(2, 3, 1)).toBeTruthy();
7+
expect(isBetween(1, 3, 1)).toBeTruthy();
8+
expect(isBetween(3, 3, 1)).toBeTruthy();
9+
expect(isBetween(0, 3, 1)).toBeFalsy();
10+
expect(isBetween(5, 3, 1)).toBeFalsy();
11+
});
12+
});

docs/api/plots/dual-axes.en.md

+4
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,10 @@ The configuration for Xaxis and Yaxis is the same, since DualAxes are biaxes, an
156156

157157
`markdown:docs/common/theme.en.md`
158158

159+
#### slider
160+
161+
`markdown:docs/common/slider.en.md`
162+
159163
### Plot Events
160164

161165
`markdown:docs/common/events.en.md`

docs/api/plots/dual-axes.zh.md

+31-27
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,13 @@ order: 6
1919
const data = [[{ time: '1991'value: 20 }], [{ time: '1992', count: 20 }]];
2020
```
2121

22-
#### xField
22+
#### xField
2323

2424
<description>**required** _string_</description>
2525

2626
点形状在 x 方向位置映射对应的数据字段名,一般对应一个连续字段。例如`{xField: 'time'}`
2727

28-
#### yField
28+
#### yField
2929

3030
<description>**required** _string[]_</description>
3131

@@ -41,39 +41,40 @@ const data = [[{ time: '1991',value: 20 }], [{ time: '1992', count: 20 }]];
4141

4242
<description>**optional** _array object_</description>
4343

44-
指定了双轴各自对应的图形配置,形式为[左轴图形配置,右轴图形配置]。每一个配置应为 Line 或 Column 类型的 Config。通过指定双轴对应图形,来实现混合图表功能:
44+
指定了双轴各自对应的图形配置,形式为[左轴图形配置,右轴图形配置]。每一个配置应为 Line 或 Column 类型的 Config。通过指定双轴对应图形,来实现混合图表功能:
45+
4546
- 双轴折线图: [Line, Line], 参考 [DEMO](../../../examples/dual-axes/dual-line)
4647
- 柱线混合图: [Column, Line], 参考 [DEMO](http://localhost:8080/zh/examples/dual-axes/column-line)
4748

4849
你还可以通过配置 Line 或 Column 的相关配置(见下文),形成双轴多折线图([DEMO](../../../examples/dual-axes/dual-line#dual-multi-line)), 堆叠柱+折线图([DEMO](../../../examples/dual-axes/stacked-column-line)), 分组柱+折线图([DEMO](../../../examples/dual-axes/grouped-column-line))
4950

5051
折线对应的图形配置为:
5152

52-
| 细分配置项名称 | 类型 | 功能描述 | 默认值 |
53-
| ----------- | -------------------------------- | ----------------------------------------------- | ------ |
54-
| geometry | _string_ | 图形类型,指定为'line' | 'line' |
55-
| seriesField | _string_ | 拆分字段, 若存在则为多折线,具体用法同[折线图 seriesfield](./line#seriesfield) |
56-
| smooth | _boolean_ | 是否平滑,具体用法同[折线图 smooth](./line#smooth) | false |
57-
| connectNulls | _boolean_ | 是否连接空数据,具体用法同[折线图 connectnulls](./line#connectnulls) | true |
58-
| lineStyle | _StyleAttr \| Function_ | 折线图形样式,具体用法同[折线图 lineStyle](./line#linestyle) | |
59-
| point | _pointStyle_ | 线数据点图形样式,具体用法同[折线图 point](./line#point) | |
60-
| label | _ContinueLegendLabelCfg_ | 折线图 label,具体用法同[折线图 label](./line#label) |
61-
| color | _string \| string[] \| Function_ | 指定点的颜色。具体用法同[折线图 color](./line#color) |
53+
| 细分配置项名称 | 类型 | 功能描述 | 默认值 |
54+
| -------------- | -------------------------------- | ------------------------------------------------------------------------------ | ------ |
55+
| geometry | _string_ | 图形类型,指定为'line' | 'line' |
56+
| seriesField | _string_ | 拆分字段, 若存在则为多折线,具体用法同[折线图 seriesfield](./line#seriesfield) |
57+
| smooth | _boolean_ | 是否平滑,具体用法同[折线图 smooth](./line#smooth) | false |
58+
| connectNulls | _boolean_ | 是否连接空数据,具体用法同[折线图 connectnulls](./line#connectnulls) | true |
59+
| lineStyle | _StyleAttr \| Function_ | 折线图形样式,具体用法同[折线图 lineStyle](./line#linestyle) | |
60+
| point | _pointStyle_ | 线数据点图形样式,具体用法同[折线图 point](./line#point) | |
61+
| label | _ContinueLegendLabelCfg_ | 折线图 label,具体用法同[折线图 label](./line#label) |
62+
| color | _string \| string[] \| Function_ | 指定点的颜色。具体用法同[折线图 color](./line#color) |
6263

6364
柱形对应的图形配置为:
6465

65-
| 细分配置项名称 | 类型 | 功能描述 | 默认值 |
66-
| ----------- | -------------------------------- | ----------------------------------------------- | ------ |
67-
| geometry | _string_ | 图形类型,应指定为'column' | |
68-
| seriesField | _string_ | 拆分字段, 在分组柱状图下同 groupField、colorField,在堆积柱状图下同 stackField、colorField ,具体参考[柱形图 seriesfield](./column#seriesfield) |
69-
| isGroup | _boolean_ | 是否分组柱形图,具体用法同[柱形图 isGroup](./column#isgroup) | false |
70-
| isStack | _boolean_ | 是否堆积柱状图,具体用法同[柱形图 isStack](./column#isstack) | false |
71-
| columnWidthRatio | _number_ | 柱状图宽度占比 [0-1] ,具体用法同[柱形图 columnWidthRatio](./column#columnwidthratio) | |
72-
| marginRatio | _number_ | 分组中柱子之间的间距 [0-1],仅对分组柱状图适用,具体用法同[柱形图 marginRatio](./column#marginratio) | |
73-
| columnStyle | _StyleAttr \| Function_ | 柱子样式配置,具体用法同[柱形图 columnStyle](./column#columnstyle) | |
74-
| label | _ContinueLegendLabelCfg_ | 柱形图 label,具体用法同[柱线图 label](./column#label) |
75-
| color | _string \| string[] \| Function_ | 指定点的颜色。具体用法同[折线图 color](./column#color) |
76-
| groupField | _string_ | 拆分字段,用于堆叠+分组柱图,拆分优先级高于 seriesField,isGroup: true 时会根据 groupField 进行分组。 | - |
66+
| 细分配置项名称 | 类型 | 功能描述 | 默认值 |
67+
| ---------------- | -------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- | ------ |
68+
| geometry | _string_ | 图形类型,应指定为'column' | |
69+
| seriesField | _string_ | 拆分字段, 在分组柱状图下同 groupField、colorField,在堆积柱状图下同 stackField、colorField ,具体参考[柱形图 seriesfield](./column#seriesfield) |
70+
| isGroup | _boolean_ | 是否分组柱形图,具体用法同[柱形图 isGroup](./column#isgroup) | false |
71+
| isStack | _boolean_ | 是否堆积柱状图,具体用法同[柱形图 isStack](./column#isstack) | false |
72+
| columnWidthRatio | _number_ | 柱状图宽度占比 [0-1] ,具体用法同[柱形图 columnWidthRatio](./column#columnwidthratio) | |
73+
| marginRatio | _number_ | 分组中柱子之间的间距 [0-1],仅对分组柱状图适用,具体用法同[柱形图 marginRatio](./column#marginratio) | |
74+
| columnStyle | _StyleAttr \| Function_ | 柱子样式配置,具体用法同[柱形图 columnStyle](./column#columnstyle) | |
75+
| label | _ContinueLegendLabelCfg_ | 柱形图 label,具体用法同[柱线图 label](./column#label) |
76+
| color | _string \| string[] \| Function_ | 指定点的颜色。具体用法同[折线图 color](./column#color) |
77+
| groupField | _string_ | 拆分字段,用于堆叠+分组柱图,拆分优先级高于 seriesField,isGroup: true 时会根据 groupField 进行分组。 | - |
7778

7879
### 图表组件
7980

@@ -144,18 +145,21 @@ xAxis、yAxis 配置相同,由于 DualAxes 是双轴, annotations 类型是
144145
}
145146
}
146147
```
148+
147149
`markdown:docs/common/annotations.zh.md`
148150

149151
#### legend
150152

151153
`markdown:docs/common/legend.zh.md`
152154

153-
154-
#### 主题
155155
### 图表主题
156156

157157
`markdown:docs/common/theme.zh.md`
158158

159+
#### 滚动条
160+
161+
`markdown:docs/common/slider.zh.md`
162+
159163
### 图表事件
160164

161165
`markdown:docs/common/events.zh.md`

examples/dual-axes/column-line/demo/meta.json

+8
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,14 @@
4343
"en": "Other column line"
4444
},
4545
"screenshot": "https://gw.alipayobjects.com/mdn/rms_d314dd/afts/img/A*_-QESLpPhPcAAAAAAAAAAAAAARQnAQ"
46+
},
47+
{
48+
"filename": "slider-column-line.ts",
49+
"title": {
50+
"zh": "柱线混合图表-滑块",
51+
"en": "Slider with column line"
52+
},
53+
"screenshot": "https://gw.alipayobjects.com/zos/antfincdn/fxukGXuXfg/89ea37e5-e00d-4424-b75e-1aba3ff8b633.png"
4654
}
4755
]
4856
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import { DualAxes } from '@antv/g2plot';
2+
3+
const data = [
4+
{ time: '2020-08-20', consumeTime: 10868, completeTime: 649.483 },
5+
{ time: '2020-08-21', consumeTime: 8786, completeTime: 1053.7 },
6+
{ time: '2020-08-22', consumeTime: 10824, completeTime: 679.817 },
7+
{ time: '2020-08-23', consumeTime: 7860, completeTime: 638.117 },
8+
{ time: '2020-08-24', consumeTime: 13253, completeTime: 843.3 },
9+
{ time: '2020-08-25', consumeTime: 17015, completeTime: 1092.983 },
10+
{ time: '2020-08-26', consumeTime: 19298, completeTime: 1036.317 },
11+
{ time: '2020-08-27', consumeTime: 13937, completeTime: 1031.9 },
12+
{ time: '2020-08-28', consumeTime: 11541, completeTime: 803.467 },
13+
{ time: '2020-08-29', consumeTime: 15244, completeTime: 830.733 },
14+
{ time: '2020-08-30', consumeTime: 14247, completeTime: 709.867 },
15+
{ time: '2020-08-31', consumeTime: 9402, completeTime: 665.233 },
16+
{ time: '2020-09-01', consumeTime: 10440, completeTime: 696.367 },
17+
{ time: '2020-09-02', consumeTime: 9345, completeTime: 692.867 },
18+
{ time: '2020-09-03', consumeTime: 18459, completeTime: 936.017 },
19+
{ time: '2020-09-04', consumeTime: 9763, completeTime: 782.867 },
20+
{ time: '2020-09-05', consumeTime: 11074, completeTime: 653.8 },
21+
{ time: '2020-09-06', consumeTime: 11770, completeTime: 856.683 },
22+
{ time: '2020-09-07', consumeTime: 12206, completeTime: 777.15 },
23+
{ time: '2020-09-08', consumeTime: 11434, completeTime: 773.283 },
24+
{ time: '2020-09-09', consumeTime: 16218, completeTime: 833.3 },
25+
{ time: '2020-09-10', consumeTime: 11914, completeTime: 793.517 },
26+
{ time: '2020-09-11', consumeTime: 16781, completeTime: 894.45 },
27+
{ time: '2020-09-12', consumeTime: 10555, completeTime: 725.55 },
28+
{ time: '2020-09-13', consumeTime: 10899, completeTime: 709.967 },
29+
{ time: '2020-09-14', consumeTime: 10713, completeTime: 787.6 },
30+
{ time: '2020-09-15', consumeTime: 0, completeTime: 644.183 },
31+
{ time: '2020-09-16', consumeTime: 0, completeTime: 1066.65 },
32+
{ time: '2020-09-17', consumeTime: 20357, completeTime: 932.45 },
33+
{ time: '2020-09-18', consumeTime: 10424, completeTime: 753.583 },
34+
];
35+
36+
const dualAxes = new DualAxes('container', {
37+
data: [data, data],
38+
xField: 'time',
39+
yField: ['consumeTime', 'completeTime'],
40+
limitInPlot: false,
41+
padding: [10, 20, 80, 30], // 需要设置底部 padding 值,同 css
42+
slider: {},
43+
meta: {
44+
time: {
45+
sync: false, // 开启之后 slider 无法重绘
46+
},
47+
},
48+
geometryOptions: [
49+
{
50+
geometry: 'column',
51+
},
52+
{
53+
geometry: 'line',
54+
},
55+
],
56+
});
57+
58+
dualAxes.render();

src/constant.ts

+2
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ export const AXIS_META_CONFIG_KEYS = [
2020
'exponent',
2121
// time 类型的格式化
2222
'mask',
23+
// 是否同步
24+
'sync',
2325
];
2426

2527
/**

0 commit comments

Comments
 (0)