前言
G6官方地址:https://antv-g6.gitee.io/zh/examples/gallery
实现的效果:动画、放大、缩小、拖拽、移动、点击交互等
有两版代码:一个是可以在G6官网里运行的。一个是在angular里运行的
我暂时未处理的问题:图的适配问题,不能随着屏幕大小变化而变化
变形
改变连接节点就行
代码
可直接在官网上运行的代码:
ps:随便找一个案例,替换掉里面的代码就行
import G6 from '@antv/g6';
G6.registerEdge(
'circle-running',
{
afterDraw(cfg, group) {
// 得到组中的第一个形状,它是边的路径在这里
const shape = group.get('children')[0];
// 边缘路径的起始位置
const startPoint = shape.getPoint(0);
// 添加一个在线上运动的圆球
const circle = group.addShape('circle', {
attrs: {
x: startPoint.x,
y: startPoint.y,
fill: '#1890ff',
r: 3,
},
name: 'circle-shape',
});
// 小球的动画
circle.animate(
(ratio) => {
// 每个帧的操作。比率范围从0到1,表示动画的进度。返回修改后的配置
// 根据比例得到边缘的位置
const tmpPoint = shape.getPoint(ratio);
// 这里返回修改后的配置,这里是x和y
return {
x: tmpPoint.x,
y: tmpPoint.y,
};
},
{
repeat: true, // 是否重复执行动画
duration: 3000, // 执行一次的持续时间
},
);
},
},
'line', // 扩展内置边'cubic'
);
const lightColors = [
'#8FE9FF',
'#87EAEF',
'#FFC9E3',
'#A7C2FF',
'#FFA1E3',
'#FFE269',
'#BFCFEE',
'#FFA0C5',
'#D5FF86',
];
const darkColors = [
'#7DA8FF',
'#44E6C1',
'#FF68A7',
'#7F86FF',
'#AE6CFF',
'#FF5A34',
'#5D7092',
'#FF6565',
'#6BFFDE',
];
const uLightColors = [
'#CFF6FF',
'#BCFCFF',
'#FFECF5',
'#ECFBFF',
'#EAD9FF',
'#FFF8DA',
'#DCE2EE',
'#FFE7F0',
'#EEFFCE',
];
const uDarkColors = [
'#CADBFF',
'#A9FFEB',
'#FFC4DD',
'#CACDFF',
'#FFD4F2',
'#FFD3C9',
'#EBF2FF',
'#FFCBCB',
'#CAFFF3',
];
const gColors = [];
const unlightColorMap = new Map();
const container = document.getElementById('container');
const width = container.scrollWidth;
const height = container.scrollHeight || 500;
const graph = new G6.Graph({
container: 'container',
width,
height,
defaultEdge: {
type: 'circle-running',
style: {
lineWidth: 2,
stroke: '#bae7ff',
},
},
defaultNode: {
type: 'circle', // 'bubble'
size: 95,
labelCfg: {
position: 'center',
style: {
fill: 'white',
fontStyle: 'bold',
},
},
},
animate: true,
layout: {
type: 'force',
preventOverlap: true,
linkDistance: (d) => {
if (d.source.id === 'node0') {
return 200;
}
return 30;
},
nodeStrength: (d) => {
if (d.isLeaf) {
return -200;
}
return -100;
},
},
defaultNode: {
color: '#5B8FF9',
},
// 整个图可以拖动、放大缩小
modes: {
default: ['drag-canvas', 'zoom-canvas'],
},
});
const data = {
nodes: [
{ id: 'node0', label: `组织管理\n(12)`, size: 100 },
{ id: 'node1', label: '管理单位', size: 100 },
{ id: 'node2', label: '角色管理', size: 100 },
{ id: 'node3', label: '岗位管理', size: 100 },
{ id: 'node4', label: '岗位管理', size: 100 },
{ id: 'node5', label: '岗位管理', size: 100 },
],
edges: [
{ source: 'node0', target: 'node1' },
{ source: 'node0', target: 'node2' },
{ source: 'node0', target: 'node3' },
{ source: 'node0', target: 'node4' },
{ source: 'node0', target: 'node5' },
],
};
lightColors.forEach((lcolor, i) => {
gColors.push('l(0) 0:' + lcolor + ' 1:' + darkColors[i]);
unlightColorMap.set(gColors[i], 'l(0) 0:' + uLightColors[i] + ' 1:' + uDarkColors[i]);
});
console.log(gColors);
for ( let i = 0; i < data.nodes.length; i++ ) {
data.nodes[i].color = gColors[i];
data.nodes[i].style = {
fill: gColors[i],
lineWidth: 0
};
data.nodes[i].labelCfg = {
style: {
fontSize: 20,
fill: '#fff',
fontWeight: 300,
},
};
}
const nodes = data.nodes;
graph.data({
nodes,
edges: data.edges.map(function (edge, i) {
edge.id = 'edge' + i;
return Object.assign({}, edge);
}),
});
graph.render();
graph.on('node:dragstart', function (e) {
graph.layout();
refreshDragedNodePosition(e);
});
graph.on('node:drag', function (e) {
refreshDragedNodePosition(e);
});
graph.on('node:dragend', function (e) {
e.item.get('model').fx = null;
e.item.get('model').fy = null;
});
graph.on('node:click', (evt) => {
window.alert('点击了');
});
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);
};
function refreshDragedNodePosition(e) {
const model = e.item.get('model');
model.fx = e.x;
model.fy = e.y;
}
我的代码
我是在angular11里使用的G6
<div #mountNodeLeft></div>
import { Component, OnInit, ViewChild } from '@angular/core';
@ViewChild('mountNodeLeft', { static: false }) mountNodeLeft!: any;
constructor() { }
ngOnInit(): void {
}
// tslint:disable-next-line:use-lifecycle-interface
ngAfterViewInit(): void {
setTimeout(() => { this.render(); }, 100);
}
// 下面两个函数是g6的图
// tslint:disable-next-line:typedef
render() {
const lightColors = [
'#8FE9FF',
'#87EAEF',
'#FFC9E3',
'#A7C2FF',
'#FFA1E3',
'#FFE269',
'#BFCFEE',
'#FFA0C5',
'#D5FF86',
];
const darkColors = [
'#7DA8FF',
'#44E6C1',
'#FF68A7',
'#7F86FF',
'#AE6CFF',
'#FF5A34',
'#5D7092',
'#FF6565',
'#6BFFDE',
];
const uLightColors = [
'#CFF6FF',
'#BCFCFF',
'#FFECF5',
'#ECFBFF',
'#EAD9FF',
'#FFF8DA',
'#DCE2EE',
'#FFE7F0',
'#EEFFCE',
];
const uDarkColors = [
'#CADBFF',
'#A9FFEB',
'#FFC4DD',
'#CACDFF',
'#FFD4F2',
'#FFD3C9',
'#EBF2FF',
'#FFCBCB',
'#CAFFF3',
];
const gColors: any = [];
const unlightColorMap = new Map();
lightColors.forEach((lcolor, i) => {
gColors.push('l(0) 0:' + lcolor + ' 1:' + darkColors[i]);
unlightColorMap.set(gColors[i], 'l(0) 0:' + uLightColors[i] + ' 1:' + uDarkColors[i]);
});
G6.registerEdge(
'circle-running',
{
// tslint:disable-next-line:typedef
afterDraw(cfg, group: any) {
// 得到组中的第一个形状,它是边的路径在这里
const shape = group.get('children')[0];
// 边缘路径的起始位置
const startPoint = shape.getPoint(0);
// 添加一个在线上运动的圆球
const circle = group.addShape('circle', {
attrs: {
x: startPoint.x,
y: startPoint.y,
fill: '#1890ff',
r: 3,
},
name: 'circle-shape',
});
// 小球的动画
circle.animate(
(ratio: any) => {
// 每个帧的操作。比率范围从0到1,表示动画的进度。返回修改后的配置
// 根据比例得到边缘的位置
const tmpPoint = shape.getPoint(ratio);
// 这里返回修改后的配置,这里是x和y
return {
x: tmpPoint.x,
y: tmpPoint.y,
};
},
{
repeat: true, // 是否重复执行动画
duration: 3000, // 执行一次的持续时间
},
);
},
},
'line', // 扩展内置边'cubic'
);
const width = window.innerWidth / 2;
const height = window.innerHeight - 300;
const graph = new G6.Graph({
container: this.mountNodeLeft.nativeElement,
width,
height,
// 改变线的颜色 绑定动画
defaultEdge: {
type: 'circle-running',
style: {
lineWidth: 2,
stroke: '#bae7ff',
},
},
layout: {
type: 'force',
preventOverlap: true,
linkDistance: (d: any) => {
// if (d.source.id === 'node0') {
// return 200;
// }
// return 30;
return 200;
},
// nodeStrength: (d: any) => {
// if (d.isLeaf) {
// return -200;
// }
// return -100;
},
},
defaultNode: {
color: '#5B8FF9',
},
// 整个图可以拖动、放大缩小
modes: {
default: ['drag-canvas', 'zoom-canvas'],
},
});
const data: any = {
nodes: [
{ id: 'node0', label: `组织管理\n(12)`, size: 110 },
{ id: 'node1', label: '管理单位', size: 110 },
{ id: 'node2', label: '角色管理', size: 110 },
{ id: 'node3', label: '岗位管理', size: 110 },
{ id: 'node4', label: '岗位管理', size: 110 },
{ id: 'node5', label: '岗位管理', size: 110 },
],
edges: [
{ source: 'node0', target: 'node1' },
{ source: 'node0', target: 'node2' },
{ source: 'node0', target: 'node3' },
{ source: 'node0', target: 'node4' },
{ source: 'node0', target: 'node5' },
],
};
// 修改图中的圆
for (let i = 0; i < data.nodes.length; i++) {
data.nodes[i].color = gColors[i];
data.nodes[i].style = {
fill: gColors[i],
lineWidth: 0,
cursor: 'pointer',
};
data.nodes[i].labelCfg = {
style: {
fontSize: 18,
fill: '#fff',
fontWeight: 300,
cursor: 'pointer',
},
};
}
const nodes = data.nodes;
graph.data({
nodes,
edges: data.edges.map((edge: any, i: any) => {
edge.id = 'edge' + i;
return Object.assign({}, edge);
}),
});
graph.render();
graph.on('node:dragstart', (e) => {
graph.layout();
this.refreshDragedNodePosition(e);
});
graph.on('node:drag', (e) => {
this.refreshDragedNodePosition(e);
});
graph.on('node:dragend', (e: any) => {
e.item.get('model').fx = null;
e.item.get('model').fy = null;
});
graph.on('node:click', (evt) => {
window.alert('点击了');
});
}
// tslint:disable-next-line:typedef
refreshDragedNodePosition(e: any) {
const model = e.item.get('model');
model.fx = e.x;
model.fy = e.y;
}