docs: 更新 combo combined 文档; docs: 新增自定义分类图 demo (#3600)

* chore: update version nums

* docs: 更新 combo combined 文档; docs: 新增自定义分类图 demo

* chore: refine
This commit is contained in:
Yanyan Wang 2022-03-28 09:52:08 +08:00 committed by GitHub
parent e0ab79dec2
commit 0179634024
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 359 additions and 14 deletions

View File

@ -57,9 +57,13 @@
"husky": "^4.2.5",
"lerna": "^3.19.0",
"lint-staged": "^10.2.2",
"monaco-editor": "0.29.1",
"monaco-editor-webpack-plugin": "5.0.0",
"normalize-url": "^7.0.3",
"npm-run-all": "^4.1.5",
"prettier": "^2.1.2",
"pretty-quick": "^3.0.2",
"react-monaco-editor": "0.40.0",
"rimraf": "^3.0.0",
"tslint": "^6.1.3",
"tslint-config-airbnb": "^5.11.2",
@ -80,6 +84,10 @@
"react-scripts": "3.1.2"
},
"resolutions": {
"@types/react": "^16.9.35"
"@types/react": "^16.9.35",
"monaco-editor": "0.29.1",
"monaco-editor-webpack-plugin": "5.0.0",
"react-monaco-editor": "^0.40.0",
"normalize-url": "^4.1.0"
}
}

View File

@ -61,7 +61,7 @@
},
"dependencies": {
"@antv/g-base": "^0.5.1",
"@antv/g6-core": "*",
"@antv/g6-core": "0.6.4",
"@antv/util": "~2.0.5"
},
"devDependencies": {

View File

@ -66,7 +66,7 @@
]
},
"dependencies": {
"@antv/g6-pc": "*"
"@antv/g6-pc": "0.6.4"
},
"devDependencies": {
"@babel/core": "^7.7.7",

View File

@ -75,9 +75,9 @@
"@antv/g-canvas": "^0.5.2",
"@antv/g-math": "^0.1.1",
"@antv/g-svg": "^0.5.1",
"@antv/g6-core": "*",
"@antv/g6-element": "*",
"@antv/g6-plugin": "*",
"@antv/g6-core": "0.6.4",
"@antv/g6-element": "0.6.4",
"@antv/g6-plugin": "0.6.4",
"@antv/hierarchy": "^0.6.7",
"@antv/layout": "^0.2.1",
"@antv/matrix-util": "^3.1.0-beta.3",

View File

@ -22,7 +22,7 @@
"@antv/g-base": "^0.5.1",
"@antv/g-canvas": "^0.5.2",
"@antv/g-svg": "^0.5.2",
"@antv/g6-core": "*",
"@antv/g6-core": "0.6.4",
"@antv/matrix-util": "^3.1.0-beta.3",
"@antv/scale": "^0.3.4",
"@antv/util": "^2.0.9",

View File

@ -71,7 +71,7 @@ outerLayout: new G6.Layout['gForce']({
});
```
**Type**: Object<br />**Default**: GForce Instance<br />**Required**: false<br />**Description**: The outer layout instance, should be a sync layout method. Refer to the corresponding layout docs. The default configuration of the `outerLayout` is:
**Type**: Object<br />**Default**: GForce Instance<br />**Required**: false<br />**Description**: The outer layout instance. Refer to the corresponding layout docs. The default configuration of the `outerLayout` is:
```javascript
outerLayout: new G6.Layout['gForce']({
@ -92,7 +92,7 @@ innerLayout: new G6.Layout['grid']({
});
```
**Type**:Object<br />**Default**:Concentric Instance<br />**Required**:false<br />**Description**: The layout method for the items inside a combo. Refer to the corresponding layout docs. The default configuration of the `outerLayout` is:
**Type**:Object<br />**Default**:Concentric Instance<br />**Required**:false<br />**Description**: The layout method for the items inside a combo, should be a sync layout method. Refer to the corresponding layout docs. The default configuration of the `outerLayout` is:
```javascript
outerLayout: new G6.Layout['concentric']({

View File

@ -71,7 +71,7 @@ outerLayout: new G6.Layout['gForce']({
});
```
**类型**Object<br />**默认值**GForce 实例<br />**是否必须**false<br />**说明**:最外层的布局算法,需要使用同步的布局算法,默认为 gForce。具体参数详见被使用布局的文档。
**类型**Object<br />**默认值**GForce 实例<br />**是否必须**false<br />**说明**:最外层的布局算法,默认为 gForce。具体参数详见被使用布局的文档。
默认情况下 gForce 布局将使用以下参数:
```javascript
@ -93,7 +93,7 @@ innerLayout: new G6.Layout['grid']({
});
```
**类型**Object<br />**默认值**Concentric 实例<br />**是否必须**false<br />**说明**ombo 内部的布局算法,默认为 concentric。具体参数详见被使用布局的文档。
**类型**Object<br />**默认值**Concentric 实例<br />**是否必须**false<br />**说明**combo 内部的布局算法,需要使用同步的布局算法,默认为 concentric。具体参数详见被使用布局的文档。
默认情况下 concentric 布局将使用以下参数:
```javascript

View File

@ -20,7 +20,8 @@ Notice that the layouts for Graph cannot be used on TreeGraph.
- [Dagre Layout](./dagre): Arranges the nodes hierarchically;
- [Concentric Layout](./concentric): Arranges the nodes on concentric circles;
- [Grid Layout](./grid): Arranges the nodes on grid.
- [Combo Force Layout](./comboForce)_New feature of V3.5_ Designed for graph with combos.- [Combo Combined Layout](./comboCombined)_New feature of V4.6_ Designed for graph with combos. Support configuring the layout for items inside a combo and the layout for the outer combos and nodes.
- [Combo Force Layout](./comboForce)_New feature of V3.5_ Designed for graph with combos.
- [Combo Combined Layout](./comboCombined)_New feature of V4.6_ Designed for graph with combos. Support configuring the layout for items inside a combo and the layout for the outer combos and nodes.
## Configure to Gaph

View File

@ -286,3 +286,17 @@ General graph layout API: [General Graph Layout API](/en/docs/api/graphLayout/gu
| depthRepulsiveForceScale | Number | | 2 | The scale for adjusting the strength of repulsive force between nodes with different depths. The range is [1, Infinity]. Lager the depth difference, larger the attractive force strength |
| velocityDecay | Number | 0.2 | 0.6 | The decay speed of the moving velocity of nodes for each iteration |
| workerEnabled | Boolean | true / false | false | Whether to enable the web-worker in case layout calculation takes too long to block page interaction |
### Combo Combined
<img src='https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*ZlvWS7xOkjMAAAAAAAAAAAAAARQnAQ' width=300 alt='img' /><br />**API**[Combo Combined API](/en/docs/api/graphLayout/comboCombined)<br />**Parameters**
| Name | Type | Example/Options | Default | Description |
| --- | --- | --- | --- | --- |
| center | Array | [ 0, 0 ] | The center of the graph | The center of the layout |
| nodeSize | Array / Number | 10 | 10 | The diameter of the node. It is used for preventing node overlappings. If `nodeSize` is not assigned, the size property in node data will take effect. If the size in node data does not exist either, `nodeSize` is assigned to 10 by default |
| spacing | Number / Function | 10 | 0 | Takes effect when the `preventNodeOverlap` or `preventOverlap` is `true`. The minimum distances between nodes and combos to prevent overlappings. It can be a function to assign different values for different items |
| comboPadding | Number / Function | 10 | 10 | The padding inside a Combo, not for rendering but for force calculation. We suggest to assign the corresponding values to the graph config |
| outerLayout | Object | GForce instance | ForceAtlas2 instance | The layout instance for the outer combos. gForce by default. For the parameters, please refer to the corresponding layout docs |
| innerLayout | Object | Concentric instance | Grid instance | The inner layout inside combos. Concentric by default. It should be synchronous algorithm. For the parameters, please refer to the corresponding layout docs |
| workerEnabled | Boolean | true / false | false | Whether to enable the web-worker in case layout calculation takes too long to block page interaction |

View File

@ -26,6 +26,7 @@ order: 0
- [Concentric Layout](#concentric):同心圆布局;
- [Grid Layout](#grid):网格布局;
- [Combo Force Layout](#combo-force)*V3.5 新增。*适用于带有 combo 图的力导向布局,推荐有 combo 的图使用该布局。
- [Combo Combined Layout](#combo-combined)*V4.6 新增。*适用于带有 combo 的图,可自由组合内外布局,默认情况下可以有较好的效果,推荐有 combo 的图使用该布局。
## 配置一般图布局
@ -286,3 +287,17 @@ const graph = new G6.Graph({
| depthRepulsiveForceScale | Number | | 2 | 根据边两端节点层级差距的调整斥力系数的因子,取值范围 [1, Infinity]。层级差距越大,斥力越大 |
| velocityDecay | Number | 0.4 | 0.6 | 每个迭代节点运动速度衰减参数 |
| workerEnabled | Boolean | true / false | false | 是否启用 web-worker 以防布局计算时间过长阻塞页面交互 |
### Combo Combined
<img src='https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*ZlvWS7xOkjMAAAAAAAAAAAAAARQnAQ' width=300 alt='img' /><br />**API**[Combo Combined API](/zh/docs/api/graphLayout/comboCombined)<br />**参数**
| 参数名 | 类型 | 示例 | 默认值 | 说明 |
| --- | --- | --- | --- | --- |
| center | Array | [ 0, 0 ] | 图的中心 | 布局的中心 |
| nodeSize | Array / Number | 10 | 10 | 节点大小(直径)。用于碰撞检测。若不指定,则根据传入的节点的 size 属性计算。若即不指定,节点中也没有 `size`,则默认大小为 `10` |
| spacing | Number / Function | 10 | 0 | `preventNodeOverlap``preventOverlap``true` 时生效, 防止重叠时节点/ combo 边缘间距的最小值。可以是回调函数, 为不同节点设置不同的最小间距 |
| comboPadding | Number / Function | 10 | 10 | Combo 内部的 padding 值,不用于渲染,仅用于计算力。推荐设置为与视图上 combo 内部 padding 值相同的值 |
| outerLayout | Object | GForce 实例 | ForceAtlas2 实例 | 最外层的布局算法,需要使用同步的布局算法,默认为 gForce。具体参数详见被使用布局的文档 |
| innerLayout | Object | Concentric 实例 | Grid 实例 | combo 内部的布局算法,默认为 concentric。具体参数详见被使用布局的文档 |
| workerEnabled | Boolean | true / false | false | 是否启用 web-worker 以防布局计算时间过长阻塞页面交互 |

View File

@ -0,0 +1,296 @@
import G6 from '@antv/g6';
/**
* by Zhihui Hu
* https://www.zhihu.com/people/wisdommm
*/
const width = document.getElementById('container').scrollWidth;
const height = document.getElementById('container').scrollHeight || 500;
/** ====== data preparation ======= */
// the first layer nodes
const baseData = {
nodes: [
{ id: "intention1", time: "2022/03/20", title: "User Intend", expandRange: [0, 2], show: false, startPosition: 0, endPosition: 0 },
{ id: "intention2", time: "2022/03/20", title: "User Intend", expandRange: [1, 3], show: false, startPosition: 0, endPosition: 0 },
{ id: "intention3", time: "2022/03/20", title: "User Intend", expandRange: [3, 5], show: false, startPosition: 0, endPosition: 0 },
],
edges: [
{ id: "edge1", source: "intention1", target: "intention2" },
{ id: "edge2", source: "intention2", target: "intention3" },
]
};
baseData.nodes.forEach(node => {
node.type = 'customNode';
});
// layout for the first layer nodes
const gridLayout = new G6.Layout['grid']({
rows: 1,
width,
sortBy: 'id'
});
gridLayout.init(baseData);
gridLayout.execute()
// the second layer nodes
const rangeData = {
nodes: [
{ id: "0", label: "Like" },
{ id: "1", label: "Follow" },
{ id: "2", label: "Collect" },
{ id: "3", label: "Shop" },
{ id: "4", label: "Pay" },
{ id: "5", label: "Comment" }
],
};
rangeData.nodes.forEach(node => node.visible = false);
const graphData = {
nodes: baseData.nodes.concat(rangeData.nodes),
edges: baseData.edges
}
/** ====== custom a node type ======= */
G6.registerNode('customNode',
{
draw(cfg, group) {
group.shapeMap = {};
const rect = group.addShape('circle', {
attrs: {
x: 0,
y: 0,
r: 50,
lineWidth: 1,
fillOpacity: 1,
radius: 12,
stroke: 'rgba(0,0,0,0.2)',
lineWidth: 1,
fill: '#fff',
},
name: 'rect-intention',
});
// title
group.addShape('text', {
attrs: {
x: 0,
y: -10,
text: cfg.title,
fontSize: 14,
fill: '#000',
fontWeight: 'bold',
textAlign: 'center'
},
name: 'rect-title',
});
// time
group.addShape('text', {
attrs: {
x: 0,
y: 20,
text: cfg.time,
fontSize: 14,
fill: '#999',
textAlign: 'center'
},
name: 'rect-time',
});
if (cfg.expandRange) {
// expand button
group.addShape('rect', {
attrs: {
x: -32.5,
y: 35,
width: 65,
height: 20,
radius: 8,
stroke: '#FF6107',
lineWidth: 1,
fill: '#FFF',
},
name: 'rectBtn',
});
// button text
group.addShape('text', {
attrs: {
x: 0,
y: 51,
text: cfg.show ? '- collapse' : '+ expand',
fill: '#FF6107',
fontSize: 12,
textAlign: 'center'
},
capture: false,
name: 'rectBtn-text',
});
group.shapeMap['rectBtn-text'] = expandShape;
}
const expandShape = group.addShape('polygon', {
attrs: {
points: [
[30, 60],
[-30, 60],
[cfg.startPosition, 200],
[cfg.endPosition, 200],
],
fill: 'l(90) 0:rgba(255,97,7,0.18) 1:rgba(255,97,7,0)',
opacity: 0.5
},
visible: false,
name: 'polygon-shape',
});
expandShape.toBack();
group.shapeMap['polygon-shape'] = expandShape;
return rect;
},
update: (cfg, item) => {
const group = item.getContainer();
const expandText = group.shapeMap?.['rectBtn-text'] || group.find(e => e.get('name') === 'rectBtn-text');
expandText.attr({
text: cfg.show ? '- collapse' : '+ expand'
});
const expandShape = group.shapeMap?.['polygon-shape'] || group.find(e => e.get('name') === 'polygon-shape');
if (cfg.show) {
expandShape.set('visible', true);
expandShape.attr({
points: [
[30, 60],
[-30, 60],
[cfg.startPosition, 200],
[cfg.endPosition, 200],
],
opacity: 0,
});
expandShape.animate({ opacity: 1 }, { duration: 300, repeat: false });
} else {
expandShape.set('visible', false);
expandShape.animate({ opacity: 0 }, { duration: 300, repeat: false });
}
}
},
'single-node'
);
/** ====== init the graph ======= */
const graph = new G6.Graph({
container: 'container',
width,
height,
modes: {
default: ['drag-canvas']
},
defaultNode: {
type: 'circle',
style: {
r: 30,
fill: '#fff',
stroke: '#ccc',
lineWidth: 1,
},
},
defaultEdge: {
style: {
color: "#fff",
stroke: "#FF6107",
lineWidth: 12,
opacity: 0.6
}
}
});
graph.read(graphData);
/** ====== bind listener ======= */
graph.on('rectBtn:click', (e) => {
const model = e.item.getModel();
const { expandRange } = model;
if (expandRange) {
showRoute(model);
}
});
const showRoute = (nodeData) => {
nodeData.show = !nodeData.show;
let { nodes, edges } = graphData;
const routeNodes = [];
const routeEdges = [];
const routeNodesMap = {};
// the nodes will be shown in the second layer
let showRangeIds = new Set();
baseData.nodes.forEach(node => {
if (!node.show) return;
const { expandRange } = node;
for (let i = +expandRange[0]; i < +expandRange[1] + 1; i++) {
showRangeIds.add(i);
}
});
showRangeIds = Array.from(showRangeIds);
showRangeIds.sort((a, b) => a - b);
const rangeLayoutNodes = [];
rangeData.nodes.forEach(node => {
const showIdx = showRangeIds.indexOf(+node.id);
if (showIdx > -1) {
graph.showItem(node.id);
rangeLayoutNodes.push(node);
}
else graph.hideItem(node.id);
});
// layout for the second row nodes
const rangeGridLayout = new G6.Layout['grid']({
rows: 1,
width: 1000,
begin: [0, 200]
});
rangeGridLayout.init({ nodes: rangeLayoutNodes, edges: [] });
rangeGridLayout.execute();
rangeLayoutNodes.forEach(node => graph.update(node.id, { x: node.x, y: node.y }))
// update the 'show', 'startPosition', and 'endPosition' for first layer node
baseData.nodes.forEach((node) => {
const { id: nodeId, expandRange, x } = node;
if (!node.show) {
graph.updateItem(nodeId, { show: false });
return;
}
const [start, end] = expandRange;
graph.updateItem(nodeId, {
show: true,
// the position for the bottom vertexes of the range polygon
startPosition: graph.findById(`${start}`).getModel().x - 30 - x,
endPosition: graph.findById(`${end}`).getModel().x + 30 - x,
});
});
// remove the blue range edges
graph.getEdges().forEach(edge => {
if (edge.getModel().tag === 'range') graph.removeItem(edge);
});
// add new blue range edges
showRangeIds.forEach((id, i) => {
if (i === 0) return;
graph.addItem('edge', {
id: `edge-${Math.random()}`,
source: `${showRangeIds[i - 1]}`,
target: `${id}`,
tag: 'range',
style: {
lineWidth: 2,
stroke: '#8CC2FC'
}
})
})
}
if (typeof window !== 'undefined')
window.onresize = () => {
if (!graph || graph.get('destroyed')) return;
if (!container || !container.scrollWidth || !container.scrollHeight) return;
graph.changeSize(container.scrollWidth, container.scrollHeight);
};

View File

@ -51,6 +51,14 @@
"en": "Donut Transfer"
},
"screenshot": "https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*YK6yRIfKQaIAAAAAAAAAAAAAARQnAQ"
},
{
"filename": "customFlow.js",
"title": {
"zh": "自定义分类图",
"en": "Custom Category Graph"
},
"screenshot": "https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*hk1QTqVIHnIAAAAAAAAAAAAAARQnAQ"
}
]
}
}

View File

@ -1,7 +1,7 @@
{
"private": true,
"name": "@antv/g6-site",
"version": "4.5.5",
"version": "4.6.4",
"description": "G6 sites deployed on gh-pages",
"keywords": [
"antv",
@ -44,7 +44,10 @@
"@types/react-dom": "^16.9.8",
"gatsby": "^2.24.40",
"gh-pages": "^2.1.1",
"monaco-editor": "0.29.1",
"monaco-editor-webpack-plugin": "5.0.0",
"react-i18next": "^11.1.0",
"react-monaco-editor": "0.40.0",
"typedoc": "^0.17.6",
"typedoc-plugin-markdown": "^2.2.11",
"typescript": "^3.6.5"