docs(site): update layout examples (#5728)

* refactor: adjust layout api and exports

* chore: update deploy actions

* docs(site): update layout examples
This commit is contained in:
Aaron 2024-05-13 10:27:15 +08:00 committed by GitHub
parent 454bc13e09
commit 352b5ba1c6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
187 changed files with 1432 additions and 12898 deletions

View File

@ -4,7 +4,7 @@ on:
workflow_dispatch:
push:
branches:
- v5
- v5
jobs:
deploy-site:
@ -16,10 +16,14 @@ jobs:
node-version: 18
- uses: pnpm/action-setup@v2
with:
version: 8
version: 8
- uses: actions/checkout@v2
- run: pnpm install
- run: npx turbo build
- run: |
cd ./packages/site
pnpm run doc
pnpm run build
- run: cp ./packages/site/CNAME ./packages/site/dist/CNAME
- run: |
cd ./packages/site/dist
@ -33,4 +37,4 @@ jobs:
github_token: ${{secrets.PERSONAL_ACCESS_TOKEN}}
directory: ./packages/site/dist
branch: v5-site
force: true
force: true

View File

@ -79,6 +79,7 @@ export { getExtension, getExtensions, register } from './registry';
export { Graph } from './runtime/graph';
export { BaseTransform } from './transforms';
export { idOf } from './utils/id';
export { invokeLayoutMethod } from './utils/layout';
export { omitStyleProps, subStyleProps } from './utils/prefix';
export { Shortcut } from './utils/shortcut';
export { parseSize } from './utils/size';

View File

@ -893,8 +893,8 @@ export class Graph extends EventEmitter {
* <en/> Execute layout
* @apiCategory layout
*/
public layout() {
this.context.layout!.layout();
public async layout() {
await this.context.layout!.layout();
}
/**

View File

@ -203,9 +203,9 @@ export function layoutAdapter(
*
* <en/> Call layout member methods
* @description
* <zh/> G6 @antv/layout
* <zh/> G6 \@antv/layout
*
* <en/> Provide a common way to call methods on G6 layout and @antv/layout layout
* <en/> Provide a common way to call methods on G6 layout and \@antv/layout layout
* @param layout - <zh/> | <en/> Layout instance
* @param method - <zh/> | <en/> Method name
* @param args - <zh/> | <en/> Arguments

View File

@ -1,174 +0,0 @@
import { Graph, Extensions, extend } from '@antv/g6';
const edgeClusterTransform = (dataAUR = {}, options = {}, graphCore) => {
const { dataAdded, dataUpdated, dataRemoved } = dataAUR;
const handler = (data = {}, options = {}, core) => {
const { nodes = [], edges = [] } = data;
const nodeMap = new Map();
graphCore?.getAllNodes().forEach((node) => nodeMap.set(node.id, node));
nodes.forEach((node) => nodeMap.set(node.id, node));
edges.forEach((edge) => {
edge.data.cluster = nodeMap.get(edge.source)?.data.cluster;
});
return data;
};
return {
dataAdded: handler(dataAdded, options, graphCore),
dataUpdated: handler(dataUpdated, options, graphCore),
dataRemoved: dataRemoved,
};
};
class LineLayout {
// implements Layout<{}>
id = 'line-layout';
constructor(options = {}) {
this.options = options;
}
/**
* Return the positions of nodes and edges(if needed).
*/
async execute(graph, options = {}) {
return this.genericLineLayout(false, graph, options);
}
/**
* To directly assign the positions to the nodes.
*/
async assign(graph, options = {}) {
this.genericLineLayout(true, graph, options);
}
async genericLineLayout(assign, graph, options = {}) {
const { height = 500 } = { ...this.options, ...options };
const nodes = graph.getAllNodes();
const edges = graph.getAllEdges();
const layoutNodes = [];
let x = 10;
let lastNodeRadius = 0;
nodes.forEach((node, i) => {
const currentRadius = node.data.keyShape?.r || 16;
if (i > 0) {
x = layoutNodes[i - 1].data.x + lastNodeRadius + currentRadius + 4;
}
layoutNodes.push({
id: node.id,
data: {
x,
y: height * 0.7,
},
});
lastNodeRadius = currentRadius;
});
const result = {
nodes: layoutNodes,
edges,
};
if (assign) {
layoutNodes.forEach((node) => {
graph.mergeNodeData(node.id, {
x: node.data.x,
y: node.data.y,
});
});
}
return result;
}
}
const container = document.getElementById('container');
const width = container.scrollWidth;
const height = container.scrollHeight || 500;
const ExtGraph = extend(Graph, {
edges: {
'quadratic-edge': Extensions.QuadraticEdge,
},
transforms: {
'edge-cluster': edgeClusterTransform,
'transform-v4-data': Extensions.TransformV4Data,
},
layouts: {
'line-layout': LineLayout,
},
});
fetch('https://gw.alipayobjects.com/os/basement_prod/70cde3be-22e8-4291-98f1-4d5a5b75b62f.json')
.then((res) => res.json())
.then((data) => {
const graph = new ExtGraph({
container: 'container',
width,
height,
autoFit: 'view',
transforms: ['transform-v4-data', 'edge-cluster'],
plugins: [
{
type: 'lod-controller',
disableLod: true,
},
],
layout: { type: 'line-layout' },
theme: {
type: 'spec',
specification: {
node: {
dataTypeField: 'cluster',
},
edge: {
dataTypeField: 'cluster',
},
},
},
node: {
keyShape: {
r: 8,
},
labelShape: {
text: {
fields: ['name'],
formatter: (model) => model.data.name,
},
angle: Math.PI / 2,
textAlign: 'left',
offsetX: 10,
maxWidth: 100,
},
anchorPoints: [[0.5, 0]],
},
edge: {
type: 'quadratic-edge',
keyShape: {
opacity: 0.4,
},
},
modes: {
default: ['click-select', 'drag-canvas', 'drag-node'],
},
data,
});
graph.on('afterlayout', (e) => {
const edgeDatas = graph.getAllEdgesData().map((edge) => {
const { id, source, target } = edge;
const sourceData = graph.getNodeData(source);
const targetData = graph.getNodeData(target);
const [width, height] = graph.getSize();
const xSep = (width - 20) / graph.getAllNodesData().length;
const endsSepStep = (targetData.data.x - sourceData.data.x) / xSep;
const sign = endsSepStep < 0 ? 1 : -1;
return {
id,
data: {
keyShape: {
curveOffset: ((sign * width) / 55) * Math.ceil(Math.abs(endsSepStep)),
},
},
};
});
graph.updateData('edge', edgeDatas);
});
window.graph = graph;
});

View File

@ -1,135 +0,0 @@
import { Graph, Extensions, extend } from '@antv/g6';
const container = document.getElementById('container');
const width = container.scrollWidth;
const height = container.scrollHeight || 500;
const edgeClusterTransform = (data = {}, options = {}, graphCore) => {
const { dataAdded, dataUpdated, dataRemoved } = data;
const handler = (data = {}, options = {}, core) => {
const { nodes = [], edges = [] } = data;
const nodeMap = new Map();
nodes.forEach((node) => nodeMap.set(node.id, node));
edges.forEach((edge) => {
edge.data.cluster = nodeMap.get(edge.source).data.cluster;
});
return data;
};
return {
dataAdded: handler(dataAdded, options, graphCore),
dataUpdated: handler(dataUpdated, options, graphCore),
dataRemoved: handler(dataRemoved, options, graphCore),
};
};
const ExtGraph = extend(Graph, {
transforms: {
'edge-cluster': edgeClusterTransform,
'transform-v4-data': Extensions.TransformV4Data,
'map-node-size': Extensions.MapNodeSize,
},
edges: {
'quadratic-edge': Extensions.QuadraticEdge,
},
});
fetch('https://gw.alipayobjects.com/os/basement_prod/70cde3be-22e8-4291-98f1-4d5a5b75b62f.json')
.then((res) => res.json())
.then((data) => {
const graph = new ExtGraph({
container: 'container',
width,
height,
transforms: [
'transform-v4-data',
{
type: 'map-node-size',
field: 'value',
},
'edge-cluster',
],
plugins: [
{
type: 'lod-controller',
disableLod: true,
},
],
layout: {
type: 'circular',
},
theme: {
type: 'spec',
specification: {
node: {
dataTypeField: 'cluster',
},
edge: {
dataTypeField: 'cluster',
},
},
},
node: (model) => {
return {
id: model.id,
data: {
...model.data,
labelShape: {
position: 'right',
textAlign: 'left',
offsetX: 0,
offsetY: 0,
...model.data.labelShape,
text: model.data.name,
maxWidth: 100,
},
animates: {
update: [
{
fields: ['opacity'],
shapeId: 'haloShape',
},
{
fields: ['lineWidth'],
shapeId: 'keyShape',
},
],
},
},
};
},
edge: {
type: 'quadratic-edge',
keyShape: {
opacity: 0.4,
controlPoints: [{ x: width / 2, y: height / 2 }],
},
},
modes: {
default: ['drag-node', 'click-select', 'zoom-canvas', 'drag-canvas'],
},
data,
});
graph.on('afterlayout', (e) => {
const angleUpdates = graph.getAllNodesData().map((node) => {
const { x, y } = node.data;
const vecX = x - width / 2;
const vecY = y - height / 2;
const dist = Math.sqrt(vecX * vecX + vecY * vecY);
let angle = Math.asin(vecY / dist);
if (vecX < 0) angle = -angle;
return {
id: node.id,
data: {
labelShape: {
position: vecX < 0 ? 'center' : 'right',
textAlign: vecX < 0 ? 'right' : 'left',
angle,
},
},
};
});
graph.updateData('node', angleUpdates);
graph.fitView();
});
window.graph = graph;
});

View File

@ -1,24 +0,0 @@
{
"title": {
"zh": "中文分类",
"en": "Category"
},
"demos": [
{
"filename": "basicArcDiagram.js",
"title": {
"zh": "基本弧线图",
"en": "Basic Arc Diagram"
},
"screenshot": "https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*_eivQrJXt8sAAAAAAAAAAABkARQnAQ"
},
{
"filename": "circularArcDiagram.js",
"title": {
"zh": "环形弧线图",
"en": "Circular Arc Diagram"
},
"screenshot": "https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*o-tESYnAAJYAAAAAAAAAAABkARQnAQ"
}
]
}

View File

@ -1,11 +0,0 @@
---
title: Arc Diagram
order: 9
---
Arc diagram is a method of graph visualization. It places the nodes on one axis, and connects the nodes by arcs.
## Usage
- Example 1 : Place the node positions to an axis, and calculate the heights of the arcs according to the distances between endpoints.
- Example 2 : Circular Arc Diagram is a deformation of Arc Diagram. In this example, the nodes are placed on a circle, the edges are quadratic bezier curves.

View File

@ -1,11 +0,0 @@
---
title: Arc Diagram 弧线图
order: 9
---
弧线图是一种图的可视化形式。其将节点排列在一个轴上,边以弧线的形式连接节点。
## 使用指南
- 代码演示 1 :通过指定节点位置,并根据节点间距计算 arc 弧线类型边的弧高度可以实现基础弧线图。
- 代码演示 2 :指定节点位置到圆环上,使用 quadratic 二阶贝塞尔曲线类型的边实现了弧线图的变种 —— 环形弧线图。

View File

@ -1,5 +0,0 @@
---
title: API
---
<embed src="@/docs/api/graphLayout/circular.en.md"></embed>

View File

@ -1,5 +0,0 @@
---
title: API
---
<embed src="@/docs/api/graphLayout/circular.zh.md"></embed>

View File

@ -0,0 +1,25 @@
import { Graph } from '@antv/g6';
fetch('https://assets.antv.antgroup.com/g6/circular.json')
.then((res) => res.json())
.then((data) => {
const graph = new Graph({
container: 'container',
autoFit: 'view',
data,
layout: {
type: 'circular',
},
node: {
style: {
size: 20,
fill: '#EFF4FF',
lineWidth: 1,
stroke: '#5F95FF',
},
},
behaviors: ['drag-canvas', 'drag-element'],
});
graph.render();
});

View File

@ -1,403 +0,0 @@
import { Graph } from '@antv/g6';
const data = {
nodes: [
{
id: '0',
label: '0',
},
{
id: '1',
label: '1',
},
{
id: '2',
label: '2',
},
{
id: '3',
label: '3',
},
{
id: '4',
label: '4',
},
{
id: '5',
label: '5',
},
{
id: '6',
label: '6',
},
{
id: '7',
label: '7',
},
{
id: '8',
label: '8',
},
{
id: '9',
label: '9',
},
{
id: '10',
label: '10',
},
{
id: '11',
label: '11',
},
{
id: '12',
label: '12',
},
{
id: '13',
label: '13',
},
{
id: '14',
label: '14',
},
{
id: '15',
label: '15',
},
{
id: '16',
label: '16',
},
{
id: '17',
label: '17',
},
{
id: '18',
label: '18',
},
{
id: '19',
label: '19',
},
{
id: '20',
label: '20',
},
{
id: '21',
label: '21',
},
{
id: '22',
label: '22',
},
{
id: '23',
label: '23',
},
{
id: '24',
label: '24',
},
{
id: '25',
label: '25',
},
{
id: '26',
label: '26',
},
{
id: '27',
label: '27',
},
{
id: '28',
label: '28',
},
{
id: '29',
label: '29',
},
{
id: '30',
label: '30',
},
{
id: '31',
label: '31',
},
{
id: '32',
label: '32',
},
{
id: '33',
label: '33',
},
],
edges: [
{
source: '0',
target: '1',
},
{
source: '0',
target: '2',
},
{
source: '0',
target: '3',
},
{
source: '0',
target: '4',
},
{
source: '0',
target: '5',
},
{
source: '0',
target: '7',
},
{
source: '0',
target: '8',
},
{
source: '0',
target: '9',
},
{
source: '0',
target: '10',
},
{
source: '0',
target: '11',
},
{
source: '0',
target: '13',
},
{
source: '0',
target: '14',
},
{
source: '0',
target: '15',
},
{
source: '0',
target: '16',
},
{
source: '2',
target: '3',
},
{
source: '4',
target: '5',
},
{
source: '4',
target: '6',
},
{
source: '5',
target: '6',
},
{
source: '7',
target: '13',
},
{
source: '8',
target: '14',
},
{
source: '9',
target: '10',
},
{
source: '10',
target: '22',
},
{
source: '10',
target: '14',
},
{
source: '10',
target: '12',
},
{
source: '10',
target: '24',
},
{
source: '10',
target: '21',
},
{
source: '10',
target: '20',
},
{
source: '11',
target: '24',
},
{
source: '11',
target: '22',
},
{
source: '11',
target: '14',
},
{
source: '12',
target: '13',
},
{
source: '16',
target: '17',
},
{
source: '16',
target: '18',
},
{
source: '16',
target: '21',
},
{
source: '16',
target: '22',
},
{
source: '17',
target: '18',
},
{
source: '17',
target: '20',
},
{
source: '18',
target: '19',
},
{
source: '19',
target: '20',
},
{
source: '19',
target: '33',
},
{
source: '19',
target: '22',
},
{
source: '19',
target: '23',
},
{
source: '20',
target: '21',
},
{
source: '21',
target: '22',
},
{
source: '22',
target: '24',
},
{
source: '22',
target: '25',
},
{
source: '22',
target: '26',
},
{
source: '22',
target: '23',
},
{
source: '22',
target: '28',
},
{
source: '22',
target: '30',
},
{
source: '22',
target: '31',
},
{
source: '22',
target: '32',
},
{
source: '22',
target: '33',
},
{
source: '23',
target: '28',
},
{
source: '23',
target: '27',
},
{
source: '23',
target: '29',
},
{
source: '23',
target: '30',
},
{
source: '23',
target: '31',
},
{
source: '23',
target: '33',
},
{
source: '32',
target: '33',
},
],
};
const graph = new Graph({
container: 'container',
data,
layout: {
type: 'circular',
},
node: {
style: {
size: 20,
fill: '#EFF4FF',
lineWidth: 1,
stroke: '#5F95FF',
},
},
behaviors: ['drag-canvas', 'drag-element'],
});
graph.render();

View File

@ -1,517 +0,0 @@
import { Graph } from '@antv/g6';
const data = {
nodes: [
{
id: '0',
label: '0',
},
{
id: '1',
label: '1',
},
{
id: '2',
label: '2',
},
{
id: '3',
label: '3',
},
{
id: '4',
label: '4',
},
{
id: '5',
label: '5',
},
{
id: '6',
label: '6',
},
{
id: '7',
label: '7',
},
{
id: '8',
label: '8',
},
{
id: '9',
label: '9',
},
{
id: '10',
label: '10',
},
{
id: '11',
label: '11',
},
{
id: '12',
label: '12',
},
{
id: '13',
label: '13',
},
{
id: '14',
label: '14',
},
{
id: '15',
label: '15',
},
{
id: '16',
label: '16',
},
{
id: '17',
label: '17',
},
{
id: '18',
label: '18',
},
{
id: '19',
label: '19',
},
{
id: '20',
label: '20',
},
{
id: '21',
label: '21',
},
{
id: '22',
label: '22',
},
{
id: '23',
label: '23',
},
{
id: '24',
label: '24',
},
{
id: '25',
label: '25',
},
{
id: '26',
label: '26',
},
{
id: '27',
label: '27',
},
{
id: '28',
label: '28',
},
{
id: '29',
label: '29',
},
{
id: '30',
label: '30',
},
{
id: '31',
label: '31',
},
{
id: '32',
label: '32',
},
{
id: '33',
label: '33',
},
],
edges: [
{
source: '0',
target: '1',
},
{
source: '0',
target: '2',
},
{
source: '0',
target: '3',
},
{
source: '0',
target: '4',
},
{
source: '0',
target: '5',
},
{
source: '0',
target: '7',
},
{
source: '0',
target: '8',
},
{
source: '0',
target: '9',
},
{
source: '0',
target: '10',
},
{
source: '0',
target: '11',
},
{
source: '0',
target: '13',
},
{
source: '0',
target: '14',
},
{
source: '0',
target: '15',
},
{
source: '0',
target: '16',
},
{
source: '2',
target: '3',
},
{
source: '4',
target: '5',
},
{
source: '4',
target: '6',
},
{
source: '5',
target: '6',
},
{
source: '7',
target: '13',
},
{
source: '8',
target: '14',
},
{
source: '9',
target: '10',
},
{
source: '10',
target: '22',
},
{
source: '10',
target: '14',
},
{
source: '10',
target: '12',
},
{
source: '10',
target: '24',
},
{
source: '10',
target: '21',
},
{
source: '10',
target: '20',
},
{
source: '11',
target: '24',
},
{
source: '11',
target: '22',
},
{
source: '11',
target: '14',
},
{
source: '12',
target: '13',
},
{
source: '16',
target: '17',
},
{
source: '16',
target: '18',
},
{
source: '16',
target: '21',
},
{
source: '16',
target: '22',
},
{
source: '17',
target: '18',
},
{
source: '17',
target: '20',
},
{
source: '18',
target: '19',
},
{
source: '19',
target: '20',
},
{
source: '19',
target: '33',
},
{
source: '19',
target: '22',
},
{
source: '19',
target: '23',
},
{
source: '20',
target: '21',
},
{
source: '21',
target: '22',
},
{
source: '22',
target: '24',
},
{
source: '22',
target: '25',
},
{
source: '22',
target: '26',
},
{
source: '22',
target: '23',
},
{
source: '22',
target: '28',
},
{
source: '22',
target: '30',
},
{
source: '22',
target: '31',
},
{
source: '22',
target: '32',
},
{
source: '22',
target: '33',
},
{
source: '23',
target: '28',
},
{
source: '23',
target: '27',
},
{
source: '23',
target: '29',
},
{
source: '23',
target: '30',
},
{
source: '23',
target: '31',
},
{
source: '23',
target: '33',
},
{
source: '32',
target: '33',
},
],
};
const descriptionDiv = document.createElement('div');
descriptionDiv.innerHTML = 'Circular layout with radius: take full use of the canvas, ordering: topology';
const container = document.getElementById('container') as HTMLDivElement;
container.appendChild(descriptionDiv);
const graph = new Graph({
container: 'container',
layout: {
type: 'circular',
},
data,
node: {
style: {
size: 20,
fill: '#EFF4FF',
lineWidth: 1,
stroke: '#5F95FF',
},
},
edge: {
style: {
endArrow: {
path: 'M 0,0 L 8,4 L 8,-4 Z',
fill: '#e2e2e2',
},
},
},
behaviors: ['drag-canvas', 'drag-element'],
animation: true,
});
graph.render();
if (typeof window !== 'undefined') {
window.addEventListener('error', () => {
if (time) {
clearInterval(time);
}
});
window.onresize = () => {
if (!graph || graph.destroyed) return;
if (!container || !container.scrollWidth || !container.scrollHeight) return;
graph.resize(container.scrollWidth, container.scrollHeight - 30);
};
}
layoutConfigTranslation();
const time = setInterval(function () {
layoutConfigTranslation();
}, 11500);
/**
*
* @param layoutConfig
*/
function updateLayout(layoutConfig) {
graph.setLayout((current) => ({
...current,
...layoutConfig,
}));
graph.render();
}
/**
*
*/
function layoutConfigTranslation() {
setTimeout(function () {
descriptionDiv.innerHTML = 'Circular layout, radius = 200, divisions = 5, ordering: degree';
updateLayout({
radius: 200,
startAngle: Math.PI / 4,
endAngle: Math.PI,
divisions: 5,
ordering: 'degree',
});
}, 1000);
setTimeout(function () {
descriptionDiv.innerHTML = 'Circular layout, radius = 200, divisions = 3, ordering: degree';
updateLayout({
startAngle: Math.PI / 4,
endAngle: Math.PI,
divisions: 3,
});
}, 2500);
setTimeout(function () {
descriptionDiv.innerHTML = 'Circular layout, radius = 200, divisions = 8, ordering: degree';
updateLayout({
radius: 200,
startAngle: 0,
endAngle: Math.PI / 2,
divisions: 8,
});
}, 4000);
setTimeout(function () {
descriptionDiv.innerHTML =
'Circular layout, radius = 10300(spiral), endAngle: PI, divisions = 1, ordering: degree';
updateLayout({
radius: null,
startRadius: 10,
endRadius: 300,
divisions: 1,
startAngle: 0,
endAngle: Math.PI,
});
}, 5500);
setTimeout(function () {
descriptionDiv.innerHTML =
'Circular layout, radius = 10300(spiral),endAngle: 2 * PI, divisions= 1, ordering: degree';
updateLayout({
endAngle: 2 * Math.PI,
});
}, 7000);
setTimeout(function () {
descriptionDiv.innerHTML = 'Circular layout, radius = 200, ordering: degree';
updateLayout({
radius: 200,
});
}, 8500);
setTimeout(function () {
descriptionDiv.innerHTML = 'Circular layout, radius = 200, ordering: topology';
updateLayout({
radius: 200,
ordering: 'topology',
});
}, 10000);
}

View File

@ -0,0 +1,27 @@
import { Graph } from '@antv/g6';
fetch('https://assets.antv.antgroup.com/g6/circular.json')
.then((res) => res.json())
.then((data) => {
const graph = new Graph({
container: 'container',
autoFit: 'view',
data,
layout: {
type: 'circular',
ordering: 'degree',
},
node: {
style: {
size: 20,
labelText: (d) => d.id,
fill: '#EFF4FF',
lineWidth: 1,
stroke: '#5F95FF',
},
},
behaviors: ['drag-canvas', 'drag-element'],
});
graph.render();
});

View File

@ -1,405 +0,0 @@
import { Graph } from '@antv/g6';
const data = {
nodes: [
{
id: '0',
label: '0',
},
{
id: '1',
label: '1',
},
{
id: '2',
label: '2',
},
{
id: '3',
label: '3',
},
{
id: '4',
label: '4',
},
{
id: '5',
label: '5',
},
{
id: '6',
label: '6',
},
{
id: '7',
label: '7',
},
{
id: '8',
label: '8',
},
{
id: '9',
label: '9',
},
{
id: '10',
label: '10',
},
{
id: '11',
label: '11',
},
{
id: '12',
label: '12',
},
{
id: '13',
label: '13',
},
{
id: '14',
label: '14',
},
{
id: '15',
label: '15',
},
{
id: '16',
label: '16',
},
{
id: '17',
label: '17',
},
{
id: '18',
label: '18',
},
{
id: '19',
label: '19',
},
{
id: '20',
label: '20',
},
{
id: '21',
label: '21',
},
{
id: '22',
label: '22',
},
{
id: '23',
label: '23',
},
{
id: '24',
label: '24',
},
{
id: '25',
label: '25',
},
{
id: '26',
label: '26',
},
{
id: '27',
label: '27',
},
{
id: '28',
label: '28',
},
{
id: '29',
label: '29',
},
{
id: '30',
label: '30',
},
{
id: '31',
label: '31',
},
{
id: '32',
label: '32',
},
{
id: '33',
label: '33',
},
],
edges: [
{
source: '0',
target: '1',
},
{
source: '0',
target: '2',
},
{
source: '0',
target: '3',
},
{
source: '0',
target: '4',
},
{
source: '0',
target: '5',
},
{
source: '0',
target: '7',
},
{
source: '0',
target: '8',
},
{
source: '0',
target: '9',
},
{
source: '0',
target: '10',
},
{
source: '0',
target: '11',
},
{
source: '0',
target: '13',
},
{
source: '0',
target: '14',
},
{
source: '0',
target: '15',
},
{
source: '0',
target: '16',
},
{
source: '2',
target: '3',
},
{
source: '4',
target: '5',
},
{
source: '4',
target: '6',
},
{
source: '5',
target: '6',
},
{
source: '7',
target: '13',
},
{
source: '8',
target: '14',
},
{
source: '9',
target: '10',
},
{
source: '10',
target: '22',
},
{
source: '10',
target: '14',
},
{
source: '10',
target: '12',
},
{
source: '10',
target: '24',
},
{
source: '10',
target: '21',
},
{
source: '10',
target: '20',
},
{
source: '11',
target: '24',
},
{
source: '11',
target: '22',
},
{
source: '11',
target: '14',
},
{
source: '12',
target: '13',
},
{
source: '16',
target: '17',
},
{
source: '16',
target: '18',
},
{
source: '16',
target: '21',
},
{
source: '16',
target: '22',
},
{
source: '17',
target: '18',
},
{
source: '17',
target: '20',
},
{
source: '18',
target: '19',
},
{
source: '19',
target: '20',
},
{
source: '19',
target: '33',
},
{
source: '19',
target: '22',
},
{
source: '19',
target: '23',
},
{
source: '20',
target: '21',
},
{
source: '21',
target: '22',
},
{
source: '22',
target: '24',
},
{
source: '22',
target: '25',
},
{
source: '22',
target: '26',
},
{
source: '22',
target: '23',
},
{
source: '22',
target: '28',
},
{
source: '22',
target: '30',
},
{
source: '22',
target: '31',
},
{
source: '22',
target: '32',
},
{
source: '22',
target: '33',
},
{
source: '23',
target: '28',
},
{
source: '23',
target: '27',
},
{
source: '23',
target: '29',
},
{
source: '23',
target: '30',
},
{
source: '23',
target: '31',
},
{
source: '23',
target: '33',
},
{
source: '32',
target: '33',
},
],
};
const graph = new Graph({
container: 'container',
data,
layout: {
type: 'circular',
ordering: 'degree',
},
node: {
style: {
size: 20,
labelText: (d) => d.id,
fill: '#EFF4FF',
lineWidth: 1,
stroke: '#5F95FF',
},
},
behaviors: ['drag-canvas', 'drag-element'],
});
graph.render();

View File

@ -0,0 +1,33 @@
import { Graph } from '@antv/g6';
fetch('https://assets.antv.antgroup.com/g6/circular.json')
.then((res) => res.json())
.then((data) => {
const graph = new Graph({
container: 'container',
data,
layout: {
type: 'circular',
divisions: 5,
radius: 200,
startAngle: Math.PI / 4,
endAngle: Math.PI,
},
node: {
style: {
size: 20,
fill: '#EFF4FF',
lineWidth: 1,
stroke: '#5F95FF',
},
},
edge: {
style: {
endArrow: true,
},
},
behaviors: ['drag-canvas', 'drag-element'],
});
graph.render();
});

View File

@ -1,415 +0,0 @@
import { Graph } from '@antv/g6';
const data = {
nodes: [
{
id: '0',
label: '0',
},
{
id: '1',
label: '1',
},
{
id: '2',
label: '2',
},
{
id: '3',
label: '3',
},
{
id: '4',
label: '4',
},
{
id: '5',
label: '5',
},
{
id: '6',
label: '6',
},
{
id: '7',
label: '7',
},
{
id: '8',
label: '8',
},
{
id: '9',
label: '9',
},
{
id: '10',
label: '10',
},
{
id: '11',
label: '11',
},
{
id: '12',
label: '12',
},
{
id: '13',
label: '13',
},
{
id: '14',
label: '14',
},
{
id: '15',
label: '15',
},
{
id: '16',
label: '16',
},
{
id: '17',
label: '17',
},
{
id: '18',
label: '18',
},
{
id: '19',
label: '19',
},
{
id: '20',
label: '20',
},
{
id: '21',
label: '21',
},
{
id: '22',
label: '22',
},
{
id: '23',
label: '23',
},
{
id: '24',
label: '24',
},
{
id: '25',
label: '25',
},
{
id: '26',
label: '26',
},
{
id: '27',
label: '27',
},
{
id: '28',
label: '28',
},
{
id: '29',
label: '29',
},
{
id: '30',
label: '30',
},
{
id: '31',
label: '31',
},
{
id: '32',
label: '32',
},
{
id: '33',
label: '33',
},
],
edges: [
{
source: '0',
target: '1',
},
{
source: '0',
target: '2',
},
{
source: '0',
target: '3',
},
{
source: '0',
target: '4',
},
{
source: '0',
target: '5',
},
{
source: '0',
target: '7',
},
{
source: '0',
target: '8',
},
{
source: '0',
target: '9',
},
{
source: '0',
target: '10',
},
{
source: '0',
target: '11',
},
{
source: '0',
target: '13',
},
{
source: '0',
target: '14',
},
{
source: '0',
target: '15',
},
{
source: '0',
target: '16',
},
{
source: '2',
target: '3',
},
{
source: '4',
target: '5',
},
{
source: '4',
target: '6',
},
{
source: '5',
target: '6',
},
{
source: '7',
target: '13',
},
{
source: '8',
target: '14',
},
{
source: '9',
target: '10',
},
{
source: '10',
target: '22',
},
{
source: '10',
target: '14',
},
{
source: '10',
target: '12',
},
{
source: '10',
target: '24',
},
{
source: '10',
target: '21',
},
{
source: '10',
target: '20',
},
{
source: '11',
target: '24',
},
{
source: '11',
target: '22',
},
{
source: '11',
target: '14',
},
{
source: '12',
target: '13',
},
{
source: '16',
target: '17',
},
{
source: '16',
target: '18',
},
{
source: '16',
target: '21',
},
{
source: '16',
target: '22',
},
{
source: '17',
target: '18',
},
{
source: '17',
target: '20',
},
{
source: '18',
target: '19',
},
{
source: '19',
target: '20',
},
{
source: '19',
target: '33',
},
{
source: '19',
target: '22',
},
{
source: '19',
target: '23',
},
{
source: '20',
target: '21',
},
{
source: '21',
target: '22',
},
{
source: '22',
target: '24',
},
{
source: '22',
target: '25',
},
{
source: '22',
target: '26',
},
{
source: '22',
target: '23',
},
{
source: '22',
target: '28',
},
{
source: '22',
target: '30',
},
{
source: '22',
target: '31',
},
{
source: '22',
target: '32',
},
{
source: '22',
target: '33',
},
{
source: '23',
target: '28',
},
{
source: '23',
target: '27',
},
{
source: '23',
target: '29',
},
{
source: '23',
target: '30',
},
{
source: '23',
target: '31',
},
{
source: '23',
target: '33',
},
{
source: '32',
target: '33',
},
],
};
const graph = new Graph({
container: 'container',
data,
layout: {
type: 'circular',
divisions: 5,
radius: 200,
startAngle: Math.PI / 4,
endAngle: Math.PI,
},
node: {
style: {
size: 20,
fill: '#EFF4FF',
lineWidth: 1,
stroke: '#5F95FF',
},
},
edge: {
style: {
endArrow: {
path: 'M 0,0 L 8,4 L 8,-4 Z',
fill: '#e2e2e2',
},
},
},
behaviors: ['drag-canvas', 'drag-element'],
});
graph.render();

View File

@ -5,7 +5,7 @@
},
"demos": [
{
"filename": "basic.ts",
"filename": "basic.js",
"title": {
"zh": "基本 Circular 布局",
"en": "Basic Circular Layout"
@ -13,15 +13,15 @@
"screenshot": "https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*ZSuXQ4PS2F8AAAAAAAAAAABkARQnAQ"
},
{
"filename": "degree.ts",
"filename": "degree.js",
"title": {
"zh": "按照节点度数排序的 Circular 布局",
"en": "Degree Ordered Circular"
"zh": "按照节点度数排序",
"en": "Degree Ordered"
},
"screenshot": "https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*MOEeS6ooB7AAAAAAAAAAAABkARQnAQ"
},
{
"filename": "spiral.ts",
"filename": "spiral.js",
"title": {
"zh": "螺旋线布局",
"en": "Spiral Layout"
@ -29,20 +29,12 @@
"screenshot": "https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*bY0iSqTc3z4AAAAAAAAAAABkARQnAQ"
},
{
"filename": "division.ts",
"filename": "division.js",
"title": {
"zh": "分割环形布局",
"en": "Divided Circular Layout"
},
"screenshot": "https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*u30nQahg6q0AAAAAAAAAAABkARQnAQ"
},
{
"filename": "configurationTranslate.ts",
"title": {
"zh": "Circular 布局参数动态变化",
"en": "Update Configurations for Circular Layout"
},
"screenshot": "https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*D85cS7-yqNEAAAAAAAAAAABkARQnAQ"
}
]
}

View File

@ -0,0 +1,32 @@
import { Graph } from '@antv/g6';
fetch('https://assets.antv.antgroup.com/g6/circular.json')
.then((res) => res.json())
.then((data) => {
const graph = new Graph({
container: 'container',
data,
layout: {
type: 'circular',
startRadius: 10,
endRadius: 300,
},
node: {
style: {
size: 20,
labelText: (d) => d.id,
fill: '#EFF4FF',
lineWidth: 1,
stroke: '#5F95FF',
},
},
edge: {
style: {
endArrow: true,
},
},
behaviors: ['drag-canvas', 'drag-element'],
});
graph.render();
});

View File

@ -1,414 +0,0 @@
import { Graph } from '@antv/g6';
const data = {
nodes: [
{
id: '0',
label: '0',
},
{
id: '1',
label: '1',
},
{
id: '2',
label: '2',
},
{
id: '3',
label: '3',
},
{
id: '4',
label: '4',
},
{
id: '5',
label: '5',
},
{
id: '6',
label: '6',
},
{
id: '7',
label: '7',
},
{
id: '8',
label: '8',
},
{
id: '9',
label: '9',
},
{
id: '10',
label: '10',
},
{
id: '11',
label: '11',
},
{
id: '12',
label: '12',
},
{
id: '13',
label: '13',
},
{
id: '14',
label: '14',
},
{
id: '15',
label: '15',
},
{
id: '16',
label: '16',
},
{
id: '17',
label: '17',
},
{
id: '18',
label: '18',
},
{
id: '19',
label: '19',
},
{
id: '20',
label: '20',
},
{
id: '21',
label: '21',
},
{
id: '22',
label: '22',
},
{
id: '23',
label: '23',
},
{
id: '24',
label: '24',
},
{
id: '25',
label: '25',
},
{
id: '26',
label: '26',
},
{
id: '27',
label: '27',
},
{
id: '28',
label: '28',
},
{
id: '29',
label: '29',
},
{
id: '30',
label: '30',
},
{
id: '31',
label: '31',
},
{
id: '32',
label: '32',
},
{
id: '33',
label: '33',
},
],
edges: [
{
source: '0',
target: '1',
},
{
source: '0',
target: '2',
},
{
source: '0',
target: '3',
},
{
source: '0',
target: '4',
},
{
source: '0',
target: '5',
},
{
source: '0',
target: '7',
},
{
source: '0',
target: '8',
},
{
source: '0',
target: '9',
},
{
source: '0',
target: '10',
},
{
source: '0',
target: '11',
},
{
source: '0',
target: '13',
},
{
source: '0',
target: '14',
},
{
source: '0',
target: '15',
},
{
source: '0',
target: '16',
},
{
source: '2',
target: '3',
},
{
source: '4',
target: '5',
},
{
source: '4',
target: '6',
},
{
source: '5',
target: '6',
},
{
source: '7',
target: '13',
},
{
source: '8',
target: '14',
},
{
source: '9',
target: '10',
},
{
source: '10',
target: '22',
},
{
source: '10',
target: '14',
},
{
source: '10',
target: '12',
},
{
source: '10',
target: '24',
},
{
source: '10',
target: '21',
},
{
source: '10',
target: '20',
},
{
source: '11',
target: '24',
},
{
source: '11',
target: '22',
},
{
source: '11',
target: '14',
},
{
source: '12',
target: '13',
},
{
source: '16',
target: '17',
},
{
source: '16',
target: '18',
},
{
source: '16',
target: '21',
},
{
source: '16',
target: '22',
},
{
source: '17',
target: '18',
},
{
source: '17',
target: '20',
},
{
source: '18',
target: '19',
},
{
source: '19',
target: '20',
},
{
source: '19',
target: '33',
},
{
source: '19',
target: '22',
},
{
source: '19',
target: '23',
},
{
source: '20',
target: '21',
},
{
source: '21',
target: '22',
},
{
source: '22',
target: '24',
},
{
source: '22',
target: '25',
},
{
source: '22',
target: '26',
},
{
source: '22',
target: '23',
},
{
source: '22',
target: '28',
},
{
source: '22',
target: '30',
},
{
source: '22',
target: '31',
},
{
source: '22',
target: '32',
},
{
source: '22',
target: '33',
},
{
source: '23',
target: '28',
},
{
source: '23',
target: '27',
},
{
source: '23',
target: '29',
},
{
source: '23',
target: '30',
},
{
source: '23',
target: '31',
},
{
source: '23',
target: '33',
},
{
source: '32',
target: '33',
},
],
};
const graph = new Graph({
container: 'container',
data,
layout: {
type: 'circular',
startRadius: 10,
endRadius: 300,
},
node: {
style: {
size: 20,
labelText: (d) => d.id,
fill: '#EFF4FF',
lineWidth: 1,
stroke: '#5F95FF',
},
},
edge: {
style: {
endArrow: {
path: 'M 0,0 L 8,4 L 8,-4 Z',
fill: '#e2e2e2',
},
},
},
behaviors: ['drag-canvas', 'drag-element'],
});
graph.render();

View File

@ -1,16 +1,4 @@
---
title: Circular Layout
order: 4
---
Circular layout orders the nodes according to the configuration, and then places the nodes on a circle.
## Usage
As the demo below, you can deploy it in `layout` while instantiating Graph. it can also be used for [Subgraph Layout](/en/docs/manual/middle/layout/sub-layout). By tuning the configurations, you can adjust the radius, start angle, end angle, nodes' order method, divisions, spiral style, and so on.
- Example 1 : Basic Circular Layout, the nodes are placed on the circle clockwise in the data order.
- Example 2 : The nodes are placed on the circle clockwise according to their degrees.
- Example 3 : Spiral layout.
- Example 4 : Divide the nodes into several divisions on the circle.
- Example 5 : Translate the parameters in dynamic.
---

View File

@ -1,16 +1,4 @@
---
title: Circular 环形布局
order: 4
---
Circular 环形布局根据参数指定的排序方式对节点进行排序后,将节点排列在圆环上。
## 使用指南
G6 内置的 Circular 环形布局可在实例化 Graph 时使用该布局。除此之外,还可以如[子图布局](/zh/docs/manual/middle/layout/sub-layout)所示单独使用布局。该布局可以通过配置调整半径、起始和结束角度、节点排序方式、节点分组、螺旋线布局等。
- 代码演示 1 :基本的环形布局,节点根据在数据中的顺序逆时针排列。
- 代码演示 2 :节点根据其度数从大到小逆时针排列。
- 代码演示 3 :螺旋线布局。
- 代码演示 4 :分组圆环布局。
- 代码演示 5 :圆环布局参数动态变化。
---

View File

@ -0,0 +1,37 @@
import { Graph } from '@antv/g6';
fetch('https://assets.antv.antgroup.com/g6/combo.json')
.then((res) => res.json())
.then((data) => {
const graph = new Graph({
container: 'container',
data,
layout: {
type: 'combo-combined',
comboPadding: 2,
},
node: {
style: {
size: 20,
labelText: (d) => d.id,
},
palette: {
type: 'group',
field: (d) => d.combo,
},
},
edge: {
style: (model) => {
const { size, color } = model.data;
return {
stroke: color || '#99ADD1',
lineWidth: size || 1,
};
},
},
behaviors: ['drag-element', 'drag-canvas', 'zoom-canvas'],
autoFit: 'view',
});
graph.render();
});

View File

@ -5,7 +5,7 @@
},
"demos": [
{
"filename": "comboCombined.ts",
"filename": "combo-combined.js",
"title": {
"zh": "ComboCombined 组合布局",
"en": "Combo Combined Layout"

View File

@ -0,0 +1,4 @@
---
title: Combo Layout
order: 2
---

View File

@ -0,0 +1,4 @@
---
title: 组合布局
order: 2
---

View File

@ -1,11 +0,0 @@
---
title: API
---
## ComboCombined
<embed src="@/docs/api/graphLayout/comboCombined.en.md"></embed>
## ComboForce
<embed src="@/docs/api/graphLayout/comboForce.en.md"></embed>

View File

@ -1,11 +0,0 @@
---
title: API
---
## ComboCombined - Combo 组合布局
<embed src="@/docs/api/graphLayout/comboCombined.zh.md"></embed>
## ComboForce - Combo 力导布局
<embed src="@/docs/api/graphLayout/comboForce.zh.md"></embed>

View File

@ -1,572 +0,0 @@
import { Graph } from '@antv/g6';
const data = {
nodes: [
{
id: '0',
style: {
parentId: 'a',
},
},
{
id: '1',
style: {
parentId: 'a',
},
},
{
id: '2',
style: {
parentId: 'a',
},
},
{
id: '3',
style: {
parentId: 'a',
},
},
{
id: '4',
style: {
parentId: 'a',
},
},
{
id: '5',
style: {
parentId: 'a',
},
},
{
id: '6',
style: {
parentId: 'a',
},
},
{
id: '7',
style: {
parentId: 'a',
},
},
{
id: '8',
style: {
parentId: 'a',
},
},
{
id: '9',
style: {
parentId: 'a',
},
},
{
id: '10',
style: {
parentId: 'a',
},
},
{
id: '11',
style: {
parentId: 'a',
},
},
{
id: '12',
style: {
parentId: 'a',
},
},
{
id: '13',
style: {
parentId: 'a',
},
},
{
id: '14',
style: {
parentId: 'a',
},
},
{
id: '15',
style: {
parentId: 'a',
},
},
{
id: '16',
style: {
parentId: 'b',
},
},
{
id: '17',
style: {
parentId: 'b',
},
},
{
id: '18',
style: {
parentId: 'b',
},
},
{
id: '19',
style: {
parentId: 'b',
},
},
{
id: '20',
},
{
id: '21',
},
{
id: '22',
},
{
id: '23',
style: {
parentId: 'c',
},
},
{
id: '24',
style: {
parentId: 'a',
},
},
{
id: '25',
},
{
id: '26',
},
{
id: '27',
style: {
parentId: 'c',
},
},
{
id: '28',
style: {
parentId: 'c',
},
},
{
id: '29',
style: {
parentId: 'c',
},
},
{
id: '30',
style: {
parentId: 'c',
},
},
{
id: '31',
style: {
parentId: 'c',
},
},
{
id: '32',
style: {
parentId: 'd',
},
},
{
id: '33',
style: {
parentId: 'd',
},
},
],
edges: [
// {
// id: 'edge-647',
// source: 'a',
// target: 'b',
// data: {
// label: 'Combo A - Combo B',
// size: 3,
// color: 'red',
// },
// },
// {
// id: 'edge-623',
// source: 'a',
// target: '33',
// data: {
// label: 'Combo-Node',
// size: 3,
// color: 'blue',
// },
// },
{
id: 'edge-444',
source: '0',
target: '1',
},
{
id: 'edge-123',
source: '0',
target: '2',
},
{
id: 'edge-689',
source: '0',
target: '3',
},
{
id: 'edge-625',
source: '0',
target: '4',
},
{
id: 'edge-110',
source: '0',
target: '5',
},
{
id: 'edge-783',
source: '0',
target: '7',
},
{
id: 'edge-72',
source: '0',
target: '8',
},
{
id: 'edge-165',
source: '0',
target: '9',
},
{
id: 'edge-109',
source: '0',
target: '10',
},
{
id: 'edge-764',
source: '0',
target: '11',
},
{
id: 'edge-596',
source: '0',
target: '13',
},
{
id: 'edge-711',
source: '0',
target: '14',
},
{
id: 'edge-81',
source: '0',
target: '15',
},
{
id: 'edge-810',
source: '0',
target: '16',
},
{
id: 'edge-957',
source: '2',
target: '3',
},
{
id: 'edge-279',
source: '4',
target: '5',
},
{
id: 'edge-83',
source: '4',
target: '6',
},
{
id: 'edge-488',
source: '5',
target: '6',
},
{
id: 'edge-453',
source: '7',
target: '13',
},
{
id: 'edge-523',
source: '8',
target: '14',
},
{
id: 'edge-543',
source: '9',
target: '10',
},
{
id: 'edge-30',
source: '10',
target: '22',
},
{
id: 'edge-146',
source: '10',
target: '14',
},
{
id: 'edge-878',
source: '10',
target: '12',
},
{
id: 'edge-369',
source: '10',
target: '24',
},
{
id: 'edge-179',
source: '10',
target: '21',
},
{
id: 'edge-759',
source: '10',
target: '20',
},
{
id: 'edge-116',
source: '11',
target: '24',
},
{
id: 'edge-940',
source: '11',
target: '22',
},
{
id: 'edge-538',
source: '11',
target: '14',
},
{
id: 'edge-522',
source: '12',
target: '13',
},
{
id: 'edge-73',
source: '16',
target: '17',
},
{
id: 'edge-59',
source: '16',
target: '18',
},
{
id: 'edge-493',
source: '16',
target: '21',
},
{
id: 'edge-162',
source: '16',
target: '22',
},
{
id: 'edge-13',
source: '17',
target: '18',
},
{
id: 'edge-892',
source: '17',
target: '20',
},
{
id: 'edge-722',
source: '18',
target: '19',
},
{
id: 'edge-617',
source: '19',
target: '20',
},
{
id: 'edge-364',
source: '19',
target: '33',
},
{
id: 'edge-926',
source: '19',
target: '22',
},
{
id: 'edge-31',
source: '19',
target: '23',
},
{
id: 'edge-695',
source: '20',
target: '21',
},
{
id: 'edge-286',
source: '21',
target: '22',
},
{
id: 'edge-300',
source: '22',
target: '24',
},
{
id: 'edge-503',
source: '22',
target: '25',
},
{
id: 'edge-509',
source: '22',
target: '26',
},
{
id: 'edge-432',
source: '22',
target: '23',
},
{
id: 'edge-293',
source: '22',
target: '28',
},
{
id: 'edge-785',
source: '22',
target: '30',
},
{
id: 'edge-514',
source: '22',
target: '31',
},
{
id: 'edge-608',
source: '22',
target: '32',
},
{
id: 'edge-946',
source: '22',
target: '33',
},
{
id: 'edge-728',
source: '23',
target: '28',
},
{
id: 'edge-416',
source: '23',
target: '27',
},
{
id: 'edge-14',
source: '23',
target: '29',
},
{
id: 'edge-776',
source: '23',
target: '30',
},
{
id: 'edge-736',
source: '23',
target: '31',
},
{
id: 'edge-508',
source: '23',
target: '33',
},
{
id: 'edge-201',
source: '32',
target: '33',
},
],
combos: [
{
id: 'a',
data: {
label: 'Combo A',
},
},
{
id: 'b',
data: {
label: 'Combo B',
},
},
{
id: 'c',
data: {
label: 'Combo D',
},
},
{
id: 'd',
data: {
label: 'Combo D',
parentId: 'b',
},
},
],
};
const graph = new Graph({
container: 'container',
data,
layout: {
type: 'combo-combined',
comboPadding: 2,
},
node: {
style: {
size: 20,
labelText: (d) => d.id,
},
},
edge: {
style: (model) => {
const { size, color } = model.data as { size: number; color: string };
return {
stroke: color || '#99ADD1',
lineWidth: size || 1,
};
},
},
behaviors: ['drag-element', 'drag-canvas', 'zoom-canvas'],
autoFit: 'view',
});
graph.render();

View File

@ -1,12 +0,0 @@
---
title: Combo Related Layout
order: 2
---
_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.
_New feature of V3.5._ Combo Force is designed for the graph with combos based on classical force directed layout algorith. It modifies the forces between nodes according to their combo infomation to achieve a final result with clustering nodes inside each combo and no overlappings.
## Usage
We suggest to use `'comboCombined'` or `'comboForce'` for the graph with combos. Other layouts in G6 are also availabel, but they do not consider the combo infomation.

View File

@ -1,12 +0,0 @@
---
title: Combo 相关布局
order: 2
---
*V4.6.0 新增功能。*是 G6 自研的、适用于带有 combo 的图,可自由组合内外布局,默认情况下,内部使用同心圆布局,外部使用力导向布局,可以有较好的效果,推荐有 combo 的图使用该布局。
*V3.5 新增功能。*Combo Force 是基于力导向的专用于带有 combo 的图的布局算法。通过自研改造经典力导向算法,将根据节点的 combo 信息,施加不同的力以达到同 combo 节点尽可能聚集,不同 combo 之间尽可能无重叠的布局。
## 使用指南
在有 Combo 的图上,推荐使用 `'comboCombined'``'comboForce'` 布局。其他内置布局将不会考虑 Combo 信息对布局的影响。

View File

@ -1,4 +1,4 @@
import { Graph, Utils } from '@antv/g6';
import { Graph, treeToGraphData } from '@antv/g6';
fetch('https://gw.alipayobjects.com/os/antvdemo/assets/data/algorithm-category.json')
.then((res) => res.json())
@ -6,7 +6,7 @@ fetch('https://gw.alipayobjects.com/os/antvdemo/assets/data/algorithm-category.j
const graph = new Graph({
container: 'container',
autoFit: 'view',
data: Utils.treeToGraphData(data),
data: treeToGraphData(data),
behaviors: ['drag-canvas', 'zoom-canvas', 'drag-element'],
node: {
style: {
@ -24,9 +24,7 @@ fetch('https://gw.alipayobjects.com/os/antvdemo/assets/data/algorithm-category.j
},
},
edge: {
style: {
type: 'cubic-horizontal',
},
type: 'cubic-horizontal',
},
layout: {
type: 'compact-box',

View File

@ -1,4 +1,4 @@
import { Graph, Utils } from '@antv/g6';
import { Graph, treeToGraphData } from '@antv/g6';
fetch('https://gw.alipayobjects.com/os/antvdemo/assets/data/algorithm-category.json')
.then((res) => res.json())
@ -6,7 +6,7 @@ fetch('https://gw.alipayobjects.com/os/antvdemo/assets/data/algorithm-category.j
const graph = new Graph({
container: 'container',
autoFit: 'view',
data: Utils.treeToGraphData(data),
data: treeToGraphData(data),
behaviors: ['drag-canvas', 'zoom-canvas', 'drag-element'],
node: {
style: {
@ -27,9 +27,7 @@ fetch('https://gw.alipayobjects.com/os/antvdemo/assets/data/algorithm-category.j
},
},
edge: {
style: {
type: 'cubic-horizontal',
},
type: 'cubic-horizontal',
},
layout: {
type: 'compact-box',

View File

@ -5,7 +5,7 @@
},
"demos": [
{
"filename": "basicCompactBox.ts",
"filename": "basic.js",
"title": {
"zh": "紧凑树",
"en": "CompactBox Layout"
@ -13,7 +13,7 @@
"screenshot": "https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*-FgIT7w4OXwAAAAAAAAAAAAADmJ7AQ/original"
},
{
"filename": "topToBottomCompactBox.ts",
"filename": "vertical.js",
"title": {
"zh": "从上向下布局",
"en": "Top to Bottom CompactBox"
@ -21,7 +21,7 @@
"screenshot": "https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*KrAqTrFbNjMAAAAAAAAAAABkARQnAQ"
},
{
"filename": "compactBoxLeftAlign.ts",
"filename": "horizontal.js",
"title": {
"zh": "节点左对齐的紧凑树",
"en": "CompactBox with Left Align Nodes"

View File

@ -1,4 +1,4 @@
import { Graph, Utils } from '@antv/g6';
import { Graph, treeToGraphData } from '@antv/g6';
fetch('https://gw.alipayobjects.com/os/antvdemo/assets/data/algorithm-category.json')
.then((res) => res.json())
@ -6,7 +6,7 @@ fetch('https://gw.alipayobjects.com/os/antvdemo/assets/data/algorithm-category.j
const graph = new Graph({
container: 'container',
autoFit: 'view',
data: Utils.treeToGraphData(data),
data: treeToGraphData(data),
node: {
style: {
labelText: (data) => data.id,
@ -28,9 +28,7 @@ fetch('https://gw.alipayobjects.com/os/antvdemo/assets/data/algorithm-category.j
},
},
edge: {
style: {
type: 'cubic-vertical',
},
type: 'cubic-vertical',
},
layout: {
type: 'compact-box',

View File

@ -0,0 +1,4 @@
---
title: CompactBox
order: 3
---

View File

@ -0,0 +1,4 @@
---
title: CompactBox 紧凑树
order: 3
---

View File

@ -1,5 +0,0 @@
---
title: API
---
<embed src="@/docs/api/treeGraphLayout/compactBox.en.md"></embed>

View File

@ -1,5 +0,0 @@
---
title: API
---
<embed src="@/docs/api/treeGraphLayout/compactBox.zh.md"></embed>

View File

@ -1,12 +0,0 @@
---
title: CompactBox
order: 3
---
The nodes with the same depth will be layed on the same level. The node size will be considred while doing layout. <br /> <img src='https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*z-ESRoHTpvIAAAAAAAAAAABkARQnAQ' alt='compactbox' width='300'/>
## Usage
CompactBox is an appropriate layout method for tree data structure. As the demo below, you can deploy it in `layout` while instantiating Graph.
You can set different configurations for different nodes if the parameter is Function type. Please refer to the ducuments for more information.

View File

@ -1,12 +0,0 @@
---
title: CompactBox 紧凑树
order: 3
---
从根节点开始,同一深度的节点在同一层,并且布局时会将节点大小考虑进去。 <br /> <img src='https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*z-ESRoHTpvIAAAAAAAAAAABkARQnAQ' alt='compactbox' width='300'/>
## 使用指南
紧凑树适用于展示树结构数据,配合 TreeGraph 使用。如下面代码所示,可在实例化 TreeGraph 时使用该布局。
其配置项中 Function 类型的配置项可以为不同的元素配置不同的值。具体描述请查看相关教程。

View File

@ -22,6 +22,7 @@ fetch('https://gw.alipayobjects.com/os/basement_prod/8dacf27e-e1bc-4522-b6d3-4b6
},
},
edge: {
type: 'line',
style: {
stroke: '#E2E2E2',
},

View File

@ -5,10 +5,10 @@
},
"demos": [
{
"filename": "basicConcentric.ts",
"filename": "basic.js",
"title": {
"zh": "基本 Concentric 同心圆布局",
"en": "Basic Concentric Layout"
"zh": "Concentric 同心圆布局",
"en": "Concentric Layout"
},
"screenshot": "https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*KXunQKOLCSAAAAAAAAAAAAAADmJ7AQ/original"
}

View File

@ -0,0 +1,4 @@
---
title: Concentric Layout
order: 10
---

View File

@ -0,0 +1,4 @@
---
title: Concentric 同心圆布局
order: 10
---

View File

@ -1,5 +0,0 @@
---
title: API
---
<embed src="@/docs/api/graphLayout/concentric.en.md"></embed>

View File

@ -1,5 +0,0 @@
---
title: API
---
<embed src="@/docs/api/graphLayout/concentric.zh.md"></embed>

View File

@ -1,10 +0,0 @@
---
title: Concentric Layout
order: 10
---
Concentric Layout places the nodes on concentric circles.
## Usage
As the demo below, you can deploy it in `layout` while instantiating Graph. it can also be used for [Subgraph Layout](/en/docs/manual/middle/layout/sub-layout). This algorithm will order the nodes according to the parameters first, then the node in the front of the order will be placed on the center of the concentric circles.

View File

@ -1,10 +0,0 @@
---
title: Concentric 同心圆布局
order: 10
---
Concentric 同心圆布局将所有节点放置在同心圆上。
## 使用指南
G6 内置的 Concentric 同心圆布局可在实例化 Graph 时使用该布局。除此之外,还可以如[子图布局](/zh/docs/manual/middle/layout/sub-layout)所示单独使用布局。该算法首先根据参数指定的排序方式对节点进行排序。排序越靠前,节点将会被放置在越中心的位置。

View File

@ -0,0 +1,85 @@
import { BaseEdge, BaseLayout, ExtensionCategory, Graph, register } from '@antv/g6';
class ArcLayout extends BaseLayout {
async execute(data, options) {
const { nodeSep = 20, nodeSize } = { ...this.options, ...options };
const { nodes } = data;
return {
nodes: nodes.map((node, index) => ({
id: node.id,
style: {
x: index * (nodeSep + nodeSize),
y: 0,
},
})),
};
}
}
class ArcEdge extends BaseEdge {
getKeyPath(attributes) {
const [sourcePoint, targetPoint] = this.getEndpoints(attributes);
const [sx, sy] = sourcePoint;
const [tx] = targetPoint;
const r = Math.abs(tx - sx) / 2;
return [
['M', sx, sy],
['A', r, r, 0, 0, sx < tx ? 1 : 0, tx, sy],
];
}
}
register(ExtensionCategory.LAYOUT, 'arc', ArcLayout);
register(ExtensionCategory.EDGE, 'arc', ArcEdge);
const palette = {
analytics: 'rgb(158, 1, 66)',
data: 'rgb(213, 62, 79)',
animate: 'rgb(244, 109, 67)',
display: 'rgb(253, 174, 97)',
flex: 'rgb(254, 224, 139)',
physics: 'rgb(230, 245, 152)',
query: 'rgb(171, 221, 164)',
scale: 'rgb(102, 194, 165)',
util: 'rgb(50, 136, 189)',
vis: 'rgb(94, 79, 162)',
};
fetch('https://gw.alipayobjects.com/os/basement_prod/70cde3be-22e8-4291-98f1-4d5a5b75b62f.json')
.then((res) => res.json())
.then((data) => {
const getCluster = (id) => data.nodes.find((node) => node.id === id).cluster;
const graph = new Graph({
container: 'container',
data,
autoFit: 'view',
animation: false,
node: {
style: {
size: 20,
fill: (d) => palette[d.cluster],
ports: [{ position: 'top' }],
labelText: (d) => d.name,
labelTextAlign: 'start',
labelTextBaseline: 'middle',
labelTransform: 'rotate(90)',
},
},
edge: {
type: 'arc',
style: {
stroke: (d) => `linear-gradient(${palette[getCluster(d.source)]}, ${palette[getCluster(d.source)]})`,
strokeOpacity: 0.5,
},
},
layout: {
type: 'arc',
nodeSize: 20,
},
behaviors: ['zoom-canvas', 'drag-canvas'],
});
graph.render();
});

View File

@ -0,0 +1,93 @@
import { BaseLayout, ExtensionCategory, Graph, register } from '@antv/g6';
const data = {
nodes: [
{ id: '0', data: { cluster: 'A' } },
{ id: '1', data: { cluster: 'A' } },
{ id: '2', data: { cluster: 'A' } },
{ id: '3', data: { cluster: 'A' } },
{ id: '4', data: { cluster: 'A' } },
{ id: '5', data: { cluster: 'A' } },
{ id: '6', data: { cluster: 'B' } },
{ id: '7', data: { cluster: 'B' } },
{ id: '8', data: { cluster: 'B' } },
{ id: '9', data: { cluster: 'B' } },
],
edges: [
{ source: '0', target: '6' },
{ source: '0', target: '7' },
{ source: '0', target: '9' },
{ source: '1', target: '6' },
{ source: '1', target: '9' },
{ source: '1', target: '7' },
{ source: '2', target: '8' },
{ source: '2', target: '9' },
{ source: '2', target: '6' },
{ source: '3', target: '8' },
{ source: '4', target: '6' },
{ source: '4', target: '7' },
{ source: '5', target: '9' },
],
};
class BiLayout extends BaseLayout {
id = 'bi-layout';
async execute(data, options) {
const { sep = 100, nodeSep = 20, nodeSize = 32 } = { ...this.options, ...options };
const [A, B] = data.nodes.reduce(
(acc, curr) => {
acc[curr.data.cluster === 'A' ? 0 : 1].push(curr);
return acc;
},
[[], []],
);
return {
nodes: [
...A.map((node, i) => ({
id: node.id,
style: {
x: i * (nodeSep + nodeSize),
y: 0,
},
})),
...B.map((node, i) => ({
id: node.id,
style: {
x: i * (nodeSep + nodeSize),
y: sep,
},
})),
],
};
}
}
register(ExtensionCategory.LAYOUT, 'bi', BiLayout);
const graph = new Graph({
container: 'container',
data,
animation: false,
autoFit: 'center',
node: {
style: {
labelText: (d) => d.id,
},
palette: {
type: 'group',
field: 'cluster',
},
},
layout: {
type: 'bi',
biSep: 300,
nodeSep: 20,
nodeSize: 32,
},
behaviors: ['drag-canvas', 'drag-element', 'zoom-canvas'],
});
graph.render();

View File

@ -0,0 +1,24 @@
{
"title": {
"zh": "中文分类",
"en": "Category"
},
"demos": [
{
"filename": "bi-graph.js",
"title": {
"zh": "二分图布局",
"en": "Bi-graph Layout"
},
"screenshot": "https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*G4-YT4NDA2oAAAAAAAAAAAAADmJ7AQ/original"
},
{
"filename": "arc.js",
"title": {
"zh": "弧线图",
"en": "Arc Diagram"
},
"screenshot": "https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*_eivQrJXt8sAAAAAAAAAAABkARQnAQ"
}
]
}

View File

@ -0,0 +1,4 @@
---
title: Custom Layout
order: 16
---

View File

@ -0,0 +1,4 @@
---
title: 自定义布局
order: 16
---

View File

@ -6,44 +6,44 @@ fetch('https://assets.antv.antgroup.com/g6/dagre-combo.json')
const graph = new Graph({
container: 'container',
autoFit: 'view',
animation: true,
data,
node: {
type: 'rect',
style: {
size: [60, 30],
radius: 8,
labelText: (d) => d.id,
labelPlacement: 'center',
ports: [{ placement: 'top' }, { placement: 'bottom' }],
},
palette: {
field: (d) => d.combo,
},
},
edge: {
type: 'cubic-vertical',
style: {
endArrow: true,
},
},
combo: {
type: 'rect',
style: {
radius: 8,
labelText: (d) => d.id,
lineDash: 0,
collapsedLineDash: [5, 5],
},
},
layout: {
type: 'antv-dagre',
ranksep: 50,
nodesep: 5,
sortByCombo: true,
},
node: {
style: {
type: 'rect',
size: [60, 30],
radius: 8,
labelPlacement: 'center',
labelText: (d) => d.id,
ports: [{ placement: 'top' }, { placement: 'bottom' }],
fill: (d) => {
const styles = { A: '#F09056', B: '#D580FF', C: '#01C9C9' };
return styles[d.style.parentId] || '#1883FF';
},
},
},
edge: {
style: {
type: 'cubic-vertical',
endArrow: true,
lineWidth: 2,
stroke: '#C2C8D5',
},
},
combo: {
style: {
type: 'rect',
labelText: (d) => d.id,
lineDash: 0,
collapsedLineDash: [5, 5],
},
},
behaviors: ['drag-element', 'drag-canvas', 'zoom-canvas'],
});
graph.render();
});

View File

@ -29,18 +29,18 @@ const data = {
],
};
const layouts = {
Default: { type: 'antv-dagre', nodesep: 100, ranksep: 70, controlPoints: true },
LR: { type: 'antv-dagre', rankdir: 'LR', align: 'DL', nodesep: 50, ranksep: 70, controlPoints: true },
'LR&UL': { type: 'antv-dagre', rankdir: 'LR', align: 'UL', controlPoints: true, nodesep: 50, ranksep: 70 },
};
const container = document.getElementById('container');
const graph = new Graph({
container,
animation: false,
data,
layout: layouts.Default,
layout: {
type: 'antv-dagre',
nodesep: 100,
ranksep: 70,
controlPoints: true,
},
node: {
style: {
type: 'rect',
@ -51,8 +51,8 @@ const graph = new Graph({
},
},
edge: {
type: 'polyline',
style: {
type: 'polyline',
endArrow: true,
lineWidth: 2,
stroke: '#C2C8D5',
@ -64,23 +64,17 @@ const graph = new Graph({
graph.render();
const btnContainer = document.createElement('div');
btnContainer.style.position = 'absolute';
btnContainer.style.zIndex = '10';
container.appendChild(btnContainer);
const tip = document.createElement('span');
tip.innerHTML = '👉 Change configs:';
btnContainer.appendChild(tip);
window.addPanel((gui) => {
const config = { layout: 'default' };
const layouts = {
default: { type: 'antv-dagre', nodesep: 100, ranksep: 70, controlPoints: true },
LR: { type: 'antv-dagre', rankdir: 'LR', align: 'DL', nodesep: 50, ranksep: 70, controlPoints: true },
'LR&UL': { type: 'antv-dagre', rankdir: 'LR', align: 'UL', controlPoints: true, nodesep: 50, ranksep: 70 },
};
Object.keys(layouts).forEach((name, i) => {
const btn = document.createElement('a');
btn.innerHTML = name;
btn.style.backgroundColor = 'rgba(255, 255, 255, 0.8)';
btn.style.padding = '4px';
btn.style.marginLeft = i > 0 ? '24px' : '8px';
btnContainer.appendChild(btn);
btn.addEventListener('click', () => {
graph.setLayout(layouts[name]);
graph.layout();
gui.add(config, 'layout', Object.keys(layouts)).onChange(async (layout) => {
graph.setLayout(layouts[layout]);
await graph.layout();
graph.fitCenter();
});
});

View File

@ -20,8 +20,8 @@ const graph = new Graph({
],
},
node: {
type: 'rect',
style: {
type: 'rect',
radius: 10,
iconText: (d) => d.data.label,
size: (d) => [d.data.width, d.data.height],
@ -32,8 +32,8 @@ const graph = new Graph({
},
},
edge: {
type: 'polyline',
style: {
type: 'polyline',
router: true,
},
},

View File

@ -5,7 +5,7 @@
},
"demos": [
{
"filename": "antv-dagre.ts",
"filename": "antv-dagre.js",
"title": {
"zh": "Dagre 流程图",
"en": "Dagre Layout"
@ -13,7 +13,7 @@
"screenshot": "https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*ESU8SrsUnlwAAAAAAAAAAAAADmJ7AQ/original"
},
{
"filename": "antv-dagre-combo.ts",
"filename": "antv-dagre-combo.js",
"title": {
"zh": "带有 Combo 的流程图",
"en": "Dagre with Combos"
@ -21,7 +21,7 @@
"screenshot": "https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*h60aQKusJRcAAAAAAAAAAAAADmJ7AQ/original"
},
{
"filename": "dagre.ts",
"filename": "dagre.js",
"title": {
"zh": "Dagre.js 布局",
"en": "Dagre.js Layout"

View File

@ -0,0 +1,4 @@
---
title: Dagre Layout
order: 1
---

View File

@ -0,0 +1,4 @@
---
title: Dagre 布局
order: 1
---

View File

@ -1,5 +0,0 @@
---
title: API
---
<embed src="@/docs/api/graphLayout/dagre.en.md"></embed>

View File

@ -1,5 +0,0 @@
---
title: API
---
<embed src="@/docs/api/graphLayout/dagre.zh.md"></embed>

View File

@ -1,16 +0,0 @@
---
title: Dagre Layout
order: 1
---
Dagre Layout is an appropriate layout method for directed flow graph. It will calculate the levels and positions of nodes automatically according to the edge directions in the data.
## Usage
As the demo below, you can deploy it in `layout` while instantiating Graph. it can also be used for [Subgraph Layout](/en/docs/manual/middle/layout/sub-layout). By tuning the parameters, you can adjust the layout direction, node alignment, node separation, level separation, and so on.
- Example 1 : Simple dagre layout.
- Example 2 : Dagre Layout with combos.
- Example 3 : Dagre layout from left to right and align top.
- Example 4 : Dagre layout from left to right.
- Example 5 : Translate the layout parameters in dynamic.

View File

@ -1,16 +0,0 @@
---
title: Dagre 流程图布局
order: 1
---
Dagre 是适合有向流程图的布局算法。其根据图数据中边的方向,自动计算节点的层级及位置。
## 使用指南
G6 内置的 Dagre 布局可以实现有向图的自动分层布局。如下面代码所示,可在实例化 Graph 时使用该布局。除此之外,还可以如[子图布局](/zh/docs/manual/middle/layout/sub-layout)所示单独使用布局。该布局可以通过配置调整布局方向、节点对齐方式、节点间距、层高等。
- 代码演示 1 :简单的 Dagre 布局。
- 代码演示 2 :带有 Combo 的 Dagre 布局(目前只能处理好同层节点的 Combo 问题)。
- 代码演示 3 :自左向右向上对齐的 Dagre 布局。
- 代码演示 4 :自左向右的 Dagre 布局。
- 代码演示 5 Dagre 布局参数动态变化。

View File

@ -1,5 +0,0 @@
---
title: API
---
<embed src="@/docs/api/treeGraphLayout/dendrogram.en.md"></embed>

View File

@ -1,5 +0,0 @@
---
title: API
---
<embed src="@/docs/api/treeGraphLayout/dendrogram.zh.md"></embed>

View File

@ -1,4 +1,4 @@
import { Graph, Utils } from '@antv/g6';
import { Graph, treeToGraphData } from '@antv/g6';
fetch('https://gw.alipayobjects.com/os/antvdemo/assets/data/algorithm-category.json')
.then((res) => res.json())
@ -6,26 +6,16 @@ fetch('https://gw.alipayobjects.com/os/antvdemo/assets/data/algorithm-category.j
const graph = new Graph({
container: 'container',
autoFit: 'view',
data: Utils.treeToGraphData(data),
data: treeToGraphData(data),
node: {
style: {
labelText: (d) => d.id,
labelPlacement: (model) => (model.style!.children?.length ? 'left' : 'right'),
ports: [
{
placement: 'right',
},
{
placement: 'left',
},
],
labelPlacement: (model) => (model.children?.length ? 'left' : 'right'),
ports: [{ placement: 'right' }, { placement: 'left' }],
},
},
edge: {
style: {
type: 'cubic-horizontal',
},
type: 'cubic-horizontal',
},
layout: {
type: 'dendrogram',
@ -33,7 +23,7 @@ fetch('https://gw.alipayobjects.com/os/antvdemo/assets/data/algorithm-category.j
nodeSep: 36,
rankSep: 250,
},
behaviors: ['drag-canvas', 'zoom-canvas', 'drag-element', 'collapse-expand-tree'],
behaviors: ['drag-canvas', 'zoom-canvas', 'drag-element'],
});
graph.render();

View File

@ -5,18 +5,18 @@
},
"demos": [
{
"filename": "basicDendrogram.ts",
"filename": "basic.js",
"title": {
"zh": "生态树",
"en": "Basic Dendrogram Layout"
"en": "Dendrogram Layout"
},
"screenshot": "https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*P-qOSoDNuckAAAAAAAAAAAAADmJ7AQ/original"
},
{
"filename": "tbDendrogram.ts",
"filename": "vertical.js",
"title": {
"zh": "至上而下的生态树",
"en": "Top to Bottom Dendrogram"
"zh": "垂直布局",
"en": "Vertical Layout"
},
"screenshot": "https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*nTKmRKkyUVUAAAAAAAAAAABkARQnAQ"
}

View File

@ -1,46 +0,0 @@
import { Graph, Utils } from '@antv/g6';
fetch('https://gw.alipayobjects.com/os/antvdemo/assets/data/algorithm-category.json')
.then((res) => res.json())
.then((data) => {
const graph = new Graph({
container: 'container',
autoFit: 'view',
data: Utils.treeToGraphData(data),
node: {
style: (model) => {
const hasChildren = !!model.style!.children?.length;
return {
labelText: model.id,
labelPlacement: hasChildren ? 'right' : 'bottom',
labelMaxWidth: 200,
labelTextAlign: 'start',
transform: hasChildren ? '' : 'rotate(90deg)',
ports: [
{
placement: 'bottom',
},
{
placement: 'top',
},
],
};
},
},
edge: {
style: {
type: 'cubic-vertical',
},
},
layout: {
type: 'dendrogram',
direction: 'TB', // H / V / LR / RL / TB / BT
nodeSep: 40,
rankSep: 100,
},
behaviors: ['drag-canvas', 'zoom-canvas', 'drag-element', 'collapse-expand-tree'],
});
graph.render();
});

View File

@ -0,0 +1,38 @@
import { Graph, treeToGraphData } from '@antv/g6';
fetch('https://gw.alipayobjects.com/os/antvdemo/assets/data/algorithm-category.json')
.then((res) => res.json())
.then((data) => {
const graph = new Graph({
container: 'container',
autoFit: 'view',
data: treeToGraphData(data),
node: {
style: (data) => {
const isLeaf = !data.children?.length;
return {
labelText: data.id,
labelWordWrap: true,
labelWordWrapWidth: 150,
labelDy: isLeaf ? 0 : 20,
labelTextAlign: isLeaf ? 'start' : 'center',
labelTextBaseline: 'middle',
labelTransform: isLeaf ? 'rotate(90deg)' : '',
ports: [{ placement: 'bottom' }, { placement: 'top' }],
};
},
},
edge: {
type: 'cubic-vertical',
},
layout: {
type: 'dendrogram',
direction: 'TB', // H / V / LR / RL / TB / BT
nodeSep: 40,
rankSep: 100,
},
behaviors: ['drag-canvas', 'zoom-canvas', 'drag-element', 'collapse-expand-tree'],
});
graph.render();
});

View File

@ -2,11 +2,3 @@
title: Dendrogram
order: 4
---
The leaves will be aligned on the same level. This algorithm does not consider the node size, which means all the nodes will be regarded as unit size with 1px. <br /> <img src='https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*zX7tSLqBvwcAAAAAAAAAAABkARQnAQ' alt='dendrogram' width='300'/>
## Usage
Dendrogram is an appropriate layout method for tree data structure. Please use it with TreeGraph. As the demo below, you can deploy it in `layout` while instantiating Graph.
You can set different configurations for different nodes if the parameter is Function type. Please refer to the ducuments for more information.

View File

@ -2,9 +2,3 @@
title: Dendrogram 生态树
order: 4
---
不管数据的深度多少,总是叶节点对齐。不考虑节点大小,布局时将节点视为 1 个像素点。 <br /> <img src='https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*zX7tSLqBvwcAAAAAAAAAAABkARQnAQ' alt='dendrogram' width='300'/>
## 使用指南
生态树适用于展示树结构数据,配合 TreeGraph 使用。如下面代码所示,可在实例化 TreeGraph 时使用该布局。

View File

@ -0,0 +1,56 @@
import { CameraSetting, ExtensionCategory, Graph, register } from '@antv/g6';
import { Light, Line3D, ObserveCanvas3D, Sphere, ZoomCanvas3D, renderer } from '@antv/g6-extension-3d';
register(ExtensionCategory.PLUGIN, '3d-light', Light);
register(ExtensionCategory.NODE, 'sphere', Sphere);
register(ExtensionCategory.EDGE, 'line3d', Line3D);
register(ExtensionCategory.PLUGIN, 'camera-setting', CameraSetting);
register(ExtensionCategory.BEHAVIOR, 'zoom-canvas-3d', ZoomCanvas3D);
register(ExtensionCategory.BEHAVIOR, 'observe-canvas-3d', ObserveCanvas3D);
fetch('https://assets.antv.antgroup.com/g6/d3-force-3d.json')
.then((res) => res.json())
.then((data) => {
const graph = new Graph({
container: 'container',
animation: true,
renderer,
data,
layout: {
type: 'd3-force-3d',
},
node: {
type: 'sphere',
style: {
materialType: 'phong',
},
palette: {
color: 'tableau',
type: 'group',
field: 'group',
},
},
edge: {
type: 'line3d',
},
behaviors: ['observe-canvas-3d', 'zoom-canvas-3d'],
plugins: [
{
type: 'camera-setting',
projectionMode: 'perspective',
near: 0.1,
far: 1000,
fov: 45,
aspect: 1,
},
{
type: '3d-light',
directional: {
direction: [0, 0, 1],
},
},
],
});
graph.render();
});

View File

@ -6,6 +6,7 @@ fetch('https://gw.alipayobjects.com/os/antvdemo/assets/data/relations.json')
const graph = new Graph({
container: 'container',
data,
autoFit: 'view',
layout: {
type: 'force-atlas2',
preventOverlap: true,

View File

@ -0,0 +1,87 @@
import { BaseBehavior, ExtensionCategory, Graph, invokeLayoutMethod, register } from '@antv/g6';
function getData(width, size = 200) {
const k = width / 200;
const r = randomUniform(k * 2, k * 8);
const n = 4;
return {
nodes: Array.from({ length: size }, (_, i) => ({ id: `${i}`, data: { r: r(), group: i && (i % n) + 1 } })),
edges: [],
};
}
function randomUniform(min, max) {
min = min == null ? 0 : +min;
max = max == null ? 1 : +max;
if (arguments.length === 1) (max = min), (min = 0);
else max -= min;
return function () {
return Math.random() * max + min;
};
}
class CollisionElement extends BaseBehavior {
constructor(context) {
super(context, {});
this.onPointerMove = this.onPointerMove.bind(this);
this.bindEvents();
}
bindEvents() {
this.context.graph.on('pointermove', this.onPointerMove);
}
onPointerMove(event) {
const pos = this.context.graph.getCanvasByClient([event.client.x, event.client.y]);
const layoutInstance = this.context.layout
?.getLayoutInstance()
.find((layout) => ['d3-force', 'd3-force-3d'].includes(layout?.id));
if (layoutInstance) {
invokeLayoutMethod(layoutInstance, 'setFixedPosition', '0', [...pos]);
}
}
}
register(ExtensionCategory.BEHAVIOR, 'collision-element', CollisionElement);
const container = document.getElementById('container');
const width = container.scrollWidth;
const graph = new Graph({
container,
animation: true,
data: getData(width),
layout: {
type: 'd3force',
alphaTarget: 0.3,
velocityDecay: 0.1,
x: {
strength: 0.01,
},
y: {
strength: 0.01,
},
collide: {
radius: (d) => d.data.r,
iterations: 3,
},
manyBody: {
strength: (d, i) => (i ? 0 : (-width * 2) / 3),
},
link: false,
},
node: {
style: {
size: (d) => (d.id === '0' ? 0 : d.data.r * 2),
},
palette: {
color: 'tableau',
type: 'group',
field: (d) => d.data.group,
},
},
behaviors: ['collision-element'],
});
graph.render();

View File

@ -0,0 +1,30 @@
import { Graph } from '@antv/g6';
fetch('https://assets.antv.antgroup.com/g6/cluster.json')
.then((res) => res.json())
.then((data) => {
const graph = new Graph({
container: 'container',
animation: true,
data,
node: {
style: {
labelText: (d) => d.id,
ports: [],
},
palette: {
type: 'group',
field: 'cluster',
},
},
layout: {
type: 'd3force',
collide: {
strength: 0.5,
},
},
behaviors: ['zoom-canvas', 'drag-canvas'],
});
graph.render();
});

View File

@ -0,0 +1,31 @@
import { Graph } from '@antv/g6';
fetch('https://assets.antv.antgroup.com/g6/cluster.json')
.then((res) => res.json())
.then((data) => {
const graph = new Graph({
container: 'container',
data,
node: {
style: {
labelText: (d) => d.id,
ports: [],
},
palette: {
type: 'group',
field: 'cluster',
},
},
layout: {
type: 'force',
linkDistance: 50,
animation: true,
clustering: true,
nodeClusterBy: 'cluster',
clusterNodeStrength: 70,
},
behaviors: ['zoom-canvas', 'drag-canvas'],
});
graph.render();
});

View File

@ -0,0 +1,47 @@
import { Graph } from '@antv/g6';
function getData(size = 10) {
const nodes = Array.from({ length: size * size }, (_, i) => ({ id: `${i}` }));
const edges = [];
for (let y = 0; y < size; ++y) {
for (let x = 0; x < size; ++x) {
if (y > 0) edges.push({ source: `${(y - 1) * size + x}`, target: `${y * size + x}` });
if (x > 0) edges.push({ source: `${y * size + (x - 1)}`, target: `${y * size + x}` });
}
}
return { nodes, edges };
}
const graph = new Graph({
animation: true,
data: getData(),
layout: {
type: 'd3force',
manyBody: {
strength: -30,
},
link: {
strength: 1,
distance: 20,
iterations: 10,
},
},
node: {
style: {
size: 10,
fill: '#000',
},
},
edge: {
style: {
stroke: '#000',
},
},
behaviors: [{ type: 'drag-element-force' }, 'zoom-canvas'],
});
graph.render();
window.addPanel((gui) => {
gui.add({ msg: 'Try to drag nodes' }, 'msg').name('Tips').disable();
});

View File

@ -0,0 +1,56 @@
{
"title": {
"zh": "中文分类",
"en": "Category"
},
"demos": [
{
"filename": "force.js",
"title": {
"zh": "Force 聚类",
"en": "Clustering Force Layout"
},
"screenshot": "https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*sA14SZo9BBMAAAAAAAAAAAAADmJ7AQ/original"
},
{
"filename": "atlas2.js",
"title": {
"zh": "Force Atlas 2",
"en": "Force-Atlas 2 Layout"
},
"screenshot": "https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*-HgiS6CyuuEAAAAAAAAAAAAADmJ7AQ/original"
},
{
"filename": "d3-force.js",
"title": {
"zh": "D3 力导向布局",
"en": "D3 Force Layout"
},
"screenshot": "https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*-_sFS5IRGGcAAAAAAAAAAAAADmJ7AQ/original"
},
{
"filename": "3d-force.js",
"title": {
"zh": "3D 力导向布局",
"en": "3D Force Layout"
},
"screenshot": "https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*4mbSTJLOXkgAAAAAAAAAAAAADmJ7AQ/original"
},
{
"filename": "mesh.js",
"title": {
"zh": "网格效果",
"en": "Mesh Layout"
},
"screenshot": "https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*ykD5QqSEgeEAAAAAAAAAAAAADmJ7AQ/original"
},
{
"filename": "collision.js",
"title": {
"zh": "弹性碰撞效果",
"en": "Collision Layout"
},
"screenshot": "https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*yzv_To2Wm_EAAAAAAAAAAAAADmJ7AQ/original"
}
]
}

View File

@ -0,0 +1,4 @@
---
title: Force-directed Layout
order: 0
---

View File

@ -0,0 +1,4 @@
---
title: 力导向图布局
order: 0
---

View File

@ -1,5 +0,0 @@
---
title: API
---
<embed src="@/docs/api/graphLayout/force.en.md"></embed>

View File

@ -1,11 +0,0 @@
---
title: API
---
# Force Layout
<embed src="@/docs/api/graphLayout/force.zh.md"></embed>
# Force Atlas 2 Layout
<embed src="@/docs/api/graphLayout/forceAtlas2.zh.md"></embed>

View File

@ -1,546 +0,0 @@
import { Graph } from '@antv/g6';
const data = {
nodes: [
{
id: '0',
data: {
cluster: 'a',
},
},
{
id: '1',
data: {
cluster: 'a',
},
},
{
id: '2',
data: {
cluster: 'a',
},
},
{
id: '3',
data: {
cluster: 'a',
},
},
{
id: '4',
data: {
cluster: 'a',
},
},
{
id: '5',
data: {
cluster: 'a',
},
},
{
id: '6',
data: {
cluster: 'a',
},
},
{
id: '7',
data: {
cluster: 'a',
},
},
{
id: '8',
data: {
cluster: 'a',
},
},
{
id: '9',
data: {
cluster: 'a',
},
},
{
id: '10',
data: {
cluster: 'a',
},
},
{
id: '11',
data: {
cluster: 'a',
},
},
{
id: '12',
data: {
cluster: 'a',
},
},
{
id: '13',
data: {
cluster: 'b',
},
},
{
id: '14',
data: {
cluster: 'b',
},
},
{
id: '15',
data: {
cluster: 'b',
},
},
{
id: '16',
data: {
cluster: 'b',
},
},
{
id: '17',
data: {
cluster: 'b',
},
},
{
id: '18',
data: {
cluster: 'c',
},
},
{
id: '19',
data: {
cluster: 'c',
},
},
{
id: '20',
data: {
cluster: 'c',
},
},
{
id: '21',
data: {
cluster: 'c',
},
},
{
id: '22',
data: {
cluster: 'c',
},
},
{
id: '23',
data: {
cluster: 'c',
},
},
{
id: '24',
data: {
cluster: 'c',
},
},
{
id: '25',
data: {
cluster: 'c',
},
},
{
id: '26',
data: {
cluster: 'c',
},
},
{
id: '27',
data: {
cluster: 'c',
},
},
{
id: '28',
data: {
cluster: 'c',
},
},
{
id: '29',
data: {
cluster: 'c',
},
},
{
id: '30',
data: {
cluster: 'c',
},
},
{
id: '31',
data: {
cluster: 'd',
},
},
{
id: '32',
data: {
cluster: 'd',
},
},
{
id: '33',
data: {
cluster: 'd',
},
},
],
edges: [
{
id: 'edge-583',
source: '0',
target: '1',
},
{
id: 'edge-0',
source: '0',
target: '2',
},
{
id: 'edge-888',
source: '0',
target: '3',
},
{
id: 'edge-560',
source: '0',
target: '4',
},
{
id: 'edge-34',
source: '0',
target: '5',
},
{
id: 'edge-654',
source: '0',
target: '7',
},
{
id: 'edge-317',
source: '0',
target: '8',
},
{
id: 'edge-962',
source: '0',
target: '9',
},
{
id: 'edge-746',
source: '0',
target: '10',
},
{
id: 'edge-75',
source: '0',
target: '11',
},
{
id: 'edge-80',
source: '0',
target: '13',
},
{
id: 'edge-674',
source: '0',
target: '14',
},
{
id: 'edge-795',
source: '0',
target: '15',
},
{
id: 'edge-886',
source: '0',
target: '16',
},
{
id: 'edge-630',
source: '2',
target: '3',
},
{
id: 'edge-710',
source: '4',
target: '5',
},
{
id: 'edge-284',
source: '4',
target: '6',
},
{
id: 'edge-758',
source: '5',
target: '6',
},
{
id: 'edge-288',
source: '7',
target: '13',
},
{
id: 'edge-103',
source: '8',
target: '14',
},
{
id: 'edge-873',
source: '9',
target: '10',
},
{
id: 'edge-108',
source: '10',
target: '22',
},
{
id: 'edge-214',
source: '10',
target: '14',
},
{
id: 'edge-928',
source: '10',
target: '12',
},
{
id: 'edge-717',
source: '10',
target: '24',
},
{
id: 'edge-752',
source: '10',
target: '21',
},
{
id: 'edge-424',
source: '10',
target: '20',
},
{
id: 'edge-755',
source: '11',
target: '24',
},
{
id: 'edge-822',
source: '11',
target: '22',
},
{
id: 'edge-366',
source: '11',
target: '14',
},
{
id: 'edge-411',
source: '12',
target: '13',
},
{
id: 'edge-781',
source: '16',
target: '17',
},
{
id: 'edge-742',
source: '16',
target: '18',
},
{
id: 'edge-762',
source: '16',
target: '21',
},
{
id: 'edge-519',
source: '16',
target: '22',
},
{
id: 'edge-187',
source: '17',
target: '18',
},
{
id: 'edge-919',
source: '17',
target: '20',
},
{
id: 'edge-854',
source: '18',
target: '19',
},
{
id: 'edge-199',
source: '19',
target: '20',
},
{
id: 'edge-475',
source: '19',
target: '33',
},
{
id: 'edge-55',
source: '19',
target: '22',
},
{
id: 'edge-528',
source: '19',
target: '23',
},
{
id: 'edge-741',
source: '20',
target: '21',
},
{
id: 'edge-920',
source: '21',
target: '22',
},
{
id: 'edge-457',
source: '22',
target: '24',
},
{
id: 'edge-524',
source: '22',
target: '25',
},
{
id: 'edge-814',
source: '22',
target: '26',
},
{
id: 'edge-258',
source: '22',
target: '23',
},
{
id: 'edge-222',
source: '22',
target: '28',
},
{
id: 'edge-803',
source: '22',
target: '30',
},
{
id: 'edge-693',
source: '22',
target: '31',
},
{
id: 'edge-491',
source: '22',
target: '32',
},
{
id: 'edge-46',
source: '22',
target: '33',
},
{
id: 'edge-145',
source: '23',
target: '28',
},
{
id: 'edge-45',
source: '23',
target: '27',
},
{
id: 'edge-98',
source: '23',
target: '29',
},
{
id: 'edge-993',
source: '23',
target: '30',
},
{
id: 'edge-514',
source: '23',
target: '31',
},
{
id: 'edge-361',
source: '23',
target: '33',
},
{
id: 'edge-154',
source: '32',
target: '33',
},
],
};
data.nodes.forEach((node) => (node.data.size = Math.random() * 30 + 5));
const graph = new Graph({
container: 'container',
data,
node: {
style: {
type: 'circle',
size: (d) => d.data.size,
labelText: (d) => d.id,
ports: [],
},
palette: {
type: 'group',
field: 'cluster',
color: 'antv',
},
},
layout: {
type: 'force',
linkDistance: 50,
animation: true,
clustering: true,
nodeClusterBy: 'cluster',
clusterNodeStrength: 70,
},
behaviors: ['zoom-canvas', 'drag-canvas'],
zoomRange: [0.1, 5],
autoResize: true,
});
graph.render();

View File

@ -1,551 +0,0 @@
import { Graph } from '@antv/g6';
const data = {
nodes: [
{
id: '0',
data: {
label: '0',
},
},
{
id: '1',
data: {
label: '1',
},
},
{
id: '2',
data: {
label: '2',
},
},
{
id: '3',
data: {
label: '3',
},
},
{
id: '4',
data: {
label: '4',
},
},
{
id: '5',
data: {
label: '5',
},
},
{
id: '6',
data: {
label: '6',
},
},
{
id: '7',
data: {
label: '7',
},
},
{
id: '8',
data: {
label: '8',
},
},
{
id: '9',
data: {
label: '9',
},
},
{
id: '10',
data: {
label: '10',
},
},
{
id: '11',
data: {
label: '11',
},
},
{
id: '12',
data: {
label: '12',
},
},
{
id: '13',
data: {
label: '13',
},
},
{
id: '14',
data: {
label: '14',
},
},
{
id: '15',
data: {
label: '15',
},
},
{
id: '16',
data: {
label: '16',
},
},
{
id: '17',
data: {
label: '17',
},
},
{
id: '18',
data: {
label: '18',
},
},
{
id: '19',
data: {
label: '19',
},
},
{
id: '20',
data: {
label: '20',
},
},
{
id: '21',
data: {
label: '21',
},
},
{
id: '22',
data: {
label: '22',
},
},
{
id: '23',
data: {
label: '23',
},
},
{
id: '24',
data: {
label: '24',
},
},
{
id: '25',
data: {
label: '25',
},
},
{
id: '26',
data: {
label: '26',
},
},
{
id: '27',
data: {
label: '27',
},
},
{
id: '28',
data: {
label: '28',
},
},
{
id: '29',
data: {
label: '29',
},
},
{
id: '30',
data: {
label: '30',
},
},
{
id: '31',
data: {
label: '31',
},
},
{
id: '32',
data: {
label: '32',
},
},
{
id: '33',
data: {
label: '33',
},
},
],
edges: [
{
id: 'edge-442',
source: '0',
target: '1',
},
{
id: 'edge-825',
source: '0',
target: '2',
},
{
id: 'edge-100',
source: '0',
target: '3',
},
{
id: 'edge-378',
source: '0',
target: '4',
},
{
id: 'edge-691',
source: '0',
target: '5',
},
{
id: 'edge-983',
source: '0',
target: '7',
},
{
id: 'edge-491',
source: '0',
target: '8',
},
{
id: 'edge-38',
source: '0',
target: '9',
},
{
id: 'edge-547',
source: '0',
target: '10',
},
{
id: 'edge-126',
source: '0',
target: '11',
},
{
id: 'edge-905',
source: '0',
target: '13',
},
{
id: 'edge-931',
source: '0',
target: '14',
},
{
id: 'edge-329',
source: '0',
target: '15',
},
{
id: 'edge-798',
source: '0',
target: '16',
},
{
id: 'edge-130',
source: '2',
target: '3',
},
{
id: 'edge-132',
source: '4',
target: '5',
},
{
id: 'edge-587',
source: '4',
target: '6',
},
{
id: 'edge-772',
source: '5',
target: '6',
},
{
id: 'edge-582',
source: '7',
target: '13',
},
{
id: 'edge-922',
source: '8',
target: '14',
},
{
id: 'edge-559',
source: '9',
target: '10',
},
{
id: 'edge-915',
source: '10',
target: '22',
},
{
id: 'edge-304',
source: '10',
target: '14',
},
{
id: 'edge-88',
source: '10',
target: '12',
},
{
id: 'edge-343',
source: '10',
target: '24',
},
{
id: 'edge-692',
source: '10',
target: '21',
},
{
id: 'edge-29',
source: '10',
target: '20',
},
{
id: 'edge-390',
source: '11',
target: '24',
},
{
id: 'edge-46',
source: '11',
target: '22',
},
{
id: 'edge-170',
source: '11',
target: '14',
},
{
id: 'edge-51',
source: '12',
target: '13',
},
{
id: 'edge-387',
source: '16',
target: '17',
},
{
id: 'edge-598',
source: '16',
target: '18',
},
{
id: 'edge-894',
source: '16',
target: '21',
},
{
id: 'edge-448',
source: '16',
target: '22',
},
{
id: 'edge-921',
source: '17',
target: '18',
},
{
id: 'edge-374',
source: '17',
target: '20',
},
{
id: 'edge-11',
source: '18',
target: '19',
},
{
id: 'edge-899',
source: '19',
target: '20',
},
{
id: 'edge-790',
source: '19',
target: '33',
},
{
id: 'edge-294',
source: '19',
target: '22',
},
{
id: 'edge-353',
source: '19',
target: '23',
},
{
id: 'edge-546',
source: '20',
target: '21',
},
{
id: 'edge-105',
source: '21',
target: '22',
},
{
id: 'edge-261',
source: '22',
target: '24',
},
{
id: 'edge-393',
source: '22',
target: '25',
},
{
id: 'edge-779',
source: '22',
target: '26',
},
{
id: 'edge-149',
source: '22',
target: '23',
},
{
id: 'edge-313',
source: '22',
target: '28',
},
{
id: 'edge-805',
source: '22',
target: '30',
},
{
id: 'edge-538',
source: '22',
target: '31',
},
{
id: 'edge-884',
source: '22',
target: '32',
},
{
id: 'edge-824',
source: '22',
target: '33',
},
{
id: 'edge-519',
source: '23',
target: '28',
},
{
id: 'edge-496',
source: '23',
target: '27',
},
{
id: 'edge-402',
source: '23',
target: '29',
},
{
id: 'edge-736',
source: '23',
target: '30',
},
{
id: 'edge-782',
source: '23',
target: '31',
},
{
id: 'edge-87',
source: '23',
target: '33',
},
{
id: 'edge-27',
source: '32',
target: '33',
},
],
};
const container = document.getElementById('container');
const descriptionDiv = document.createElement('div');
descriptionDiv.innerHTML = 'Force layout, linkDistance = 50, preventOverlap: false';
container.appendChild(descriptionDiv);
const width = container.scrollWidth;
const height = (container.scrollHeight || 500) - 30;
const graph = new Graph({
container: 'container',
width,
height,
data,
node: (model) => {
return {
id: model.id,
labelText: (d) => model.data.label,
};
},
layout: {
type: 'force',
linkDistance: 50,
animation: true,
},
behaviors: ['zoom-canvas', 'drag-canvas'],
});
setTimeout(() => {
descriptionDiv.innerHTML = 'Force layout, linkDistance = 100, preventOverlap: true';
graph.layout({
type: 'force',
linkDistance: 100,
preventOverlap: true,
nodeSize: 20,
animation: true,
});
}, 5000);
graph.render();

View File

@ -1,65 +0,0 @@
import { Graph } from '@antv/g6';
const graph = new Graph({
container: 'container',
data: {
nodes: [
{ id: 'node0', size: 50 },
{ id: 'node1', size: 30 },
{ id: 'node2', size: 30 },
{ id: 'node3', size: 30 },
{ id: 'node4', size: 30, isLeaf: true },
{ id: 'node5', size: 30, isLeaf: true },
{ id: 'node6', size: 15, isLeaf: true },
{ id: 'node7', size: 15, isLeaf: true },
{ id: 'node8', size: 15, isLeaf: true },
{ id: 'node9', size: 15, isLeaf: true },
{ id: 'node10', size: 15, isLeaf: true },
{ id: 'node11', size: 15, isLeaf: true },
{ id: 'node12', size: 15, isLeaf: true },
{ id: 'node13', size: 15, isLeaf: true },
{ id: 'node14', size: 15, isLeaf: true },
{ id: 'node15', size: 15, isLeaf: true },
{ id: 'node16', size: 15, isLeaf: true },
],
edges: [
{ source: 'node0', target: 'node1' },
{ source: 'node0', target: 'node2' },
{ source: 'node0', target: 'node3' },
{ source: 'node0', target: 'node4' },
{ source: 'node0', target: 'node5' },
{ source: 'node1', target: 'node6' },
{ source: 'node1', target: 'node7' },
{ source: 'node2', target: 'node8' },
{ source: 'node2', target: 'node9' },
{ source: 'node2', target: 'node10' },
{ source: 'node2', target: 'node11' },
{ source: 'node2', target: 'node12' },
{ source: 'node2', target: 'node13' },
{ source: 'node3', target: 'node14' },
{ source: 'node3', target: 'node15' },
{ source: 'node3', target: 'node16' },
],
},
node: {
style: {
size: (d) => d.size,
},
},
layout: {
type: 'force',
preventOverlap: true,
animation: true,
linkDistance: (d) => {
if (d.source === 'node0' || d.target === 'node0') {
return 200;
}
return 80;
},
},
behaviors: ['zoom-canvas', 'drag-canvas'],
autoResize: true,
zoomRange: [0.1, 5],
});
graph.render();

View File

@ -1,40 +0,0 @@
{
"title": {
"zh": "中文分类",
"en": "Category"
},
"demos": [
{
"filename": "basicForce.js",
"title": {
"zh": "Force 聚类及固定拖拽节点",
"en": "Clustering Force Layout with dragging"
},
"screenshot": "https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*sA14SZo9BBMAAAAAAAAAAAAADmJ7AQ/original"
},
{
"filename": "basicFA2.js",
"title": {
"zh": "基本 Force Atlas 2",
"en": "Basic Force-Atlas 2 Layout"
},
"screenshot": "https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*-HgiS6CyuuEAAAAAAAAAAAAADmJ7AQ/original"
},
{
"filename": "forceDirectedFunctionalParams.js",
"title": {
"zh": "定制不同节点的参数",
"en": "Different Configurations for Nodes"
},
"screenshot": "https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*xQRmQ7pTvZMAAAAAAAAAAAAADmJ7AQ/original"
},
{
"filename": "forceDirectedConfigurationTranslate.js",
"title": {
"zh": "力导向布局参数动态变化",
"en": "Update Configurations for Force-directed Layout"
},
"screenshot": "https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*2HKeQqGwjQoAAAAAAAAAAAAADmJ7AQ/original"
}
]
}

View File

@ -1,18 +0,0 @@
---
title: Force-directed Layout
order: 0
---
Force-directed layout is a set of algorithms which are imporved and extended by lots of researchers based on the earliest classical force-directed algorithm. They simulate the nodes and edges in the graph as the physical objects. There are attractive forces and repulsive forces between nodes to iteratively move them to reach a reasonable layout.
## Usage
The classical force-directed layout in G6 comes from d3.js. As the demo below, you can deploy it in `layout` while instantiating Graph. it can also be used for [Subgraph Layout](/en/docs/manual/middle/layout/sub-layout).
- Example 1 : Basic force-directed layout and dragging interactions.
- Example 2 : Prevent node overlappings.
- Example 3 : Adjust the link distances and forces for different nodes.
- Example 4 : Fix the dragged node.
- Example 5 : Translate the layout parameters in dynamic.
- Example 6 : The bubbles layout and interactions.
- Example 7 : Constrain the layout in a certain area.

View File

@ -1,18 +0,0 @@
---
title: 力导向图布局
order: 0
---
力导向图布局作为较早被发明的一种实际应用布局算法,经过研究者多年改进、扩展,已发展成为一类算法的集合。该类算法的特点是模拟物理世界中的作用力,施加在节点上,并迭代计算以达到合理放置节点、美观布局的一类算法。
## 使用指南
G6 内置的经典力导向算法引用了 d3.js 的力导向算法。如下面代码所示,可在实例化 Graph 时使用该布局。除此之外,还可以如[子图布局](/zh/docs/manual/middle/layout/sub-layout)所示单独使用布局。
- 代码演示 1 :基础的经典力导向布局及节点的拖拽。
- 代码演示 2 :节点不重叠。
- 代码演示 3 :为不同节点调整边长和力。
- 代码演示 4 :固定被拖拽的节点。
- 代码演示 5 :支持布局参数的动态切换。
- 代码演示 6 :使用力导向算法实现气泡效果及交互。
- 代码演示 7 :约束在一定范围内进行力导向布局。

View File

@ -0,0 +1,32 @@
import { Graph } from '@antv/g6';
fetch('https://assets.antv.antgroup.com/g6/cluster.json')
.then((res) => res.json())
.then((data) => {
const graph = new Graph({
container: 'container',
data,
layout: {
type: 'fruchterman',
gravity: 5,
speed: 5,
animated: true,
},
node: {
style: {
size: 20,
labelPlacement: 'center',
labelText: (d) => d.id,
labelBackground: false,
},
palette: {
type: 'group',
field: 'cluster',
},
},
animation: true,
behaviors: ['drag-canvas', 'drag-element'],
});
graph.render();
});

View File

@ -0,0 +1,38 @@
import { Graph } from '@antv/g6';
fetch('https://assets.antv.antgroup.com/g6/cluster.json')
.then((res) => res.json())
.then((data) => {
const graph = new Graph({
container: 'container',
data,
layout: {
type: 'fruchterman',
gravity: 10,
speed: 5,
clustering: true,
nodeClusterBy: 'cluster',
},
node: {
style: {
size: 20,
labelPlacement: 'center',
labelText: (d) => d.id,
labelBackground: false,
},
palette: {
type: 'group',
field: 'cluster',
},
},
edge: {
style: {
endArrow: true,
},
},
behaviors: ['drag-canvas', 'drag-element'],
animation: true,
});
graph.render();
});

View File

@ -5,7 +5,7 @@
},
"demos": [
{
"filename": "basicFruchterman.ts",
"filename": "basic.js",
"title": {
"zh": "基本 Fruchterman 布局",
"en": "Basic Fruchterman Layout"
@ -13,7 +13,7 @@
"screenshot": "https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*1KY7SLEXxqMAAAAAAAAAAABkARQnAQ"
},
{
"filename": "fruchtermanCluster.ts",
"filename": "cluster.js",
"title": {
"zh": "基本 Fruchterman 布局",
"en": "Basic Fruchterman Layout"
@ -21,20 +21,20 @@
"screenshot": "https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*-s9CTphuwgcAAAAAAAAAAAAADmJ7AQ/original"
},
{
"filename": "fruchtermanWebWorker.ts",
"filename": "run-in-web-worker.js",
"title": {
"zh": "Fruchterman 使用 Web-worker",
"en": "Fruchterman with Web-worker"
"zh": "使用 Web-worker",
"en": "Run in Web-worker"
},
"screenshot": "https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*3gn9TZ3oUoIAAAAAAAAAAAAADmJ7AQ/original"
},
{
"filename": "fruchtermanFix.ts",
"filename": "run-in-gpu.js",
"title": {
"zh": "Fruchterman 固定被拖拽的节点",
"en": "Fruchterman with Fixing Dragged Nodes"
"zh": "使用 GPU 加速",
"en": "Run in GPU"
},
"screenshot": "https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*sbvuTbp3L6sAAAAAAAAAAAAADmJ7AQ/original"
"screenshot": "https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*SmXBQ6fv8PoAAAAAAAAAAAAADmJ7AQ/original"
}
]
}

View File

@ -1,7 +1,6 @@
import { Graph, register } from '@antv/g6';
import { FruchtermanLayout } from '@antv/layout-gpu';
// Resister the layout into G6.
register('layout', 'fruchterman-gpu', FruchtermanLayout);
fetch('https://gw.alipayobjects.com/os/basement_prod/7bacd7d1-4119-4ac1-8be3-4c4b9bcbc25f.json')
@ -17,15 +16,17 @@ fetch('https://gw.alipayobjects.com/os/basement_prod/7bacd7d1-4119-4ac1-8be3-4c4
},
edge: {
style: {
opacity: 0.5,
endArrow: true,
endArrowSize: 2,
startArrow: true,
},
},
layout: {
type: 'fruchterman-gpu',
type: 'fruchterman',
speed: 20,
gravity: 1,
maxIteration: 10000,
workerEnabled: true,
},
behaviors: ['zoom-canvas', 'drag-canvas', 'drag-element', 'click-select'],
behaviors: ['zoom-canvas', 'drag-canvas', 'drag-element'],
});
graph.render();

View File

@ -0,0 +1,43 @@
import { Graph, GraphEvent } from '@antv/g6';
fetch('https://assets.antv.antgroup.com/g6/cluster.json')
.then((res) => res.json())
.then((data) => {
const graph = new Graph({
container: 'container',
data,
layout: {
type: 'fruchterman',
speed: 20,
gravity: 10,
maxIteration: 10000,
workerEnabled: true,
},
node: {
style: {
size: 20,
labelText: (d) => d.id,
labelPlacement: 'center',
},
palette: {
type: 'group',
field: 'cluster',
},
},
edge: {
style: {
stroke: '#ddd',
},
},
behaviors: ['drag-canvas', 'drag-element'],
});
graph.render();
window.addPanel((gui) => {
const msg = gui.add({ msg: 'Running...' }, 'msg').name('Tips').disable();
graph.on(GraphEvent.AFTER_LAYOUT, () => {
msg.setValue('Layout Done!');
});
});
});

View File

@ -0,0 +1,4 @@
---
title: Fruchterman Layout
order: 13
---

View File

@ -0,0 +1,4 @@
---
title: Fruchterman 图布局
order: 13
---

View File

@ -1,5 +0,0 @@
---
title: API
---
<embed src="@/docs/api/graphLayout/fruchterman.en.md"></embed>

View File

@ -1,5 +0,0 @@
---
title: API
---
<embed src="@/docs/api/graphLayout/fruchterman.zh.md"></embed>

View File

@ -1,135 +0,0 @@
import { Graph } from '@antv/g6';
const data = {
nodes: [
{ id: '0', data: { cluster: 'a' } },
{ id: '1', data: { cluster: 'a' } },
{ id: '2', data: { cluster: 'a' } },
{ id: '3', data: { cluster: 'a' } },
{ id: '4', data: { cluster: 'a' } },
{ id: '5', data: { cluster: 'a' } },
{ id: '6', data: { cluster: 'a' } },
{ id: '7', data: { cluster: 'a' } },
{ id: '8', data: { cluster: 'a' } },
{ id: '9', data: { cluster: 'a' } },
{ id: '10', data: { cluster: 'a' } },
{ id: '11', data: { cluster: 'a' } },
{ id: '12', data: { cluster: 'a' } },
{ id: '13', data: { cluster: 'b' } },
{ id: '14', data: { cluster: 'b' } },
{ id: '15', data: { cluster: 'b' } },
{ id: '16', data: { cluster: 'b' } },
{ id: '17', data: { cluster: 'b' } },
{ id: '18', data: { cluster: 'c' } },
{ id: '19', data: { cluster: 'c' } },
{ id: '20', data: { cluster: 'c' } },
{ id: '21', data: { cluster: 'c' } },
{ id: '22', data: { cluster: 'c' } },
{ id: '23', data: { cluster: 'c' } },
{ id: '24', data: { cluster: 'c' } },
{ id: '25', data: { cluster: 'c' } },
{ id: '26', data: { cluster: 'c' } },
{ id: '27', data: { cluster: 'c' } },
{ id: '28', data: { cluster: 'c' } },
{ id: '29', data: { cluster: 'c' } },
{ id: '30', data: { cluster: 'c' } },
{ id: '31', data: { cluster: 'd' } },
{ id: '32', data: { cluster: 'd' } },
{ id: '33', data: { cluster: 'd' } },
],
edges: [
{ source: '0', target: '1' },
{ source: '0', target: '2' },
{ source: '0', target: '3' },
{ source: '0', target: '4' },
{ source: '0', target: '5' },
{ source: '0', target: '7' },
{ source: '0', target: '8' },
{ source: '0', target: '9' },
{ source: '0', target: '10' },
{ source: '0', target: '11' },
{ source: '0', target: '13' },
{ source: '0', target: '14' },
{ source: '0', target: '15' },
{ source: '0', target: '16' },
{ source: '2', target: '3' },
{ source: '4', target: '5' },
{ source: '4', target: '6' },
{ source: '5', target: '6' },
{ source: '7', target: '13' },
{ source: '8', target: '14' },
{ source: '9', target: '10' },
{ source: '10', target: '22' },
{ source: '10', target: '14' },
{ source: '10', target: '12' },
{ source: '10', target: '24' },
{ source: '10', target: '21' },
{ source: '10', target: '20' },
{ source: '11', target: '24' },
{ source: '11', target: '22' },
{ source: '11', target: '14' },
{ source: '12', target: '13' },
{ source: '16', target: '17' },
{ source: '16', target: '18' },
{ source: '16', target: '21' },
{ source: '16', target: '22' },
{ source: '17', target: '18' },
{ source: '17', target: '20' },
{ source: '18', target: '19' },
{ source: '19', target: '20' },
{ source: '19', target: '33' },
{ source: '19', target: '22' },
{ source: '19', target: '23' },
{ source: '20', target: '21' },
{ source: '21', target: '22' },
{ source: '22', target: '24' },
{ source: '22', target: '25' },
{ source: '22', target: '26' },
{ source: '22', target: '23' },
{ source: '22', target: '28' },
{ source: '22', target: '30' },
{ source: '22', target: '31' },
{ source: '22', target: '32' },
{ source: '22', target: '33' },
{ source: '23', target: '28' },
{ source: '23', target: '27' },
{ source: '23', target: '29' },
{ source: '23', target: '30' },
{ source: '23', target: '31' },
{ source: '23', target: '33' },
{ source: '32', target: '33' },
],
};
const graph = new Graph({
container: 'container',
data,
layout: {
type: 'fruchterman',
gravity: 10,
speed: 5,
clustering: true,
nodeClusterBy: 'cluster',
},
node: {
style: {
size: 20,
stroke: '#5B8FF9',
fill: '#C6E5FF',
lineWidth: 1,
labelPlacement: 'center',
labelText: (d) => d.id,
labelBackground: false,
},
},
edge: {
style: {
endArrow: true,
endArrowPath: 'M 0,0 L 4,2 L 4,-2 Z',
},
},
behaviors: ['drag-canvas', 'drag-element'],
animation: true,
});
graph.render();

Some files were not shown because too many files have changed in this diff Show More