const container = document.getElementById('container');
const width = container.scrollWidth;
const height = container.scrollHeight || 900;
const graph = new G6.Graph({
container: 'container',
width,
height,
layout: {
type: 'force',
preventOverlap: true,
linkDistance: 100, // 可选,边长
nodeStrength: -100, // 可选
edgeStrength: 0.1, // 可选
collideStrength: 1, // 可选
nodeSize: 50, // 可选
alpha: 0.3, // 可选
alphaDecay: 0.028, // 可选
alphaMin: 0.01,
onTick: () => {
graph.refreshPositions();
}, // 可选
},
modes: {
default: ['zoom-canvas', 'drag-canvas', 'drag-node'],
},
defaultNode: {
size: 50,
type: "menu-node"
},
defaultEdge: {
style: {
endArrow: true,
lineAppendWidth: 4,
},
labelCfg: {
autoRotate: true,
style: {
fill: '#1890ff',
fontSize: 14,
background: {
fill: '#ffffff',
// stroke: '#9EC9FF',
padding: [2, 2, 2, 2],
radius: 2,
},
},
},
},
});
// 截断长文本。length 为文本截断后长度,elipsis 是后缀
const formatText = (text, length = 5, elipsis = "...") => {
if (!text) return "";
if (text.length > length) {
return `${text.substr(0, length)}${elipsis}`;
}
return text;
};
// 清除图上所有节点的 selected 状态及相应样式
const clearFocusNodeState = (graph) => {
const focusNodes = graph.findAllByState("node", "selected");
focusNodes.forEach((fnode) => {
graph.setItemState(fnode, "selected", false); // false
});
};
// 清除图上所有边的 selected 状态及相应样式
const clearFocusEdgeState = (graph) => {
const focusEdges = graph.findAllByState("edge", "selected");
focusEdges.forEach((fedge) => {
graph.setItemState(fedge, "selected", false);
});
};
const clearFocusItemState = (graph) => {
if (!graph) return;
clearFocusNodeState(graph);
clearFocusEdgeState(graph);
};
G6.registerNode(
"menu-node",
{
draw(cfg, group) {
let r = 50;
if (!isNaN(cfg.size)) {
r = cfg.size / 2;
} else if (Array.isArray(cfg.size)) {
r = cfg.size[0] / 2;
}
nodebars.forEach((bar) => {
group.addShape("path", {
attrs: {
path: bar.path,
lineWidth: 0,
fill: "#D2D5DA",
cursor: "pointer",
},
// must be assigned in G6 3.3 and later versions. it can be any value you want
name: bar.key,
title: bar.title,
visible: false,
});
group.addShape("image", {
attrs: bar.icon,
style: {
transform: "translate(-8,38) scale(0.7)",
},
// must be assigned in G6 3.3 and later versions. it can be any value you want
name: bar.key + "icon",
visible: false,
capture: false,
});
});
const keyShape = group.addShape("circle", {
attrs: {
x: 0,
y: 0,
r,
fill: '#C990C0',
opacity: 0.8,
stroke: '#5B8FF9',
lineWidth: 0,
cursor: "pointer",
shadowColor: '#5B8FF9',
shadowBlur: 0
},
name: "aggregated-node-keyShape",
});
let labelStyle = {};
if (cfg.labelCfg) {
labelStyle = Object.assign(
labelStyle,
cfg.labelCfg.style
);
}
if (cfg.label) {
const text = formatText(cfg.label);
let labelStyle = {};
if (cfg.labelCfg) {
labelStyle = Object.assign(
labelStyle,
cfg.labelCfg.style
);
}
group.addShape("text", {
attrs: {
text,
x: 0,
y: 4,
textAlign: "center",
textBaseLine: "alphabetic",
cursor: "pointer",
fill: "#fff",
opacity: 0.85,
fontWeight: 400,
pointerEvents: "none",
// stroke: global.edge.labelCfg.style.stroke,
},
name: "text-shape",
className: "text-shape",
draggable: true,
capture: false,
});
}
return keyShape;
},
setState: (name, value, item) => {
const group = item.get("group");
if (name === "highlight") {
if (item.hasState("selected")) {
return;
}
const halo = group.find(
(e) => e.get("name") === "aggregated-node-keyShape"
);
const keyShape = item.getKeyShape();
const colorSet = item.getModel().colorSet;
if (value) {
// halo.attr("shadowBlur", 10);
halo.attr("lineWidth", 3);
} else {
// halo && halo.hide();
// halo.attr("shadowBlur", 0);
halo.attr("lineWidth", 0);
}
} else if (name === "selected") {
const halo = group.find(
(e) => e.get("name") === "aggregated-node-keyShape"
);
const nodebars = group.findAll((e) =>
e.get("name").includes("menu")
);
const label = group.find(
(e) => e.get("name") === "text-shape"
);
const keyShape = item.getKeyShape();
const colorSet = item.getModel().colorSet;
if (value) {
nodebars.forEach((p) => p.show());
halo.attr("shadowBlur", 10);
halo.attr("lineWidth", 3);
// keyShape.attr("fill", 'yellow');
label && label.attr("fontWeight", 800);
} else {
nodebars.forEach((p) => p.hide());
halo.attr("shadowBlur", 0);
halo.attr("lineWidth", 0);
// keyShape.attr("fill", colorSet.mainFill); // '#2B384E'
label && label.attr("fontWeight", 400);
}
} else if (name.includes("highlight-menu")) {
let menu = name.split("highlight-")[1];
let menuShape = group.find(
(e) => e.get("name") === menu
);
if (value) {
menuShape.attr({
fill: "#b9b9b9",
});
} else {
menuShape.attr({
fill: "#D2D5DA",
});
}
}
},
update: undefined,
},
"aggregated-node"
); // 这样可以继承 aggregated-node 的 setState
const bindListener = (graph) => {
graph.on("node:mouseenter", (evt) => {
const { item } = evt;
const model = item.getModel();
// const currentLabel = model.label;
// model.oriFontSize = model.labelCfg.style.fontSize;
// item.update({
// label: model.oriLabel,
// });
// model.oriLabel = currentLabel;
graph.setItemState(item, "highlight", true);
item.toFront();
});
graph.on("node:mouseleave", (evt) => {
const { item } = evt;
const model = item.getModel();
const currentLabel = model.label;
// item.update({
// label: model.oriLabel,
// });
model.oriLabel = currentLabel;
graph.setItemState(item, "highlight", false);
});
graph.on("edge:mouseenter", (evt) => {
const { item } = evt;
const model = item.getModel();
const currentLabel = model.label;
item.update({
label: model.oriLabel,
});
model.oriLabel = currentLabel;
graph.setItemState(item, "highlight", true);
item.toFront();
item.getSource().toFront();
item.getTarget().toFront();
});
graph.on("edge:mouseleave", (evt) => {
const { item } = evt;
const model = item.getModel();
const currentLabel = model.label;
item.update({
label: model.oriLabel,
});
model.oriLabel = currentLabel;
graph.setItemState(item, "highlight", false);
});
// click node to show the detail drawer
graph.on("node:click", (evt) => {
clearFocusItemState(graph);
const { item } = evt;
graph.setItemState(item, "selected", true);
});
// click edge to show the detail of integrated edge drawer
graph.on("edge:click", (evt) => {
clearFocusItemState(graph);
const { item } = evt;
// highlight the clicked edge
graph.setItemState(item, "selected", true);
});
// click canvas to cancel all the selected state
graph.on("canvas:click", (evt) => {
clearFocusItemState(graph);
console.log(
graph.getGroup(),
graph.getGroup().getBBox(),
graph.getGroup().getCanvasBBox()
);
});
graph.on("afterupdateitem", (evt) => {
const nodes = graph.findAll("node", (node) => {
return node.get("visible");
});
const edges = graph.findAll("edge", (edge) => {
return edge.get("visible");
});
});
graph.on("afteritemvisibilitychange", (evt) => {
const nodes = graph.findAll("node", (node) => {
return node.get("visible");
});
const edges = graph.findAll("edge", (edge) => {
return edge.get("visible");
});
_this.nodesLength = nodes.length;
_this.edgesLength = edges.length;
});
// graph.on("afterrender", (evt) => {
// let chartData = graph.save();
// console.log(2142, chartData);
// });
};
bindListener(graph);
function refreshDragedNodePosition(e) {
const model = e.item.get('model');
model.fx = e.x;
model.fy = e.y;
}
graph.on('node:dragstart', function (e) {
graph.layout();
refreshDragedNodePosition(e);
});
graph.on('node:drag', function (e) {
const forceLayout = graph.get('layoutController').layoutMethods[0];
forceLayout.execute();
refreshDragedNodePosition(e);
});
// graph.on('node:dragend', function (e) {
// e.item.get('model').fx = null;
// e.item.get('model').fy = null;
// });
fetch('https://gw.alipayobjects.com/os/antvdemo/assets/data/relations.json')
.then((res) => res.json())
.then((res) => {
// --------------------------------------------------------------大数据测试
let data = {
nodes: res.nodes.map(p => {
return {
...p,
label: 888
}
}),
edges: res.edges.map(function (edge, i) {
edge.id = 'edge' + i;
edge.label = 'sadasd'
return Object.assign({}, edge);
}),
}
// --------------------------------------------------多边测试
// const data = {
// nodes: [
// {
// id: 'node1',
// x: 50,
// y: 350,
// label: 'Adsfgdfffffffffffffffffffffffffffffffffffffffs',
// },
// {
// id: 'node2',
// x: 250,
// y: 150,
// label: 'B',
// },
// {
// id: 'node3',
// x: 450,
// y: 350,
// label: 'C',
// },
// ],
// edges: [],
// };
// for (let i = 0; i < 10; i++) {
// data.edges.push({
// source: 'node1',
// target: 'node2',
// label: `${i}th edge of A-B`,
// });
// }
// for (let i = 0; i < 5; i++) {
// data.edges.push({
// source: 'node2',
// target: 'node3',
// label: `${i}th edge of B-C`,
// });
// }
// // loop边测试
// data.edges.push({
// source: 'node1',
// target: 'node1',
// type: "loop",
// label: `${1}th edge of B-C`,
// });
// data.edges.push({
// source: 'node1',
// target: 'node1',
// type: "loop",
// label: `${1}th edge of B-C`,
// });
// data.edges.push({
// source: 'node1',
// target: 'node1',
// type: "loop",
// label: `${1}th edge of B-C`,
// });
// data.edges.push({
// source: 'node2',
// target: 'node2',
// type: "loop",
// label: `${1}th edge of B-C`,
// });
// data.edges.push({
// source: 'node3',
// target: 'node3',
// type: "loop",
// label: `${1}th edge of B-C`,
// });
graph.data(data);
G6.Util.processParallelEdges(data.edges);
graph.render();
// setTimeout(() => {
// const data = {
// nodes: [
// {
// id: 'node1',
// x: 50,
// y: 350,
// label: 'Adsfgdfffffffffffffffffffffffffffffffffffffffs',
// },
// {
// id: 'node2',
// x: 250,
// y: 150,
// label: 'B',
// },
// {
// id: 'node3',
// x: 450,
// y: 350,
// label: 'C',
// },
// ],
// edges: [],
// };
// for (let i = 0; i < 10; i++) {
// data.edges.push({
// source: 'node1',
// target: 'node2',
// label: `${i}th edge of A-B`,
// });
// }
// for (let i = 0; i < 5; i++) {
// data.edges.push({
// source: 'node2',
// target: 'node3',
// label: `${i}th edge of B-C`,
// });
// }
// // loop边测试
// data.edges.push({
// source: 'node1',
// target: 'node1',
// type: "loop",
// label: `${1}th edge of B-C`,
// });
// data.edges.push({
// source: 'node1',
// target: 'node1',
// type: "loop",
// label: `${1}th edge of B-C`,
// });
// data.edges.push({
// source: 'node1',
// target: 'node1',
// type: "loop",
// label: `${1}th edge of B-C`,
// });
// data.edges.push({
// source: 'node2',
// target: 'node2',
// type: "loop",
// label: `${1}th edge of B-C`,
// });
// data.edges.push({
// source: 'node3',
// target: 'node3',
// type: "loop",
// label: `${1}th edge of B-C`,
// });
// graph.changeData(data)
// }, 5000);
// setTimeout(() => {
// const edges = []
// for (let i = 0; i < 10; i++) {
// edges.push({
// source: 'node1',
// target: 'node2',
// label: `${i}th edge of A-B`,
// });
// }
// for (let i = 0; i < 5; i++) {
// edges.push({
// source: 'node2',
// target: 'node3',
// label: `${i}th edge of B-C`,
// });
// }
// // loop边测试
// edges.push({
// source: 'node1',
// target: 'node1',
// type: "loop",
// label: `${1}th edge of B-C`,
// });
// edges.push({
// source: 'node1',
// target: 'node1',
// type: "loop",
// label: `${1}th edge of B-C`,
// });
// edges.push({
// source: 'node1',
// target: 'node1',
// type: "loop",
// label: `${1}th edge of B-C`,
// });
// edges.push({
// source: 'node2',
// target: 'node2',
// type: "loop",
// label: `${1}th edge of B-C`,
// });
// edges.push({
// source: 'node3',
// target: 'node3',
// type: "loop",
// label: `${1}th edge of B-C`,
// });
// edges.forEach(p=>{
// graph.addItem('edge', p)
// })
// }, 5000);
});
if (typeof window !== 'undefined')
window.onresize = () => {
if (!graph || graph.get('destroyed')) return;
if (!container || !container.scrollWidth || !container.scrollHeight) return;
graph.changeSize(container.scrollWidth, container.scrollHeight);
};
git地址;
https://gitee.com/youmoxiang_1/imitating-neo4j.githttps://gitee.com/youmoxiang_1/imitating-neo4j.git