为辅助用户在图上探索,G6 提供了一些辅助工具,其中一部分是插件工具,另一部分是交互工具。
本文将为 图 添加缩略图插件 、网格插件 、节点提示框 、边提示框。
插件
使用插件时,有三个步骤:
- Step 1: 引入插件;
- Step 2: 实例化插件;
- Step 3: 在实例化图时将插件的实例配置到图上。
Minimap
缩略图 (Minimap) 是一种常见的用于快速预览和探索图的工具,可作为导航辅助用户探索大规模图。
现在,我们配置一个 Minimap:
目标效果:
使用方式:
js
// 实例化 minimap 插件
const minimap = new G6.Minimap({
size: [100, 100],// Minimap 的大小
className: 'minimap',
type: 'delegate',// 只渲染图上元素的大致图形
});
// 实例化图
const graph = new G6.Graph({
// ... // 其他配置项
plugins: [minimap], // 将 minimap 实例配置到图上
});
Grid 网格
网格可用于辅助用户在拖拽节点时对齐到网格。
目标效果:
使用方式:
js
// const minimap = ...
// 实例化 grid 插件
const grid = new G6.Grid();
// 实例化图
const graph = new G6.Graph({
// ... // 其他配置项
plugins: [minimap, grid], // 将 grid 实例配置到图上
});
交互工具
交互工具是指配置到图上交互模式中的工具。使用交互工具时,有两个步骤:
- Step 1: 在实例化图时配置 modes;
- Step 2: 为交互工具定义样式。
tooltip 节点提示框
节点提示框可以用在节点的详细信息的展示。当鼠标滑过节点时,显示一个浮层告知节点的详细信息。
目标效果:
使用方式:
实例化图时配置 'tooltip' 到 modes 中:
js
const graph = new G6.Graph({
modes: {
default: [
// ...
{
type: 'tooltip', // 提示框
formatText(model) { // 提示框文本内容
const text = 'label: ' + model.label + '<br/> class: ' + model.class; return text;
},
},
],
},
});
由于 tooltip 实际上是一个悬浮的 <div>
标签,因此可在 CSS 中设置样式。下面展示在 CSS 中设置样式:
css
/* 提示框的样式 */
.g6-tooltip {
border: 1px solid #e2e2e2;
border-radius: 4px;
font-size: 12px;
color: #545454;
background-color: rgba(255, 255, 255, 0.9);
padding: 10px 8px;
box-shadow: rgb(174, 174, 174) 0px 0px 10px;
}
edge-tooltip 边提示框
边提示框可以用在边的详细信息的展示。当鼠标滑过边时,显示一个浮层告知边的详细信息。
目标效果:
使用方式:
js
const graph = new G6.Graph({
modes: {
default: [
// ...
{
type: 'tooltip', // 节点提示框
// ...
},
{
type: 'edge-tooltip', // 边提示框
formatText(model) {
// 边提示框文本内容
const text =
'source: ' +
model.source +
'<br/> target: ' +
model.target +
'<br/> weight: ' +
model.weight;
return text;
},
},
],
},
});
与 tooltip 相同,edge-tooltip 是一个悬浮的 <div>
标签,可以使用与 tooltip 相同的方法设置其悬浮框的样式。
效果代码
css
.plugin-container .g6-tooltip {
border: 1px solid #e2e2e2;
border-radius: 4px;
font-size: 12px;
color: #545454;
background-color: rgba(255, 255, 255, 0.9);
padding: 10px 8px;
box-shadow: rgb(174, 174, 174) 0px 0px 10px;
}
js
import { useRef, useEffect } from "react";
import G6 from "@antv/g6";
import "./index.css";
const PluginAndUtils = () => {
const containerRef = useRef(null);
const graphRef = useRef();
useEffect(() => {
if (graphRef.current || !containerRef.current) return;
// 实例化 minimap 插件
const minimap = new G6.Minimap({
size: [100, 100], // Minimap 的大小
className: "minimap",
type: "delegate", // 只渲染图上元素的大致图形
});
// 实例化 grid 插件
const grid = new G6.Grid();
const graph = new G6.Graph({
container: containerRef.current, // String | HTMLElement,必须,在 Step 1 中创建的容器 id 或容器本身
width: 800, // Number,必须,图的宽度
height: 600, // Number,必须,图的高度
// 布局配置项
layout: {
// Object,可选,布局的方法及其配置项,默认为 random 布局。
type: "force", // 指定为力导向布局
preventOverlap: true, // 防止节点重叠
// nodeSize: 40 // 节点大小,用于算法中防止节点重叠时的碰撞检测。由于已经在上一节的元素配置中设置了每个节点的 size 属性,则不需要在此设置 nodeSize。
linkDistance: 150, // 指定边距离为150
},
// 设置画布的交互模式
modes: {
default: [
"drag-canvas",
"zoom-canvas",
"drag-node",
{
type: "tooltip", // 提示框
formatText(model) {
// 提示框文本内容
const text =
"label: " +
model.label +
"<br/> class: " +
model.class;
return text;
},
},
{
type: "edge-tooltip", // 边提示框
formatText(model) {
// 边提示框文本内容
const text =
"source: " +
model.source +
"<br/> target: " +
model.target +
"<br/> weight: " +
model.weight;
return text;
},
},
], // 允许拖拽画布、放缩画布、拖拽节点
},
plugins: [minimap, grid], // 将 minimap 实例配置到图上
// 节点在默认状态下的样式配置(style)和其他配置
defaultNode: {
size: 40, //节点的大小
// 节点样式配置
style: {
fill: "steelblue", // 节点填充色
stroke: "#666", // 节点描边色
lineWidth: 1, // 节点描边粗细
},
// 节点上的标签文本配置
labelCfg: {
// 节点上的标签文本样式配置
style: {
fill: "#fff", // 节点标签文字颜色
},
},
},
// 边在默认状态下的样式配置(style)和其他配置
defaultEdge: {
// 边上的标签文本配置
labelCfg: {
autoRotate: true, // 边上的标签文本根据边的方向旋转
},
},
// 节点不同状态下的样式集合
nodeStateStyles: {
// 鼠标 hover 上节点,即 hover 状态为 true 时的样式
hover: {
fill: "lightsteelblue",
},
// 鼠标点击节点,即 click 状态为 true 时的样式
click: {
stroke: "#000",
lineWidth: 3,
},
},
// 边不同状态下的样式集合
edgeStateStyles: {
// 鼠标点击边,即 click 状态为 true 时的样式
click: {
stroke: "steelblue",
},
},
});
onLoadData((data) => {
graph.data(data); // 绑定数据
graph.render(); // 渲染图
graphRef.current = graph;
// 鼠标进入节点
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
});
});
}, []);
const onLoadData = async (callback) => {
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;
nodes.forEach((node) => {
if (!node.style) {
node.style = {};
}
// 根据节点数据中的 class 属性配置图形
switch (node.class) {
case "c0": {
node.type = "circle"; // class = 'c0' 时节点图形为 circle
break;
}
case "c1": {
node.type = "rect"; // class = 'c1' 时节点图形为 rect
node.size = [35, 20]; // class = 'c1' 时节点大小
break;
}
case "c2": {
node.type = "ellipse"; // class = 'c2' 时节点图形为 ellipse
node.size = [35, 20]; // class = 'c2' 时节点大小
break;
}
}
});
const edges = remoteData.edges;
edges.forEach((edge) => {
if (!edge.style) {
edge.style = {};
}
edge.style.lineWidth = edge.weight; // 边的粗细映射边数据中的 weight 属性数值
// 移到此处
edge.style.opacity = 0.6;
edge.style.stroke = "grey";
});
callback(remoteData);
};
return (
<div
className="plugin-container"
ref={containerRef}
style={{ border: "2px solid #000", display: "inline-block" }}
></div>
);
};
export default PluginAndUtils;