交互模式 Mode
本章以添加节点及在两个节点之间连线为例进行介绍 G6 中的交互。在阅读本章之前,需要先熟悉以下内容:
- 自定义交互行为 Behavior;
- 交互模式 Mode。 上图是本文要实现的最终效果。完整 demo 代码参见:动态添加元素。方下拉菜单中有三个选项,用于切换交互模式 mode:
- 选择“默认”按钮时,切换到 default 交互模式:拖拽节点时节点跟随鼠标移动;点击节点时选中该节点;
- 选择“添加节点”按钮时,切换到 addNode 交互模式:点击空白区域在点击处增加一个节点;点击节点时选中该节点;
- 选择“添加边”按钮时,切换到 addEdge 交互模式:依次点击两个节点将会在这两个节点之间添加一条边。
使用多个 mode 的原因 相同的鼠标操作,在不同场景下有不同的含义。例如:
- 点击空白画布取消目前图上所有节点的选中状态、点击空白画布在响应位置添加节点,这两种需求都对应了用户点击画布空白处的操作;
- 点击选中、点击两个节点添加边都涉及到了鼠标在节点上的点击操作。
为了区分这些操作的含义,我们使用交互模式 mode 划分不同的场景。
前提代码
下面 HTML 代码是本文的基础代码,后续功能将在这份代码中增量添加。下面代码定义了左上方的下拉菜单,以及后面将会用到图上的初始数据 data
。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Interactively Add</title>
</head>
<body>
<!-- 左上方的下拉菜单 -->
<select id="selector">
<option value="default">默认</option>
<option value="addNode">添加节点</option>
<option value="addEdge">添加边</option>
</select>
<div id="mountNode"></div>
<script src="https://gw.alipayobjects.com/os/antv/pkg/_antv.g6-3.1.0/build/g6.js"></script>
<script>
// 初始数据
const data = {
nodes: [{
id: 'node1',
x: 100,
y: 200
},{
id: 'node2',
x: 300,
y: 200
},{
id: 'node3',
x: 300,
y: 300
}],
edges: [{
id: 'edge1',
target: 'node2',
source: 'node1'
}]
};
</script>
</body>
</html>
配置交互模式
下面代码实例化了图,并配置了交互模式的集合 modes
,其中包括 default
默认交互模式、addNode
增加节点交互模式、addEdge
增加边交互模式。每种交互模式中都包含了各自的交互行为,其中 'drag-node'
(拖拽节点) 和 'click-select'
(点击选中) 是 G6 内置的交互行为,'click-add-node'
(点击空白画布添加节点) 和 'click-add-edge'
(点击两个节点添加边) 需要我们在后面进行自定义。
// const data = ...
const graph = new G6.Graph({
container: 'mountNode',
width: 500,
height: 500,
// 交互模式集合
modes: {
// 默认交互模式
default: ['drag-node', 'click-select'],
// 增加节点交互模式
addNode: ['click-add-node', 'click-select'],
// 增加边交互模式
addEdge: ['click-add-edge', 'click-select'],
},
});
graph.data(data);
graph.render();
// 监听左上角下拉菜单的变化,根据其变化切换图的交互模式
document.getElementById('selector').addEventListener('change', e => {
const value = e.target.value;
// 切换交互模式
graph.setMode(value);
});
添加节点
在上面的实例中,当选中添加节点按钮时,会切换到添加节点的 Mode 上。实现在点击空白画布时,在点击位置添加节点的方式是通过 G6.registerBehavior
自定义名为 'click-add-node'
(名字可以自由设定) 的 Behavior 实现的 。
// 封装点击添加边的交互
G6.registerBehavior('click-add-node', {
// 设定该自定义行为需要监听的事件及其响应函数
getEvents() {
// 监听的事件为 cnavas:click,响应函数时 onClick
return {
'canvas:click': 'onClick',
};
},
// 点击事件
onClick(ev) {
const graph = this.graph;
// 在图上新增一个节点
const node = graph.addItem('node', {
x: ev.x,
y: ev.y,
id: G6.Util.uniqueId(), // 生成唯一的 id
});
},
});
添加边
在上面的案例中,当需要在两个节点之间连线时,要先切换到添加边的 Mode 上。下面代码自定义了名为 'click-add-edge'
(名字可以自由设定)的 Behavior 实现两个节点之间连线。
// 封装点击添加边的交互
G6.registerBehavior('click-add-edge', {
// 设定该自定义行为需要监听的事件及其响应函数
getEvents() {
return {
'node:click': 'onClick', // 监听事件 node:click,响应函数时 onClick
mousemove: 'onMousemove', // 监听事件 mousemove,响应函数时 onMousemove
'edge:click': 'onEdgeClick', // 监听事件 edge:click,响应函数时 onEdgeClick
};
},
// getEvents 中定义的 'node:click' 的响应函数
onClick(ev) {
const node = ev.item;
const graph = this.graph;
// 鼠标当前点击的节点的位置
const point = { x: ev.x, y: ev.y };
const model = node.getModel();
if (this.addingEdge && this.edge) {
graph.updateItem(this.edge, {
target: model.id,
});
this.edge = null;
this.addingEdge = false;
} else {
// 在图上新增一条边,结束点是鼠标当前点击的节点的位置
this.edge = graph.addItem('edge', {
source: model.id,
target: point,
});
this.addingEdge = true;
}
},
// getEvents 中定义的 mousemove 的响应函数
onMousemove(ev) {
// 鼠标的当前位置
const point = { x: ev.x, y: ev.y };
if (this.addingEdge && this.edge) {
// 更新边的结束点位置为当前鼠标位置
this.graph.updateItem(this.edge, {
target: point,
});
}
},
// getEvents 中定义的 'edge:click' 的响应函数
onEdgeClick(ev) {
const currentEdge = ev.item;
// 拖拽过程中,点击会点击到新增的边上
if (this.addingEdge && this.edge == currentEdge) {
graph.removeItem(this.edge);
this.edge = null;
this.addingEdge = false;
}
},
});
完整代码
完整 demo 代码参见:动态添加元素。