mirror of
https://gitee.com/antv/g6.git
synced 2024-12-04 12:49:04 +08:00
649 lines
18 KiB
HTML
649 lines
18 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8" />
|
|
<title>Expand Nodes with Force Layout</title>
|
|
</head>
|
|
|
|
<body>
|
|
<div id="mountNode"></div>
|
|
<script src="../build/g6.js"></script>
|
|
<script src="../build/force.js"></script>
|
|
<script src="./assets/d3-4.13.0.min.js"></script>
|
|
<script>
|
|
const getNodeById = {
|
|
success: true,
|
|
data: {
|
|
nodes: [
|
|
{
|
|
id: '20',
|
|
nodeType: 'Person',
|
|
label: 'name1',
|
|
properties: {
|
|
age: '37',
|
|
annualIncome: '0',
|
|
career: '',
|
|
credit: '',
|
|
education: '',
|
|
email: '',
|
|
gender: 'M',
|
|
highestEducation: '',
|
|
isBusiness: '',
|
|
isOnBlacklist: '',
|
|
name: 'name1',
|
|
permanentResidenceAddress: '',
|
|
residentialAddress: '',
|
|
sbh: '',
|
|
telephone: '',
|
|
workUnit: '',
|
|
},
|
|
},
|
|
{
|
|
id: '1',
|
|
nodeType: 'Person',
|
|
label: 'name2',
|
|
properties: {
|
|
age: '31',
|
|
annualIncome: '0',
|
|
career: '',
|
|
credit: '',
|
|
education: '',
|
|
email: '',
|
|
gender: 'FM',
|
|
highestEducation: '',
|
|
isBusiness: '',
|
|
isOnBlacklist: '',
|
|
name: 'name2',
|
|
permanentResidenceAddress: '',
|
|
residentialAddress: '',
|
|
sbh: '',
|
|
telephone: '',
|
|
workUnit: '',
|
|
},
|
|
},
|
|
],
|
|
edges: [
|
|
{
|
|
source: '20',
|
|
target: '1',
|
|
},
|
|
],
|
|
queryResult: '',
|
|
graphView: true,
|
|
},
|
|
errorCode: null,
|
|
msg: null,
|
|
};
|
|
|
|
// 根据节点扩展
|
|
const getNodesByNodeId = {
|
|
success: true,
|
|
data: {
|
|
nodes: [
|
|
{
|
|
id: '20',
|
|
nodeType: 'Person',
|
|
label: '祁同伟',
|
|
properties: {
|
|
age: '37',
|
|
annualIncome: '0',
|
|
career: '',
|
|
credit: '',
|
|
education: '',
|
|
email: '',
|
|
gender: 'M',
|
|
highestEducation: '',
|
|
isBusiness: '',
|
|
isOnBlacklist: '',
|
|
name: '祁同伟',
|
|
permanentResidenceAddress: '',
|
|
residentialAddress: '',
|
|
sbh: '',
|
|
telephone: '',
|
|
workUnit: '',
|
|
},
|
|
},
|
|
{
|
|
id: '22',
|
|
nodeType: 'Person',
|
|
label: '高晓秦',
|
|
properties: {
|
|
age: '23',
|
|
annualIncome: '0',
|
|
career: '',
|
|
credit: '',
|
|
education: '',
|
|
email: '',
|
|
gender: 'F',
|
|
highestEducation: '',
|
|
isBusiness: '',
|
|
isOnBlacklist: '',
|
|
name: '高晓秦',
|
|
permanentResidenceAddress: '',
|
|
residentialAddress: '',
|
|
sbh: '',
|
|
telephone: '',
|
|
workUnit: '',
|
|
},
|
|
},
|
|
{
|
|
id: '10',
|
|
nodeType: 'Person',
|
|
label: '高育良',
|
|
properties: {
|
|
age: '57',
|
|
annualIncome: '0',
|
|
career: '',
|
|
credit: '',
|
|
education: '',
|
|
email: '',
|
|
gender: 'M',
|
|
highestEducation: '',
|
|
isBusiness: '',
|
|
isOnBlacklist: '',
|
|
name: '高育良',
|
|
permanentResidenceAddress: '',
|
|
residentialAddress: '',
|
|
sbh: '',
|
|
telephone: '',
|
|
workUnit: '',
|
|
},
|
|
},
|
|
{
|
|
id: '12',
|
|
nodeType: 'Person',
|
|
label: '高晓凤',
|
|
properties: {
|
|
age: '22',
|
|
annualIncome: '0',
|
|
career: '',
|
|
credit: '',
|
|
education: '',
|
|
email: '',
|
|
gender: 'F',
|
|
highestEducation: '',
|
|
isBusiness: '',
|
|
isOnBlacklist: '',
|
|
name: '高晓凤',
|
|
permanentResidenceAddress: '',
|
|
residentialAddress: '',
|
|
sbh: '',
|
|
telephone: '',
|
|
workUnit: '',
|
|
},
|
|
},
|
|
{
|
|
id: '21',
|
|
nodeType: 'Person',
|
|
label: '梁璐',
|
|
properties: {
|
|
age: '40',
|
|
annualIncome: '0',
|
|
career: '',
|
|
credit: '',
|
|
education: '',
|
|
email: '',
|
|
gender: 'F',
|
|
highestEducation: '',
|
|
isBusiness: '',
|
|
isOnBlacklist: '',
|
|
name: '梁璐',
|
|
permanentResidenceAddress: '',
|
|
residentialAddress: '',
|
|
sbh: '',
|
|
telephone: '',
|
|
workUnit: '',
|
|
},
|
|
},
|
|
{
|
|
id: '11',
|
|
nodeType: 'Person',
|
|
label: '吴淑芬',
|
|
properties: {
|
|
age: '50',
|
|
annualIncome: '0',
|
|
career: '',
|
|
credit: '',
|
|
education: '',
|
|
email: '',
|
|
gender: 'F',
|
|
highestEducation: '',
|
|
isBusiness: '',
|
|
isOnBlacklist: '',
|
|
name: '吴淑芬',
|
|
permanentResidenceAddress: '',
|
|
residentialAddress: '',
|
|
sbh: '',
|
|
telephone: '',
|
|
workUnit: '',
|
|
},
|
|
},
|
|
{
|
|
id: '100',
|
|
nodeType: 'Enterprise',
|
|
label: '小米',
|
|
properties: {
|
|
IsChineseFunded: '',
|
|
businessScope: '',
|
|
credit: '',
|
|
employeesNumber: '0',
|
|
enterpriseNature: '',
|
|
establishmentTime: '',
|
|
holdingType: '',
|
|
importExportRightMark: '',
|
|
industryType: '',
|
|
isAdvantageEnterprise: '',
|
|
isLeadingEnterprise: '',
|
|
isOnBlacklist: '',
|
|
isOvercapacity: '',
|
|
isRegionalKeyEnterprise: '',
|
|
isSangaoEnterprise: '',
|
|
name: '小米',
|
|
registeredCapital: '0',
|
|
registrationAddress: '',
|
|
sbh: '',
|
|
size: '',
|
|
stockCode: '',
|
|
telephone: '',
|
|
},
|
|
},
|
|
{
|
|
id: '102',
|
|
nodeType: 'Enterprise',
|
|
label: '茅台',
|
|
properties: {
|
|
IsChineseFunded: '',
|
|
businessScope: '',
|
|
credit: '',
|
|
employeesNumber: '0',
|
|
enterpriseNature: '',
|
|
establishmentTime: '',
|
|
holdingType: '',
|
|
importExportRightMark: '',
|
|
industryType: '',
|
|
isAdvantageEnterprise: '',
|
|
isLeadingEnterprise: '',
|
|
isOnBlacklist: '',
|
|
isOvercapacity: '',
|
|
isRegionalKeyEnterprise: '',
|
|
isSangaoEnterprise: '',
|
|
name: '茅台',
|
|
registeredCapital: '0',
|
|
registrationAddress: '',
|
|
sbh: '',
|
|
size: '',
|
|
stockCode: '',
|
|
telephone: '',
|
|
},
|
|
},
|
|
],
|
|
edges: [
|
|
{
|
|
target: '100',
|
|
source: '20',
|
|
},
|
|
{
|
|
target: '102',
|
|
source: '20',
|
|
},
|
|
{
|
|
target: '10',
|
|
source: '20',
|
|
},
|
|
{
|
|
target: '11',
|
|
source: '20',
|
|
},
|
|
{
|
|
target: '21',
|
|
source: '20',
|
|
},
|
|
{
|
|
target: '21',
|
|
source: '20',
|
|
},
|
|
{
|
|
target: '22',
|
|
source: '20',
|
|
},
|
|
{
|
|
target: '11',
|
|
source: '10',
|
|
},
|
|
{
|
|
target: '12',
|
|
source: '10',
|
|
},
|
|
{
|
|
target: '22',
|
|
source: '21',
|
|
},
|
|
{
|
|
target: '22',
|
|
source: '12',
|
|
},
|
|
],
|
|
queryResult: '',
|
|
graphView: true,
|
|
},
|
|
errorCode: null,
|
|
msg: null,
|
|
};
|
|
|
|
// 根据节点扩展
|
|
const getNodesByNodeId2 = {
|
|
success: true,
|
|
data: {
|
|
nodes: [
|
|
{
|
|
id: '22',
|
|
nodeType: 'Person',
|
|
label: '高晓秦',
|
|
properties: {
|
|
age: '23',
|
|
annualIncome: '0',
|
|
career: '',
|
|
credit: '',
|
|
education: '',
|
|
email: '',
|
|
gender: 'F',
|
|
highestEducation: '',
|
|
isBusiness: '',
|
|
isOnBlacklist: '',
|
|
name: '高晓秦',
|
|
permanentResidenceAddress: '',
|
|
residentialAddress: '',
|
|
sbh: '',
|
|
telephone: '',
|
|
workUnit: '',
|
|
},
|
|
},
|
|
{
|
|
id: '1000',
|
|
nodeType: 'Person',
|
|
label: '晓秦1',
|
|
properties: {
|
|
age: '57',
|
|
annualIncome: '0',
|
|
career: '',
|
|
credit: '',
|
|
education: '',
|
|
email: '',
|
|
gender: 'M',
|
|
highestEducation: '',
|
|
isBusiness: '',
|
|
isOnBlacklist: '',
|
|
name: '晓秦1',
|
|
permanentResidenceAddress: '',
|
|
residentialAddress: '',
|
|
sbh: '',
|
|
telephone: '',
|
|
workUnit: '',
|
|
},
|
|
},
|
|
{
|
|
id: '1001',
|
|
nodeType: 'Person',
|
|
label: '晓秦2',
|
|
properties: {
|
|
age: '22',
|
|
annualIncome: '0',
|
|
career: '',
|
|
credit: '',
|
|
education: '',
|
|
email: '',
|
|
gender: 'F',
|
|
highestEducation: '',
|
|
isBusiness: '',
|
|
isOnBlacklist: '',
|
|
name: '晓秦2',
|
|
permanentResidenceAddress: '',
|
|
residentialAddress: '',
|
|
sbh: '',
|
|
telephone: '',
|
|
workUnit: '',
|
|
},
|
|
},
|
|
{
|
|
id: '1002',
|
|
nodeType: 'Person',
|
|
label: '晓秦3',
|
|
properties: {
|
|
age: '40',
|
|
annualIncome: '0',
|
|
career: '',
|
|
credit: '',
|
|
education: '',
|
|
email: '',
|
|
gender: 'F',
|
|
highestEducation: '',
|
|
isBusiness: '',
|
|
isOnBlacklist: '',
|
|
name: '晓秦3',
|
|
permanentResidenceAddress: '',
|
|
residentialAddress: '',
|
|
sbh: '',
|
|
telephone: '',
|
|
workUnit: '',
|
|
},
|
|
},
|
|
{
|
|
id: '1003',
|
|
nodeType: 'Person',
|
|
label: '晓秦4',
|
|
properties: {
|
|
age: '40',
|
|
annualIncome: '0',
|
|
career: '',
|
|
credit: '',
|
|
education: '',
|
|
email: '',
|
|
gender: 'F',
|
|
highestEducation: '',
|
|
isBusiness: '',
|
|
isOnBlacklist: '',
|
|
name: '晓秦4',
|
|
permanentResidenceAddress: '',
|
|
residentialAddress: '',
|
|
sbh: '',
|
|
telephone: '',
|
|
workUnit: '',
|
|
},
|
|
},
|
|
{
|
|
id: '1004',
|
|
nodeType: 'Person',
|
|
label: '晓秦5',
|
|
properties: {
|
|
age: '40',
|
|
annualIncome: '0',
|
|
career: '',
|
|
credit: '',
|
|
education: '',
|
|
email: '',
|
|
gender: 'F',
|
|
highestEducation: '',
|
|
isBusiness: '',
|
|
isOnBlacklist: '',
|
|
name: '晓秦5',
|
|
permanentResidenceAddress: '',
|
|
residentialAddress: '',
|
|
sbh: '',
|
|
telephone: '',
|
|
workUnit: '',
|
|
},
|
|
},
|
|
],
|
|
edges: [
|
|
{
|
|
target: '22',
|
|
source: '1000',
|
|
},
|
|
{
|
|
target: '22',
|
|
source: '1001',
|
|
},
|
|
{
|
|
target: '22',
|
|
source: '1002',
|
|
},
|
|
{
|
|
target: '1001',
|
|
source: '1002',
|
|
},
|
|
{
|
|
target: '1000',
|
|
source: '1',
|
|
},
|
|
{
|
|
target: '20',
|
|
source: '1002',
|
|
},
|
|
{
|
|
target: '22',
|
|
source: '1003',
|
|
},
|
|
{
|
|
target: '22',
|
|
source: '1004',
|
|
},
|
|
],
|
|
queryResult: '',
|
|
graphView: true,
|
|
},
|
|
errorCode: null,
|
|
msg: null,
|
|
};
|
|
|
|
let clickedNodeId = -1;
|
|
let clickedNodePos = {
|
|
x: 500,
|
|
y: 300,
|
|
};
|
|
|
|
// 封装点击添加边的交互
|
|
G6.registerBehavior('click-add-node', {
|
|
getEvents() {
|
|
return {
|
|
'node:click': 'onClick',
|
|
};
|
|
},
|
|
onClick(ev) {
|
|
const itemModel = ev.item.getModel();
|
|
clickedNodeId = itemModel.id;
|
|
clickedNodePos.x = itemModel.x;
|
|
clickedNodePos.y = itemModel.y;
|
|
const graph = this.graph;
|
|
const nodes = graph.getNodes();
|
|
const edges = graph.getEdges();
|
|
let newData = getNodeById.data;
|
|
if (itemModel.id == 20) newData = getNodesByNodeId.data;
|
|
else if (itemModel.id == 22) newData = getNodesByNodeId2.data;
|
|
let newNodeModels = newData.nodes;
|
|
let newEdgeModels = newData.edges;
|
|
let allNodeModels = [],
|
|
allEdgeModels = [];
|
|
|
|
// fix the existing nodes by set the fx and fy to the current value
|
|
// to cancel the fix the node, set fx = null fy = null
|
|
nodes.forEach((n, i) => {
|
|
const model = n.getModel();
|
|
model.fixed = true;
|
|
model.fx = model.x;
|
|
model.fy = model.y;
|
|
allNodeModels.push(model);
|
|
});
|
|
|
|
newNodeModels.forEach((nodeModel, i) => {
|
|
// if it does not exist in nodes
|
|
let exist = false;
|
|
nodes.forEach((n, i) => {
|
|
if (n.getModel().id == nodeModel.id) {
|
|
exist = true;
|
|
return;
|
|
}
|
|
});
|
|
// then add it into graph
|
|
if (!exist) {
|
|
// set the initial positions of the new nodes at the clicked node
|
|
nodeModel.x = itemModel.x;
|
|
nodeModel.y = itemModel.y;
|
|
const node = graph.addItem('node', nodeModel);
|
|
allNodeModels.push(nodeModel);
|
|
nodes.push(node);
|
|
}
|
|
});
|
|
|
|
newEdgeModels.forEach((em, i) => {
|
|
// if it does not exist in nodes
|
|
let exist = false;
|
|
edges.forEach((e, i) => {
|
|
const eModel = e.getModel();
|
|
if (eModel.source == em.source && eModel.target == em.target) {
|
|
exist = true;
|
|
return;
|
|
}
|
|
});
|
|
if (!exist) {
|
|
const edge = graph.addItem('edge', em);
|
|
edges.push(edge);
|
|
}
|
|
});
|
|
edges.forEach((e, i) => {
|
|
allEdgeModels.push(e.getModel());
|
|
});
|
|
|
|
graph.changeData({
|
|
nodes: allNodeModels,
|
|
edges: allEdgeModels,
|
|
});
|
|
},
|
|
});
|
|
|
|
let data = getNodeById.data;
|
|
|
|
const graph = new G6.Graph({
|
|
container: 'mountNode',
|
|
width: 1000,
|
|
height: 600,
|
|
layout: {
|
|
type: 'force',
|
|
center: [500, 300],
|
|
// 向心力作用点
|
|
nodeStrength: -30,
|
|
// 节点作用力
|
|
preventOverlap: true,
|
|
// 是否防止节点相互覆盖
|
|
nodeSize: 40,
|
|
// 节点大小 / 直径
|
|
edgeStrength: 0.1,
|
|
// 边的作用力, 默认为根据节点的入度出度自适应
|
|
linkDistance: 100,
|
|
// 默认边长度
|
|
},
|
|
modes: {
|
|
default: ['drag-node', 'click-select', 'click-add-node'],
|
|
},
|
|
defaultNode: {
|
|
size: [40, 40],
|
|
color: 'steelblue',
|
|
},
|
|
defaultEdge: {
|
|
size: 1,
|
|
color: '#e2e2e2',
|
|
},
|
|
});
|
|
|
|
graph.data(data);
|
|
|
|
graph.render();
|
|
graph.on('node:dragend', ev => {
|
|
const itemModel = ev.item.getModel();
|
|
itemModel.fixed = true;
|
|
itemModel.fx = ev.x;
|
|
itemModel.fy = ev.y;
|
|
});
|
|
</script>
|
|
</body>
|
|
</html>
|