G6 封装了一系列交互方法,方便用户直接使用。本文将为 Tutorial 案例 增加简单的交互:hover 节点、点击节点、点击边、放缩画布、拖拽画布。本节目标效果如下:
G6 中的交互行为。G6 内置了一系列交互行为,用户可以直接使用。简单地理解,就是可以一键开启这些交互行为:
Mode 是 G6 交互行为的管理机制,一个 mode 是多种行为 Behavior 的组合,允许用户通过切换不同的模式进行交互行为的管理。
状态 State 是 G6 中的状态机制。用户可以为图中的元素(节点/边)设置不同的状态及不同状态下的样式。在状态发生变化时,G6 自动更新元素的样式。例如,可以为节点设置状态 ‘click’ 为 true 或 false,并为节点设置 ‘click’ 的样式为加粗节点边框。当 ‘click’ 状态被切换为 true 时,节点的边框将会被加粗,‘click’ 状态被切换为 false 时,节点的样式恢复到默认。
在 G6 中使用内置 Behavior 的方式非常简单,只需要在图实例化时配置 modes。
拖拽和缩放属于 G6 内置交互行为,修改代码如下:
const graph = new G6.Graph({
// ... // 其他配置项
modes: {
default: ['drag-canvas', 'zoom-canvas', 'drag-node'], // 允许拖拽画布、放缩画布、拖拽节点
},
});
除了直接使用内置交互名称外,也可以为 Behavior 配置参数,例如放缩画布的敏感度、最大/最小放缩程度等。
上面代码中的 modes 定义了 G6 的模式,default 是默认的模式,还可以允许有其他的模式,比如:编辑模式 edit 等。不同的模式,用户能进行的行为可以不同,比如默认模式能拖拽画布,编辑模式不允许拖拽画布:
// 举例解释不同模式
modes: {
default: ['drag-canvas'],
edit: []
}
有时我们希望通过交互可以将元素样式变成特定样式,如我们看到的图 1 中,鼠标 hover 节点、点击节点、点击边时,样式发生了变化。这里涉及到了 G6 中 状态 State 的概念。简单地说,是否 hover 、click 、任何操作(可以是自己起的状态名),都可以称为一种状态(state)。用户可以自由设置不同状态下的元素样式。要达到交互更改元素样式,需要两步:
在实例化图时,通过 nodeStateStyles 和 edgeStateStyles 两个配置项可以配置元素在不同状态下的样式。
为达到 Tutorial 案例 中的效果:
下面代码设置了节点分别在 hover 和 click 状态为 true 时的样式,边在 click 状态为 true 时的样式:
const graph = new G6.Graph({
// ... // 其他配置项
// 节点不同状态下的样式集合
nodeStateStyles: {
// 鼠标 hover 上节点,即 hover 状态为 true 时的样式
hover: {
fill: 'lightsteelblue',
},
// 鼠标点击节点,即 click 状态为 true 时的样式
click: {
stroke: '#000',
lineWidth: 3,
},
},
// 边不同状态下的样式集合
edgeStateStyles: {
// 鼠标点击边,即 click 状态为 true 时的样式
click: {
stroke: 'steelblue',
},
},
});
G6 中所有元素监听都挂载在图实例上,如下代码中的 graph 对象是 G6.Graph 的实例,graph.on() 函数监听了某元素类型(node / edge)的某种事件。
// 在图实例 graph 上监听
graph.on('元素类型:事件名', e => {
// do something
});
现在,我们通过下面代码,为 Tutorial 案例 增加点和边上的监听事件,并在监听函数里使用 graph.setItemState() 改变元素的状态:
// 鼠标进入节点
graph.on('node:mouseenter', e => {
const nodeItem = e.item; // 获取鼠标进入的节点元素对象
graph.setItemState(nodeItem, 'hover', true); // 设置当前节点的 hover 状态为 true
});
// 鼠标离开节点
graph.on('node:mouseleave', e => {
const nodeItem = e.item; // 获取鼠标离开的节点元素对象
graph.setItemState(nodeItem, 'hover', false); // 设置当前节点的 hover 状态为 false
});
// 点击节点
graph.on('node:click', e => {
// 先将所有当前是 click 状态的节点置为非 click 状态
const clickNodes = graph.findAllByState('node', 'click');
clickNodes.forEach(cn => {
graph.setItemState(cn, 'click', false);
});
const nodeItem = e.item; // 获取被点击的节点元素对象
graph.setItemState(nodeItem, 'click', true); // 设置当前节点的 click 状态为 true
});
// 点击边
graph.on('edge:click', e => {
// 先将所有当前是 click 状态的边置为非 click 状态
const clickEdges = graph.findAllByState('edge', 'click');
clickEdges.forEach(ce => {
graph.setItemState(ce, 'click', false);
});
const edgeItem = e.item; // 获取被点击的边元素对象
graph.setItemState(edgeItem, 'click', true); // 设置当前边的 click 状态为 true
});
至此,完整代码如下:
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Tutorial Demotitle>
head>
<body>
<div id="mountNode">div>
<script src="https://gw.alipayobjects.com/os/antv/pkg/_antv.g6-3.1.0/build/g6.js">script>
<script>
const graph = new G6.Graph({
container: 'mountNode',
width: 800,
height: 600,
// 节点默认配置
defaultNode: {
labelCfg: {
style: {
fill: '#fff',
},
},
},
// 边默认配置
defaultEdge: {
labelCfg: {
autoRotate: true,
},
},
// 节点在各状态下的样式
nodeStateStyles: {
// hover 状态为 true 时的样式
hover: {
fill: 'lightsteelblue',
},
// click 状态为 true 时的样式
click: {
stroke: '#000',
lineWidth: 3,
},
},
// 边在各状态下的样式
edgeStateStyles: {
// click 状态为 true 时的样式
click: {
stroke: 'steelblue',
},
},
// 布局
layout: {
type: 'force',
linkDistance: 200,
preventOverlap: true,
nodeStrength: -30,
edgeStrength: 0.1,
},
// 内置交互
modes: {
default: ['drag-canvas', 'zoom-canvas', 'drag-node'],
},
});
const main = async () => {
const response = await fetch(
'https://gw.alipayobjects.com/os/basement_prod/6cae02ab-4c29-44b2-b1fd-4005688febcb.json',
);
const remoteData = await response.json();
const nodes = remoteData.nodes;
const edges = remoteData.edges;
nodes.forEach(node => {
if (!node.style) {
node.style = {};
}
node.style.lineWidth = 1;
node.style.stroke = '#666';
node.style.fill = 'steelblue';
switch (node.class) {
case 'c0': {
node.shape = 'circle';
node.size = 30;
break;
}
case 'c1': {
node.shape = 'rect';
node.size = [35, 20];
break;
}
case 'c2': {
node.shape = 'ellipse';
node.size = [35, 20];
break;
}
}
});
edges.forEach(edge => {
if (!edge.style) {
edge.style = {};
}
edge.style.lineWidth = edge.weight;
edge.style.opacity = 0.6;
edge.style.stroke = 'grey';
});
graph.data(remoteData);
graph.render();
// 监听鼠标进入节点
graph.on('node:mouseenter', e => {
const nodeItem = e.item;
// 设置目标节点的 hover 状态 为 true
graph.setItemState(nodeItem, 'hover', true);
});
// 监听鼠标离开节点
graph.on('node:mouseleave', e => {
const nodeItem = e.item;
// 设置目标节点的 hover 状态 false
graph.setItemState(nodeItem, 'hover', false);
});
// 监听鼠标点击节点
graph.on('node:click', e => {
// 先将所有当前有 click 状态的节点的 click 状态置为 false
const clickNodes = graph.findAllByState('node', 'click');
clickNodes.forEach(cn => {
graph.setItemState(cn, 'click', false);
});
const nodeItem = e.item;
// 设置目标节点的 click 状态 为 true
graph.setItemState(nodeItem, 'click', true);
});
// 监听鼠标点击节点
graph.on('edge:click', e => {
// 先将所有当前有 click 状态的边的 click 状态置为 false
const clickEdges = graph.findAllByState('edge', 'click');
clickEdges.forEach(ce => {
graph.setItemState(ce, 'click', false);
});
const edgeItem = e.item;
// 设置目标边的 click 状态 为 true
graph.setItemState(edgeItem, 'click', true);
});
};
main();
script>
body>
html>