-
Notifications
You must be signed in to change notification settings - Fork 604
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #389 from antvis/feat-waterfall
feat(waterfall): 添加瀑布图
- Loading branch information
Showing
16 changed files
with
780 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,170 @@ | ||
import { Waterfall } from '../../src'; | ||
import { Shape } from '@antv/g'; | ||
import * as _ from '@antv/util'; | ||
|
||
describe('waterfall plot', () => { | ||
const data = [ | ||
{ type: '日用品', money: 300 }, | ||
{ type: '伙食费', money: 900 }, | ||
{ type: '交通费', money: 200 }, | ||
{ type: '水电费', money: 300 }, | ||
{ type: '房租', money: 1200 }, | ||
{ type: '商场消费', money: 1000 }, | ||
{ type: '应酬交际', money: -2000 }, | ||
]; | ||
|
||
const plotOptions = { | ||
title: { | ||
visible: true, | ||
text: '每月收支情况(瀑布图)', | ||
}, | ||
forceFit: true, | ||
data, | ||
padding: 'auto', | ||
data, | ||
xField: 'type', | ||
yField: 'money', | ||
meta: { | ||
type: { | ||
alias: '类别', | ||
}, | ||
money: { | ||
alias: '金额', | ||
}, | ||
}, | ||
}; | ||
|
||
const canvasDiv = document.createElement('div'); | ||
canvasDiv.style.width = '600px'; | ||
canvasDiv.style.height = '600px'; | ||
canvasDiv.style.left = '30px'; | ||
canvasDiv.style.top = '30px'; | ||
canvasDiv.id = 'canvas1'; | ||
document.body.appendChild(canvasDiv); | ||
|
||
const waterfallPlot = new Waterfall(canvasDiv, plotOptions); | ||
const waterfallLayer = waterfallPlot.getLayer(); | ||
|
||
it('normal', () => { | ||
waterfallPlot.render(); | ||
const shapes = waterfallLayer.view.get('elements')[0].getShapes(); | ||
expect(shapes.length).toBe(data.length * 2 + 1); | ||
const lines = shapes.filter((s) => s.name === 'leader-line'); | ||
expect(lines.length).toBe(data.length); | ||
}); | ||
|
||
it('custom color, string', () => { | ||
waterfallPlot.updateConfig({ | ||
color: 'rgba(0, 255, 255, 0.2)', | ||
}); | ||
waterfallPlot.render(); | ||
const shapes = waterfallLayer.view | ||
.get('elements')[0] | ||
.getShapes() | ||
.filter((s) => s.name === 'interval'); | ||
expect(_.every(shapes, (s: Shape) => s.attr('fill') === 'rgba(0, 255, 255, 0.2)')).toBe(true); | ||
}); | ||
|
||
it('custom color, object', () => { | ||
waterfallPlot.updateConfig({ | ||
color: { | ||
rising: 'red', | ||
falling: 'green', | ||
total: '#ddd', | ||
}, | ||
}); | ||
waterfallPlot.render(); | ||
const shapes = waterfallLayer.view | ||
.get('elements')[0] | ||
.getShapes() | ||
.filter((s) => s.name === 'interval'); | ||
expect(shapes[0].attr('fill')).toBe('red'); | ||
expect(shapes[6].attr('fill')).toBe('green'); | ||
expect(_.last(shapes).attr('fill')).toBe('#ddd'); | ||
}); | ||
|
||
it('use callback to custom color', () => { | ||
waterfallPlot.updateConfig({ | ||
color: (type, value, values, index) => { | ||
if (index === data.length) { | ||
return '#ddd'; | ||
} else if (value > 0) { | ||
return 'rgba(255, 0, 0, 0.45)'; | ||
} | ||
return 'rgba(255, 255, 0, 0.45)'; | ||
}, | ||
}); | ||
waterfallPlot.render(); | ||
const shapes = waterfallLayer.view | ||
.get('elements')[0] | ||
.getShapes() | ||
.filter((s) => s.name === 'interval'); | ||
expect(shapes[0].attr('fill')).toBe('rgba(255, 0, 0, 0.45)'); | ||
expect(shapes[6].attr('fill')).toBe('rgba(255, 255, 0, 0.45)'); | ||
expect(_.last(shapes).attr('fill')).toBe('#ddd'); | ||
}); | ||
|
||
it('not show total', () => { | ||
waterfallPlot.updateConfig({ | ||
showTotal: { | ||
visible: false, | ||
}, | ||
}); | ||
waterfallPlot.render(); | ||
const shapes = waterfallLayer.view.get('elements')[0].getShapes(); | ||
expect(shapes.length).toBe(data.length * 2 - 1); | ||
const lines = shapes.filter((s) => s.name === 'leader-line'); | ||
expect(lines.length).toBe(data.length - 1); | ||
}); | ||
|
||
it('diff-label', () => { | ||
waterfallPlot.updateConfig({ | ||
diffLabel: { | ||
visible: true, | ||
style: { | ||
fill: 'red', | ||
}, | ||
formatter: (text, item, index) => { | ||
if (text.startsWith('+')) { | ||
return `涨 ${text.substr(1)}`; | ||
} else if (text.startsWith('-')) { | ||
return `跌 ${text.substr(1)}`; | ||
} | ||
return text; | ||
}, | ||
}, | ||
}); | ||
waterfallPlot.render(); | ||
const shapes = waterfallLayer.view.get('elements')[0].getShapes(); | ||
// @ts-ignore | ||
const diffLabel = waterfallLayer.diffLabel; | ||
const labelShapes = diffLabel.container.get('children')[0].get('children'); | ||
const intervals = shapes.filter((s) => s.name === 'interval'); | ||
/** auto hide label that is overflowed */ | ||
expect(labelShapes.length).toBe(intervals.length); | ||
expect( | ||
_.every( | ||
intervals, | ||
(shape: Shape, idx) => labelShapes[idx].attr('y') === (shape.getBBox().minY + shape.getBBox().maxY) / 2 | ||
) | ||
).toBe(true); | ||
expect(labelShapes[0].attr('text')).toBe('涨 300'); | ||
expect(labelShapes[0].attr('fill')).toBe('red'); | ||
}); | ||
|
||
it('hidden diff-label', () => { | ||
waterfallPlot.updateConfig({ | ||
diffLabel: { | ||
visible: false, | ||
}, | ||
}); | ||
waterfallPlot.render(); | ||
// @ts-ignore | ||
const diffLabel = waterfallLayer.diffLabel; | ||
expect(diffLabel).toBe(null); | ||
}); | ||
|
||
afterAll(() => { | ||
// waterfallPlot.destroy(); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
--- | ||
title: API | ||
--- | ||
|
||
API. | ||
|
||
- Modern browsers and Internet Explorer 9+ (with [polyfills](https:// ant.design/docs/react/getting-started#Compatibility)) | ||
- Server-side Rendering | ||
- [Electron](http:// electron.atom.io/) | ||
|
||
| [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/edge/edge_48x48.png" alt="IE / Edge" width="24px" height="24px" />](http:// godban.github.io/browsers-support-badges/)</br>IE / Edge | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/firefox/firefox_48x48.png" alt="Firefox" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Firefox | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/chrome/chrome_48x48.png" alt="Chrome" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Chrome | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/safari/safari_48x48.png" alt="Safari" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Safari | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/opera/opera_48x48.png" alt="Opera" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Opera | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/electron/electron_48x48.png" alt="Electron" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Electron | | ||
| ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | ||
| IE9, IE10, IE11, Edge | last 2 versions | last 2 versions | last 2 versions | last 2 versions | last 2 versions | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
--- | ||
title: API | ||
--- | ||
|
||
暂无。 | ||
|
||
- Modern browsers and Internet Explorer 9+ (with [polyfills](https:// ant.design/docs/react/getting-started#Compatibility)) | ||
- Server-side Rendering | ||
- [Electron](http:// electron.atom.io/) | ||
|
||
| [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/edge/edge_48x48.png" alt="IE / Edge" width="24px" height="24px" />](http:// godban.github.io/browsers-support-badges/)</br>IE / Edge | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/firefox/firefox_48x48.png" alt="Firefox" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Firefox | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/chrome/chrome_48x48.png" alt="Chrome" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Chrome | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/safari/safari_48x48.png" alt="Safari" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Safari | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/opera/opera_48x48.png" alt="Opera" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Opera | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/electron/electron_48x48.png" alt="Electron" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Electron | | ||
| ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | ||
| IE9, IE10, IE11, Edge | last 2 versions | last 2 versions | last 2 versions | last 2 versions | last 2 versions | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
import { Waterfall } from '@antv/g2plot'; | ||
|
||
const data = [ | ||
{ type: '日用品', money: 120 }, | ||
{ type: '伙食费', money: 900 }, | ||
{ type: '交通费', money: 200 }, | ||
{ type: '水电费', money: 300 }, | ||
{ type: '房租', money: 1200 }, | ||
{ type: '商场消费', money: 1000 }, | ||
{ type: '应酬红包', money: -2000 }, | ||
]; | ||
|
||
const waterfallPlot = new Waterfall(document.getElementById('container'), { | ||
title: { | ||
visible: true, | ||
text: '每月收支情况(瀑布图)', | ||
}, | ||
forceFit: true, | ||
data, | ||
padding: 'auto', | ||
data, | ||
xField: 'type', | ||
yField: 'money', | ||
meta: { | ||
type: { | ||
alias: '类别', | ||
}, | ||
money: { | ||
alias: '金额', | ||
}, | ||
}, | ||
}); | ||
|
||
waterfallPlot.render(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
{ | ||
"title": { | ||
"zh": "中文分类", | ||
"en": "Category" | ||
}, | ||
"demos": [ | ||
{ | ||
"filename": "basic.js", | ||
"title": "基础瀑布图", | ||
"screenshot": "https://gw.alipayobjects.com/mdn/rms_d314dd/afts/img/A*gTX1T6UddcYAAAAAAAAAAABkARQnAQ" | ||
} | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
title: 设计规范 | ||
--- | ||
|
||
设计规范 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
title: 设计规范 | ||
--- | ||
|
||
设计规范 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
--- | ||
title: Waterfall Chart | ||
order: 5 | ||
--- | ||
|
||
Description about this component. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
title: 瀑布图 | ||
order: 5 | ||
--- | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
import { Group, BBox } from '@antv/g'; | ||
import { View } from '@antv/g2'; | ||
import * as _ from '@antv/util'; | ||
import { VALUE_FIELD, IS_TOTAL } from '../../layer'; | ||
|
||
export interface DiffLabelcfg { | ||
view: View; | ||
fields: string[]; | ||
formatter: (text: string, item: object, idx: number) => string; | ||
style?: { | ||
fill?: string; | ||
stroke?: string; | ||
strokeOpacity?: number; | ||
[k: string]: any; | ||
}; | ||
} | ||
|
||
function getDefaultCfg() { | ||
return { | ||
fill: '#fff', | ||
fontSize: 12, | ||
lineHeight: 12, | ||
stroke: 'rgba(0, 0, 0, 0.45)', | ||
}; | ||
} | ||
|
||
export default class DiffLabel { | ||
private view: View; | ||
private fields: string[]; | ||
private container: Group; | ||
private formatter: (text: string, item: object, idx: number) => string; | ||
private textAttrs: object = {}; | ||
|
||
constructor(cfg: DiffLabelcfg) { | ||
this.view = cfg.view; | ||
this.fields = cfg.fields; | ||
this.formatter = cfg.formatter; | ||
this.textAttrs = _.mix(getDefaultCfg(), cfg.style); | ||
|
||
this._init(); | ||
} | ||
|
||
/** 绘制辅助labels */ | ||
public draw() { | ||
if (!this.view || this.view.destroyed) { | ||
return; | ||
} | ||
const data = _.clone(this.view.get('data')); | ||
this.container = this.view.get('frontgroundGroup').addGroup(); | ||
const shapes = this.view | ||
.get('elements')[0] | ||
.getShapes() | ||
.filter((s) => s.name === 'interval'); | ||
const labelsGroup = new Group(); | ||
|
||
_.each(shapes, (shape, idx) => { | ||
if (!shape.get('origin')) return; | ||
const _origin = shape.get('origin')._origin; | ||
const shapeBox: BBox = shape.getBBox(); | ||
const values = _origin[VALUE_FIELD]; | ||
let diff = values; | ||
if (_.isArray(values)) { | ||
diff = values[1] - values[0]; | ||
} | ||
diff = diff > 0 ? `+${diff}` : diff; | ||
/** is total, total do not need `+` sign */ | ||
if (_origin[IS_TOTAL]) { | ||
diff = values[0] - values[1]; | ||
} | ||
let formattedText = diff; | ||
if (this.formatter) { | ||
const color = shapes[idx].attr('fill'); | ||
formattedText = this.formatter(`${diff}`, { _origin: data[idx], color }, idx); | ||
} | ||
const text = labelsGroup.addShape('text', { | ||
attrs: { | ||
text: formattedText, | ||
textBaseline: 'middle', | ||
textAlign: 'center', | ||
x: (shapeBox.minX + shapeBox.maxX) / 2, | ||
y: (shapeBox.minY + shapeBox.maxY) / 2, | ||
...this.textAttrs, | ||
}, | ||
}); | ||
if (text.getBBox().height > shapeBox.height) { | ||
text.set('visible', false); | ||
} | ||
}); | ||
this.container.add(labelsGroup); | ||
this.view.get('canvas').draw(); | ||
} | ||
|
||
public clear() { | ||
if (this.container) { | ||
this.container.clear(); | ||
} | ||
} | ||
|
||
private _init() { | ||
this.view.on('beforerender', () => { | ||
this.clear(); | ||
}); | ||
|
||
this.view.on('afterrender', () => { | ||
this.draw(); | ||
}); | ||
} | ||
} |
Oops, something went wrong.