mirror of
https://gitee.com/antv/g6.git
synced 2024-12-04 20:59:15 +08:00
feat: add sankye table interface && demo
This commit is contained in:
parent
78bacb9bbd
commit
7228bcd802
167
demos/assets/data/atm-investment.json
Normal file
167
demos/assets/data/atm-investment.json
Normal file
@ -0,0 +1,167 @@
|
||||
[
|
||||
{
|
||||
"group": "Tencent",
|
||||
"company": "Didi Chuxing",
|
||||
"cap": 56
|
||||
},
|
||||
{
|
||||
"group": "Alibaba",
|
||||
"company": "Didi Chuxing",
|
||||
"cap": 56
|
||||
},
|
||||
{
|
||||
"group": "Ant",
|
||||
"company": "Didi Chuxing",
|
||||
"cap": 56
|
||||
},
|
||||
{
|
||||
"group": "Tencent",
|
||||
"company": "Meituan-Dianping",
|
||||
"cap": 30
|
||||
},
|
||||
{
|
||||
"group": "Alibaba",
|
||||
"company": "Meituan-Dianping",
|
||||
"cap": 30
|
||||
},
|
||||
{
|
||||
"group": "Tencent",
|
||||
"company": "Pingduoduo",
|
||||
"cap": 21
|
||||
},
|
||||
{
|
||||
"group": "Alibaba",
|
||||
"company": "Ele.me",
|
||||
"cap": 9.5
|
||||
},
|
||||
{
|
||||
"group": "Tencent",
|
||||
"company": "China Literature",
|
||||
"cap": 7.6
|
||||
},
|
||||
{
|
||||
"group": "Alibaba",
|
||||
"company": "Zhong An Insurance",
|
||||
"cap": 6.5
|
||||
},
|
||||
{
|
||||
"group": "Tencent",
|
||||
"company": "Guahao Tech/Weiyi/WeDoctor",
|
||||
"cap": 6
|
||||
},
|
||||
{
|
||||
"group": "Alibaba",
|
||||
"company": "Easyhome",
|
||||
"cap": 5.7
|
||||
},
|
||||
{
|
||||
"group": "Alibaba",
|
||||
"company": "Meizu",
|
||||
"cap": 4.6
|
||||
},
|
||||
{
|
||||
"group": "Alibaba",
|
||||
"company": "SenseTime",
|
||||
"cap": 4.5
|
||||
},
|
||||
{
|
||||
"group": "Alibaba",
|
||||
"company": "Xiaohongshu/Little Red Book",
|
||||
"cap": 3
|
||||
},
|
||||
{
|
||||
"group": "Tencent",
|
||||
"company": "Xiaohongshu/Little Red Book",
|
||||
"cap": 3
|
||||
},
|
||||
{
|
||||
"group": "Tencent",
|
||||
"company": "Kuaishou",
|
||||
"cap": 3
|
||||
},
|
||||
{
|
||||
"group": "Tencent",
|
||||
"company": "VIPKid",
|
||||
"cap": 3
|
||||
},
|
||||
{
|
||||
"group": "Tencent",
|
||||
"company": "NIO",
|
||||
"cap": 2.9
|
||||
},
|
||||
{
|
||||
"group": "Alibaba",
|
||||
"company": "Cambricon",
|
||||
"cap": 2
|
||||
},
|
||||
{
|
||||
"group": "Tencent",
|
||||
"company": "Weiying Technology/Wepiao",
|
||||
"cap": 2
|
||||
},
|
||||
{
|
||||
"group": "Tencent",
|
||||
"company": "Full Truck Alliance Group",
|
||||
"cap": 2
|
||||
},
|
||||
{
|
||||
"group": "Ant",
|
||||
"company": "Ucommune",
|
||||
"cap": 1.7
|
||||
},
|
||||
{
|
||||
"group": "Tencent",
|
||||
"company": "Douyu",
|
||||
"cap": 1.5
|
||||
},
|
||||
{
|
||||
"group": "Ant",
|
||||
"company": "Hellobike",
|
||||
"cap": 1.5
|
||||
},
|
||||
{
|
||||
"group": "Ant",
|
||||
"company": "UrWork",
|
||||
"cap": 1.4
|
||||
},
|
||||
{
|
||||
"group": "Ant",
|
||||
"company": "ofo",
|
||||
"cap": 1
|
||||
},
|
||||
{
|
||||
"group": "Alibaba",
|
||||
"company": "ofo",
|
||||
"cap": 1
|
||||
},
|
||||
{
|
||||
"group": "Alibaba",
|
||||
"company": "iTutorGroup",
|
||||
"cap": 1
|
||||
},
|
||||
{
|
||||
"group": "Alibaba",
|
||||
"company": "Face++",
|
||||
"cap": 1
|
||||
},
|
||||
{
|
||||
"group": "Ant",
|
||||
"company": "Face++",
|
||||
"cap": 1
|
||||
},
|
||||
{
|
||||
"group": "Tencent",
|
||||
"company": "iCarbonX",
|
||||
"cap": 1
|
||||
},
|
||||
{
|
||||
"group": "Tencent",
|
||||
"company": "Zhihu",
|
||||
"cap": 1
|
||||
},
|
||||
{
|
||||
"group": "Tencent",
|
||||
"company": "Ding Xiang Yuan",
|
||||
"cap": 1
|
||||
}
|
||||
]
|
126
demos/gallery-atm-investment.html
Normal file
126
demos/gallery-atm-investment.html
Normal file
@ -0,0 +1,126 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<title>画廊-美国总统信息桑基图</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="mountNode"></div>
|
||||
<script src="./assets/jquery-3.2.1.min.js"></script>
|
||||
<script src="../build/g6.js"></script>
|
||||
<script src="../build/plugin.template.tableSankey.js"></script>
|
||||
<script>
|
||||
$.getJSON('./assets/data/atm-investment.json', table => {
|
||||
const sankeyPlugin = new G6.Plugins['template.tableSankey']({
|
||||
table,
|
||||
onBeforeSankeyProcessorExecute(sankeyProcessor) {
|
||||
sankeyProcessor.nodeWidth(4);
|
||||
sankeyProcessor.nodePadding(16);
|
||||
},
|
||||
onBeforeRender(graph) {
|
||||
const width = graph.getWidth();
|
||||
graph.node({
|
||||
color() {
|
||||
return 'black';
|
||||
},
|
||||
style(model) {
|
||||
const style = {
|
||||
lineWidth:0
|
||||
}
|
||||
if (model.field === 'cap') {
|
||||
style.fillOpacity = 0;
|
||||
}
|
||||
return style;
|
||||
},
|
||||
label: model => {
|
||||
const label = {
|
||||
text: model.field === 'cap' ? '$ ' + model.fieldValue : model.fieldValue,
|
||||
...this.labelStyle
|
||||
};
|
||||
if (model.x > width / 2) {
|
||||
label.textAlign = 'right';
|
||||
} else {
|
||||
label.textAlign = 'left';
|
||||
}
|
||||
return label;
|
||||
},
|
||||
labelOffsetX(model) {
|
||||
if (model.field === 'cap') {
|
||||
return 0;
|
||||
}
|
||||
const labelGap = 8;
|
||||
if (model.x > width / 2) {
|
||||
return -(model.x1 - model.x0) / 2 - labelGap;
|
||||
}
|
||||
return (model.x1 - model.x0) / 2 + labelGap;
|
||||
}
|
||||
});
|
||||
graph.edge({
|
||||
sourceOffset(model) {
|
||||
const {source, target} = model;
|
||||
if(target.indexOf('cap') !== -1) {
|
||||
const sourceItem = graph.find(source);
|
||||
const sourceLabel = sourceItem.getLabel();
|
||||
const sourceLabelBox = sourceLabel.getBBox();
|
||||
return sourceLabelBox.width+10;
|
||||
}
|
||||
},
|
||||
targetOffset(model) {
|
||||
const {target} = model;
|
||||
if(target.indexOf('cap') !== -1) {
|
||||
const targetItem = graph.find(target);
|
||||
const targetLabel = targetItem.getLabel();
|
||||
const targetLabelBox = targetLabel.getBBox();
|
||||
return targetLabelBox.width;
|
||||
}
|
||||
},
|
||||
style(model){
|
||||
const {source, target} = model;
|
||||
const strokeOpacity = 0.6;
|
||||
let stroke = '#333';
|
||||
if (source === 'groupTencent') {
|
||||
stroke = "#61C489";
|
||||
} else if(source === 'groupAlibaba') {
|
||||
stroke = '#E7AC45';
|
||||
} else if(source === 'groupAnt') {
|
||||
stroke = '#326DF6';
|
||||
}
|
||||
if(target.indexOf('cap') !== -1) {
|
||||
return {
|
||||
lineWidth: 1,
|
||||
lineDash: [1, 1]
|
||||
};
|
||||
}
|
||||
return {
|
||||
stroke,
|
||||
strokeOpacity
|
||||
};
|
||||
}
|
||||
});
|
||||
},
|
||||
combine({ field, value, row }) {
|
||||
if (field === 'cap') {
|
||||
return row.company + field + value;
|
||||
}
|
||||
return field + value;
|
||||
}
|
||||
});
|
||||
new G6.Graph({
|
||||
container: 'mountNode',
|
||||
width: 500,
|
||||
height: 600,
|
||||
fitView: 'cc',
|
||||
animate: true,
|
||||
plugins: [ sankeyPlugin ]
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
||||
|
@ -98,7 +98,7 @@
|
||||
"screenshot": "node ./bin/screenshot.js",
|
||||
"start": "npm run dev",
|
||||
"test": "torch --compile --renderer --recursive ./test/unit",
|
||||
"test-live": "torch --compile --interactive --watch --recursive ./test/unit/",
|
||||
"test-live": "torch --compile --interactive --watch --recursive ./test/unit/plugins/template.tableSankey-spec",
|
||||
"watch": "webpack --config webpack-dev.config.js",
|
||||
"win-dev": "node ./bin/win-dev.js"
|
||||
},
|
||||
|
@ -42,20 +42,20 @@ G6.registerEdge('sankey-edge', {
|
||||
const targetBox = target.getBBox();
|
||||
const sourceModel = source.getModel();
|
||||
const targetModel = target.getModel();
|
||||
let { y0, y1 } = model;
|
||||
let { y0, y1, sourceOffset = 0, targetOffset = 0 } = model;
|
||||
y0 = sourceBox.minY + y0 - sourceModel.y0;
|
||||
y1 = targetBox.minY + y1 - targetModel.y0;
|
||||
if (sourceBox.centerX < targetBox.centerX) {
|
||||
const hgap = targetBox.minX - sourceBox.maxX;
|
||||
return [
|
||||
[ 'M', sourceBox.maxX, y0 ],
|
||||
[ 'C', sourceBox.maxX + hgap / 4, y0, targetBox.minX - hgap / 2, y1, targetBox.minX, y1 ]
|
||||
];
|
||||
let startX = sourceBox.maxX + sourceOffset;
|
||||
let endX = targetBox.minX - targetOffset;
|
||||
let hgap = endX - startX;
|
||||
if (sourceBox.centerX > targetBox.centerX) {
|
||||
startX = targetBox.maxX + targetOffset;
|
||||
endX = sourceBox.minX - sourceOffset;
|
||||
hgap = endX - startX;
|
||||
}
|
||||
const hgap = sourceBox.minX - targetBox.maxX;
|
||||
return [
|
||||
[ 'M', targetBox.maxX, y1 ],
|
||||
[ 'C', targetBox.maxX + hgap / 4, y1, sourceBox.minX - hgap / 2, y0, sourceBox.minX, y0 ]
|
||||
[ 'M', startX, y0 ],
|
||||
[ 'C', startX + hgap / 4, y0, endX - hgap / 2, y1, endX, y1 ]
|
||||
];
|
||||
}
|
||||
});
|
||||
@ -67,27 +67,30 @@ G6.registerGuide('col-names', {
|
||||
const nodes = graph.getNodes();
|
||||
const model = item.getModel();
|
||||
const colMap = {};
|
||||
const { textStyle } = model;
|
||||
const { textStyle, fields } = model;
|
||||
let minY = Infinity;
|
||||
nodes.forEach(node => {
|
||||
const model = node.getModel();
|
||||
const { field, y, x } = model;
|
||||
const { field, y, x, colIndex } = model;
|
||||
|
||||
if (minY > y) {
|
||||
minY = y;
|
||||
}
|
||||
if (!colMap[field]) {
|
||||
colMap[field] = {
|
||||
field,
|
||||
x
|
||||
x,
|
||||
colIndex
|
||||
};
|
||||
}
|
||||
});
|
||||
Util.each(colMap, ({ field, x }) => {
|
||||
Util.each(colMap, ({ field, x, colIndex }) => {
|
||||
group.addShape('text', {
|
||||
attrs: {
|
||||
text: field,
|
||||
x,
|
||||
y: minY - 12,
|
||||
textAlign: colIndex === fields.length - 1 ? 'right' : 'left',
|
||||
...textStyle
|
||||
}
|
||||
});
|
||||
@ -123,8 +126,7 @@ class Plugin {
|
||||
* @type {object} colNameTextStyle - col name text style
|
||||
*/
|
||||
colNameTextStyle: {
|
||||
fill: '#333',
|
||||
textAlign: 'center'
|
||||
fill: '#333'
|
||||
},
|
||||
|
||||
/**
|
||||
@ -147,15 +149,25 @@ class Plugin {
|
||||
/**
|
||||
* @type {function} combine - comine the node id
|
||||
* @param {object} cfg - combine cfg
|
||||
* @property {string} cfg.field - input object
|
||||
* @property {string} cfg.value - input object
|
||||
* @property {string} cfg.colIndex - input object
|
||||
* @property {object} cfg.rowIndex - input object
|
||||
* @property {string} cfg.field -field
|
||||
* @property {string} cfg.value - value
|
||||
* @property {string} cfg.colIndex - colIndex
|
||||
* @property {object} cfg.rowIndex - rowIndex
|
||||
* @property {object} cfg.row - row
|
||||
* @return {string} combine id
|
||||
*/
|
||||
combine({ field, value }) {
|
||||
return field + value;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* @type {function} onBeforeSankeyProcessorExecute - trigger before sankeyProcessor execute
|
||||
*/
|
||||
onBeforeSankeyProcessorExecute(/* sankeyProcessor */) {},
|
||||
|
||||
/**
|
||||
* @type {function} onBeforeSankeyProcessorExecute - trigger after sankeyProcessor execute
|
||||
*/
|
||||
onAfterSankeyProcessorExecute(/* sankeyProcessor */) {}
|
||||
}, options);
|
||||
}
|
||||
_getFields() {
|
||||
@ -181,7 +193,7 @@ class Plugin {
|
||||
table.forEach((row, rowIndex) => {
|
||||
fields.forEach((field, colIndex) => {
|
||||
const value = row[field];
|
||||
const id = this.combine({ field, value, colIndex, rowIndex });
|
||||
const id = this.combine({ field, value, colIndex, rowIndex, row });
|
||||
if (!map[id]) {
|
||||
map[id] = {
|
||||
id,
|
||||
@ -206,8 +218,8 @@ class Plugin {
|
||||
}
|
||||
const value = row[field];
|
||||
const nextValue = row[nextField];
|
||||
const source = this.combine({ field, value, colIndex, rowIndex });
|
||||
const target = this.combine({ field: nextField, value: nextValue, colIndex: nextColIndex, rowIndex });
|
||||
const source = this.combine({ field, value, colIndex, rowIndex, row });
|
||||
const target = this.combine({ field: nextField, value: nextValue, colIndex: nextColIndex, rowIndex, row });
|
||||
const id = source + '-' + target;
|
||||
if (!map[id]) {
|
||||
map[id] = {
|
||||
@ -276,7 +288,8 @@ class Plugin {
|
||||
if (this.showColName) {
|
||||
guides.push({
|
||||
shape: 'col-names',
|
||||
textStyle: this.colNameTextStyle
|
||||
textStyle: this.colNameTextStyle,
|
||||
fields: this._getFields()
|
||||
});
|
||||
}
|
||||
return guides;
|
||||
@ -290,7 +303,9 @@ class Plugin {
|
||||
edges: this._getEdges(table, fields),
|
||||
guides: this._getGuides()
|
||||
};
|
||||
this.onBeforeSankeyProcessorExecute(sankeyProcessor);
|
||||
sankeyProcessor(data);
|
||||
this.onAfterSankeyProcessorExecute(sankeyProcessor);
|
||||
data.nodes.forEach(node => {
|
||||
node.x = (node.x0 + node.x1) / 2;
|
||||
node.y = (node.y0 + node.y1) / 2;
|
||||
|
0
test/unit/controller/animate-spec.js
Normal file
0
test/unit/controller/animate-spec.js
Normal file
21
test/unit/plugins/template.tableSankey-spec.js
Normal file
21
test/unit/plugins/template.tableSankey-spec.js
Normal file
@ -0,0 +1,21 @@
|
||||
const G6 = require('../../../src/index');
|
||||
const container = document.createElement('div');
|
||||
const table = require('../../../demos/assets/data/atm-investment.json');
|
||||
container.setAttribute('data-test-spec', 'plugin/template.tableSankey-spec.js');
|
||||
document.body.appendChild(container);
|
||||
require('../../../plugins/template.tableSankey/');
|
||||
describe('tableSankey test', () => {
|
||||
const sankeyPlugin = new G6.Plugins['template.tableSankey']({
|
||||
table
|
||||
});
|
||||
const graph = new G6.Graph({
|
||||
container,
|
||||
height: 600,
|
||||
fitView: 'cc',
|
||||
animate: true,
|
||||
plugins: [ sankeyPlugin ]
|
||||
});
|
||||
it('destroy', () => {
|
||||
graph.destroy();
|
||||
});
|
||||
});
|
Loading…
Reference in New Issue
Block a user