UI 树的实现
背景
目前需要实现一个 UI 树,用于展示设备树,以及设备树中设备的属性。与树状列表不同,UI 树需要有特定的交互方式,支持边以及当前节点的点击事件。
实现效果【复制到.html文件夹就看见了】
总体效果
点击节点效果
点击边效果
代码示例
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>G6 Tree Example</title>
<script src="https://gw.alipayobjects.com/os/lib/antv/g6/4.3.11/dist/g6.min.js"></script>
<style>
#container {
width: 100%;
height: 100vh;
border: 1px solid #eee;
}
</style>
</head>
<body>
<div id="container"></div>
<script>
// 树结构数据
const treeData = {
id: 'root',
label: 'CEO',
children: [
{
id: 'tech',
label: 'CTO',
type: 'modelRect',
description: '技术负责人',
children: [
{ id: 'frontend', label: 'Frontend' },
{ id: 'backend', label: 'Backend' }
]
},
{
id: 'finance',
label: 'CFO',
children: [
{ id: 'accounting', label: 'Accounting' },
{ id: 'investment', label: 'Investment' }
]
}
]
};
// 创建 TreeGraph 实例
const tree = new G6.TreeGraph({
container: 'container',
fitView: true,
modes: {
default: ['drag-canvas', 'zoom-canvas'],
edit: ['click-select-node', 'click-select-edge', 'drag-node', 'drag-canvas', 'zoom-canvas']
},
defaultNode: {
type: 'modelRect',
size: [200, 80],
style: {
fill: '#fff',
stroke: '#1890ff',
radius: 4
},
labelCfg: {
style: {
fill: '#333',
fontSize: 14
}
}
},
defaultEdge: {
type: 'cubic-horizontal',
style: {
stroke: '#999',
lineWidth: 2,
endArrow: false,
}
},
layout: {
type: 'compactBox',
direction: 'TB', // 树方向:Top-Bottom
getHeight: () => 40,
getWidth: () => 120,
getVGap: () => 40,
getHGap: () => 100
}
});
// 绑定交互事件
tree.on('node:click', e => {
const node = e.item;
console.log('节点点击:', node.getModel().label);
// 添加点击效果
node.update({
style: {
fill: '#e6f7ff',
stroke: '#1890ff'
}
});
});
tree.on('edge:click', e => {
const edge = e.item;
console.log('连线点击:', edge.getSource().getModel().label,
'->', edge.getTarget().getModel().label);
// 添加点击效果
edge.update({
style: {
stroke: '#ff4d4f',
lineWidth: 3
}
});
});
// 渲染树
tree.data(treeData);
tree.render();
// 适配画布尺寸
window.onresize = () => {
if (!tree || tree.get('destroyed')) return;
tree.changeSize(window.innerWidth, window.innerHeight);
tree.fitView();
};
</script>
</body>
</html>
实现方式
在网上进行了一波搜索,一开始我打算使用原生的 canvas 或者 svg 实现此类效果,但是发现实现起来比较麻烦,而且效果也不够好。后来我找到了一个开源的库,AntV G6 这个库可以很方便的实现树状图,而且效果很好,于是我决定使用这个库来实现 UI 树。
一. 安装 G6 库
npm install --save @antv/g6
import G6 from '@antv/g6';
或者像我一样使用 CDN 导入
// version <= 3.2
<script src="https://gw.alipayobjects.com/os/antv/pkg/_antv.g6-{$version}/build/g6.js"></script>
// version >= 3.3
<script src="https://gw.alipayobjects.com/os/lib/antv/g6/{$version}/dist/g6.min.js"></script>
// version >= 4.0
<script src="https://gw.alipayobjects.com/os/lib/antv/g6/4.3.11/dist/g6.min.js"></script>
二. 创建树图实例
// 创建 TreeGraph 实例
const tree = new G6.TreeGraph({
container: "container",
fitView: true,
modes: {
default: ["drag-canvas", "zoom-canvas"],
edit: [
"click-select-node",
"click-select-edge",
"drag-node",
"drag-canvas",
"zoom-canvas",
],
},
defaultNode: {
// 配置节点的相关内容
type: "modelRect",
// 配置节点的大小
size: [200, 80],
// 配置节点的样式
style: {
fill: "#fff",
stroke: "#1890ff",
radius: 4,
},
// 配置节点的文本样式
labelCfg: {
style: {
fill: "#333",
fontSize: 14,
},
},
},
defaultEdge: {
// 配置边的相关内容
// 线条样式
type: "cubic-horizontal",
style: {
stroke: "#999",
lineWidth: 2,
// 设置箭头样式,我这里不设置结尾箭头
endArrow: false,
},
},
layout: {
// 配置布局相关内容,部分配置项树形UI特有的,需要在此进行配置
type: "compactBox",
direction: "TB", // 树方向:Top-Bottom
// 在特定的树下的API,为高、宽、水平距离、垂直距离
getHeight: () => 40,
getWidth: () => 120,
getVGap: () => 40,
getHGap: () => 100,
},
});
三. 绑定交互事件
// 绑定交互事件
// 节点点击事件
tree.on("node:click", (e) => {
const node = e.item;
console.log("节点点击:", node.getModel().label);
// 添加点击效果
node.update({
style: {
fill: "#e6f7ff",
stroke: "#1890ff",
},
});
});
// 边点击事件
tree.on("edge:click", (e) => {
const edge = e.item;
console.log(
"连线点击:",
edge.getSource().getModel().label,
"->",
edge.getTarget().getModel().label
);
// 添加点击效果
edge.update({
style: {
stroke: "#ff4d4f",
lineWidth: 3,
},
});
});
四. 加载数据并渲染
数据格式:
const treeData = {
id: "root",
label: "CEO",
children: [
{
id: "tech",
label: "CTO",
type: "modelRect",
description: "技术负责人",
children: [
{ id: "frontend", label: "Frontend" },
{ id: "backend", label: "Backend" },
],
},
{
id: "finance",
label: "CFO",
children: [
{ id: "accounting", label: "Accounting" },
{ id: "investment", label: "Investment" },
],
},
],
};
注意:在节点里面也是可以进行配置对应的属性的,比如:label、type、description等,这样在渲染的时候就会显示出来。
渲染
// 加载数据
tree.data(treeData);
// 渲染树图
tree.render();
五. 总结
以上就是实现树形图的基本步骤,如果需要更详细的配置,可以开头所贴的对应的API。