Vue项目中使用AntV G6绘制自适应图谱

news2024/11/13 10:22:50

Vue项目中使用AntV G6绘制自适应图谱

一、需求

  • 需求1:Vue3.x项目下使用AntV G6绘制图谱

  • 需求2:图谱节点为两个IP地址,节点间存在多条连线情况

  • 需求3:鼠标悬浮到节点上方时,高亮当前节点并出现tooltip气泡提示,展示相关信息

  • 需求4:点击图谱连线后需要高亮关联节点和当前连线

  • 需求5:图谱大小需要保证自适应(这里可以是根据窗口自适应,也可以是可调整大小的弹窗DOM内的自适应,后续会分开实现)

二、实现

1.初始Vue3.x的项目可以看之前的博客:通过vite搭建Vue3.x项目,初始化项目后需要安装AntV G6的依赖,执行如下命令:

npm install --save @antv/g6

2.安装G6后在需要使用的页面进行导入,在创建好容器后利用G6进行实例化创建一个画布,后续的图谱绘制都将在画布内进行,目前为止需求1部分已经完成了,代码如下:

<template>
  <div
    class="home"
    id="container"
  >
  </div>
</template>

<script setup>
import G6 from '@antv/g6';
import { onMounted, ref } from '@vue/runtime-core';
// 图谱实例化对象
const graph = ref(null);
/* 挂载钩子 */
onMounted(() => {
  initGraph();
});
/* 初始化图谱 */
const initGraph = () => {
  graph.value = new G6.Graph({
    container: 'container', // String | HTMLElement,必须,容器 id 或容器本身
    width: document.getElementById('container').clientWidth, // Number,必须,图的宽度
    height: document.getElementById('container').clientHeight, // Number,必须,图的高度
  });
}
</script>

<style lang="less" scoped>
.home {
  width: 100%;
  height: 100%;
  position: relative;
}
</style>

3.为了方便后续步骤的进行,这里先造一些节点和连线的数据,大致仿照 G6官网示例中的数据,根据需求2在模拟数据的边集中添加两个节点存在多条连线的情况(实际场景下根据实际数据做处理即可),如下:

<script setup>
...
// 模拟数据
const data = {
  // 点集
  nodes: [
    {
      id: '10.0.0.1', // String,该节点存在则必须,节点的唯一标识
      label: '节点1',
    },
    {
      id: '192.168.1.1',
      label: '节点2',
    },
    {
      id: '192.168.1.2',
      label: '节点3',
    },
    {
      id: '10.0.0.2',
      label: '节点4',
    },
  ],
  // 边集
  edges: [
    {
      source: '10.0.0.1', // String,必须,起始点 id
      target: '192.168.1.1', // String,必须,目标点 id
      label: '节点1->节点2',
    },
    {
      source: '10.0.0.1',
      target: '192.168.1.1',
      label: '节点1->节点2',
    },
    {
      source: '192.168.1.1',
      target: '10.0.0.1',
      label: '节点2->节点1',
    },
    {
      source: '192.168.1.1',
      target: '192.168.1.2',
      label: '节点2->节点3',
    },
    {
      source: '192.168.1.2',
      target: '192.168.1.2',
      label: '节点3->节点3',
    },
    {
      source: '192.168.1.2',
      target: '10.0.0.2',
      label: '节点3->节点4',
    },
    {
      source: '10.0.0.2',
      target: '192.168.1.2',
      label: '节点4->节点3',
    },
    {
      source: '10.0.0.2',
      target: '10.0.0.1',
      label: '节点4->节点1',
    },
  ],
};
...
</script>

4.创建好模拟数据后继续完善画布的配置,这时可以参考官网的Api对画布、节点样式、连线样式、气泡提示tooltip进行一些配置

  • 这里需要注意的是,拖动画布或缩放画布时有概率出现**残影(拖影)**的问题,需要单独处理
  • 利用G6的工具函数将连线类型设置为贝塞尔曲线就能保证两个节点之间多条连线不会出现重叠的问题,这样就实现了需求2的部分
  • 需求3则利用G6实例化一个Tooltip后,再利用plugins字段配置到到画布内,当鼠标移入节点时会出现一个tooltip显示节点的IP地址(id),但是移出节点后tooltip并不会消失,需要在移出节点的事件中单独处理一次
  • 需求4可以给节点添加一个点击事件,根据点击的节点信息找到关联的两个节点的IP地址(id)并进行高亮处理
  • 代码如下:
<script setup>
...
/* 初始化图谱 */
const initGraph = () => {
  // 节点添加提示框
  const tooltip = new G6.Tooltip({
    offsetX: 10, // tooltip 的 x 方向偏移值,需要考虑父级容器的 padding
    offsetY: 20, // tooltip 的 y 方向偏移值,需要考虑父级容器的 padding
    getContent(e) {
      const outDiv = document.createElement('div');
      outDiv.style.width = '180px';
      outDiv.innerHTML = `
          <ul>
            <li>节点IP:${e.item.getModel().id}</li>
          </ul>`;
      return outDiv;
    },
    itemTypes: ['node'] // 给node添加tooltip
  });
  graph.value = new G6.Graph({
    container: 'container', // String | HTMLElement,必须,容器 id 或容器本身
    width: document.getElementById('container').clientWidth, // Number,必须,图的宽度
    height: document.getElementById('container').clientHeight, // Number,必须,图的高度
    fitCenter: true, // 是否平移图使其中心对齐到画布中心
    plugins: [tooltip], // 添加tooltip
    // 画布配置
    modes: {
      default: ['drag-canvas', 'zoom-canvas', 'drag-node'], // 允许拖拽画布、放缩画布、拖拽节点
    },
    // 基本配置
    layout: {
      type: 'force', // 指定为力导向布局
      preventOverlap: true, // 防止节点重叠
      linkDistance: 180, // 指定边距离为150
      nodeSpacing: 85, // 防止重叠时节点边缘间距的最小值
      alpha: 1, // 当前的迭代收敛阈值
      alphaDecay: 0.05, // 迭代阈值的衰减率。范围 [0, 1]
    },
    // 默认节点配置
    defaultNode: {
      type: 'rect', // 节点类型
      size: [120, 46],
      style: {
        fill: '#F6FCFE', // 填充色
        stroke: '#2EA1FF', // 节点描边颜色
        shadowColor: 'rgba(78,89,105,0.3)', // 阴影颜色
        lineWidth: 1, // 描边宽度
        radius: 3, // 圆角
        shadowBlur: 2, // 阴影大小
      },
      // 文本配置
      labelCfg: {
        position: 'center',
        style: {
          fontSize: 14,
          fill: '#0282FF',
          fontWeight: 400,
        },
      },
    },
    // 默认边配置
    defaultEdge: {
      type: 'loop', // 设置边默认为“自环”类型
      style: {
        stroke: '#2EA1FF',
        lineWidth: 1,
        // 边添加箭头
        endArrow: {
          path: G6.Arrow.triangle(4, 4, 2),
          d: 2,
          fill: '#2EA1FF',
        }
      },
      labelCfg: {
        autoRotate: true,
        refY: 12,
        style: {
          fontSize: 12,
          fill: '#1D2129',
        }
      },
    },
    // 边状态样式
    edgeStateStyles: {
      selected: {
        stroke: '#0282FF',
        shadowBlur: 0,
        'text-shape': {
          fill: "#0282FF",
          fontWeight: 600,
        }
      }
    },
    // 节点状态样式
    nodeStateStyles: {
      // 选中后样式
      selected: {
        fill: '#0282FF', // 填充色
        stroke: '#0282FF', // 节点描边颜色
        lineWidth: 1, // 描边宽度
        shadowColor: 'rgba(0,102,210,0.5)',
        'text-shape': {
          fill: "#ffffff"
        }
      },
      // 悬浮后样式
      active: {
        fill: '#CDEEFF', // 填充色
        stroke: '#2EA1FF', // 节点描边颜色
        lineWidth: 1, // 描边宽度
        shadowColor: 'rgba(78,89,105,0.3)',
        'text-shape': {
          fill: "#0282FF",
          fontWeight: 500,
        }
      }
    }
  });
  // 添加节点间多条连线,贝塞尔曲线
  G6.Util.processParallelEdges(data.edges, 65);
  // 解决拖动产生残影问题
  graph.value.get('canvas').set('localRefresh', false);
  // 边添加点击事件
  graph.value.on('edge:click', (evt) => {
    const { item } = evt;
    graph.value.getEdges().forEach((edge) => {
      graph.value.clearItemStates(edge);
    });
    graph.value.getNodes().forEach((node) => {
      graph.value.clearItemStates(node);
    });
    graph.value.setItemState(item, 'selected', true);
    // 高亮关联节点
    graph.value.setItemState(item['_cfg'].source, 'selected', true);
    graph.value.setItemState(item['_cfg'].target, 'selected', true);
  });
  // 节点悬浮高亮
  graph.value.on('node:mouseover', (e) => {
    graph.value.setItemState(e.item, 'active', true);
  });
  // 节点鼠标移出后使tooltip消失并取消节点悬浮高亮
  graph.value.on('node:mouseout', (e) => {
    document.getElementsByClassName('g6-component-tooltip')[0].style.display = 'none';
    graph.value.setItemState(e.item, 'active', false);
  });
  graph.value.data(data);
  graph.value.render();
}
</script>

<style lang="less" scoped>
.home {
  width: 100%;
  height: 100%;
  position: relative;
}
</style>

<style lang="less">
// g6的tooltip气泡提示样式需要写进不含scoped的style中,否则不生效
.g6-component-tooltip {
  background-color: rgba(0, 0, 0, 0.7);
  padding: 7px 17px;
  box-shadow: none;
}
.g6-component-tooltip ul li {
  font-size: 12px;
  font-weight: 400;
  color: #ffffff;
  line-height: 19px;
}
</style>

5.需求5要分成两种情况来说

  • 第一种情况是窗口大小改变时影响到画布,可以直接通过监听窗口windowresize来对画布进行自适应的操作,代码如下:
<script setup>
...
  	graph.value.render();
    // 监听window的resize事件
  	window.addEventListener('resize', () => {
    // 利用changeSize()事件使画布自适应
    graph.value.changeSize(document.getElementById('container').clientWidth, document.getElementById('container').clientHeight);
    // 将图谱移动到画布正中间
    graph.value.fitCenter();
...
</script>
  • 第二种情况就稍微复杂一些,笔者实际中的业务是在一个可以拖动改变大小的弹窗中实现画布自适应;同理这里可以是收起导航菜单影响到画布的DOM亦或是改变布局等方式影响了画布的DOM大小发生改变,总之就是画布所在的DOM元素大小发生改变时的自适应,因为不是window的大小改变,所以不能使用第一种监听windowresize方式来处理,笔者这里使用的是一个jselement-resize-detector用来监听画布的DOM大小是否改变,步骤如下:

    • 安装element-resize-detector库,详情:https://www.npmjs.com/package/element-resize-detector
    npm install --save element-resize-detector
    
    • 使用element-resize-detector监听目标DOM大小是否发生改变
    <script setup>
    // 引入
    import elementResizeDetectorMaker from 'element-resize-detector';
    ...
    	graph.value.render();
     	 // 使用
      	let erd = elementResizeDetectorMaker();
      	// 监听目标DOM的大小是否改变,以container为例
      	erd.listenTo(document.getElementById("container"), (element) => {
            let width = element.clientWidth;
            let height = element.clientHeight;
            // 利用changeSize()事件使画布自适应
            graph.value.changeSize(width, height);
            // 将图谱移动到画布正中间
            graph.value.fitCenter();
      	});
    ...
    </script>
    

6.最后在界面卸载时需要释放一次资源,将画布资源进行释放,代码如下:

<script setup>
...
/* 卸载钩子 */
onUnmounted(() => {
  if (graph.value) {
    // 清空数据(可省略)
    graph.value.clear();
    // 销毁画布
    graph.value.destroy();
    // 将画布实例对象置空
    graph.value = null;
  }
});
...
</script>

7.最终效果如下图,方便学习代码demo会放到Gitee上,笔者根据需求5拆分出了两个示例,方便针对不同场景做不同的处理,仓库地址:g6_learning: Vue3+Elemet Plus+G6绘制自适应图谱demo (gitee.com),CSDN资源地址:g6_learning: Vue3+Elemet Plus+G6绘制自适应图谱demo(csdn)
在这里插入图片描述
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/62079.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Blackmagic黑魔法摄像机braw视频文件修复方法

Blackmagic是全球知名的影视级产品供应商&#xff0c;其高清摄像机是国内外各种剧组的最爱。Blackmagic的新产品目前使用braw格式&#xff0c;其编码采用自定义的raw编码&#xff0c;视频的效果和阿莱不相上下。近期我们处理了一例braw损坏无法播放的问题&#xff0c;我们来看看…

网络编程入门

什 么 是 网 络 编 程 网络编程的本质是多台计算机之间的数据交换。 数据传递本身没有多大的难度&#xff0c;不就是把一个设备中的数据发送给其他设备&#xff0c;然后接受另外一个设备反馈的数据。 现在的网络编程基本上都是基于请求/响应方式的&#xff0c;也就是一个设备发…

[附源码]计算机毕业设计基于springboot的小区宠物管理系统

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

css样式引入方式及优缺点

这篇文章主要介绍了css样式引入及优缺点&#xff0c;本文给大家分享三种css的引入方式&#xff0c;通过代码给大家介绍的非常详细&#xff0c;对大家的学习或工作具有一定的参考借鉴价值&#xff0c;需要的朋友参考下吧 三种css的引入方式 1.行内样式 优点&#xff1a;书写方…

【Python自然语言处理】文本向量化处理用户对不同类型服装评论问题(超详细 附源码)

需要源码和数据集请点赞关注收藏后评论区留言私信~~~ 下面以文本向量化为目标&#xff0c;举例说明基于不同模型的实现过程&#xff0c;使用的数据集的主题是用户对不同类型的女性服装的评论&#xff0c;总共有23485条记录 实现步骤如下 一、导入库文件 首先导入需要的库文件…

MySQL数据库日志、备份与恢复

目录 前言 一.MySQL 日志管理 数据的重要性 造成数据丢失的原因 1、错误日志 2、通用查询日志 3、二进制日志 4、慢查询日志 5、查看日志 6.中继日志&#xff08;relay log&#xff09; 7、普通日志&#xff08;general log&#xff09; 配置文件 二、数据库备份的…

Metabase学习教程:系统管理-4

序列化&#xff1a;在Metabase实例间迁移 如何使用Metabase的序列化功能将问题、仪表板、集合、设置等从一个Metabase实例复制到新的Metabase实例。 Metabase序列化 序列化仅在商业版上可用&#xff08;仅在自托管计划上&#xff09;。 许多客户在迁移到本地部署的商业版时…

二叉树OJ题详解

第一题&#xff1a;单值二叉树 力扣链接&#xff1a;力扣 单值二叉树就是每一个节点存放的数据都相同&#xff0c;那么如何判断一棵树为单值二叉树呢&#xff1f;我们就拿最简单的一棵树为例子&#xff0c;比如根节点为1它的左子树和右子树也为1的一棵树&#xff0c;我们只需要…

以太网 TCP协议(TCP报文交互后的状态机变化)

2.7.2 以太网 TCP协议&#xff08;TCP报文交互后的状态机变化&#xff09; 一、TCP状态机&#xff1a; 二、TCP状态机变化 1、TCP三次握手 客户端主动发起SYN置位TCP之后&#xff0c;状态变为SYN_SENT(请求发送状态)服务器默认处于LISTEN(监听状态)。收到SYN报文之后&#x…

VMwareWorkStation如何添加万兆网卡,万兆网卡添加教程

1.引言 不同于ESXi&#xff0c;在VMware WorkStation&#xff08;后文简称VMware&#xff09;中添加网卡后没有选择网卡速度等级的选项&#xff0c;例如百兆、千兆、万兆等。就算点开右下角的”高级“也不管用。不过按照VMware的默认设定&#xff0c;当新建虚拟机选择32位操作系…

【玩转STL】STL的简介和string类用法和接口讲解(源码解析)

接触编程时间长一点的朋友想必都多多少少听过vector、string、queue等容器&#xff0c;也大抵了解一些有关STL的概念&#xff0c;这一节&#xff0c;我们就一起来谈一谈STL的六大组件&#xff0c;再来一起深入理解string类。 这里写目录标题&#x1f34e;、什么是STL&#xff0…

人工智能:语音合成技术介绍

❤️作者主页&#xff1a;IT技术分享社区 ❤️作者简介&#xff1a;大家好,我是IT技术分享社区的博主&#xff0c;从事C#、Java开发九年&#xff0c;对数据库、C#、Java、前端、运维、电脑技巧等经验丰富。 ❤️个人荣誉&#xff1a; 数据库领域优质创作者&#x1f3c6;&#x…

PCL 点云采样

一、简介 点云采样一般有三种方式&#xff0c;上采样&#xff0c;下采样&#xff0c;平均采样 原理介绍 下采样&#xff1a; 一般是采样是通过构建一个三维体素的格栅&#xff0c;然后在每个体素内用体素的重心近似的作为这个体素的整体特征&#xff0c;也就是说用这一个体素…

python 拆分pdf(有可执行文件exe)

1.背景 被那些软件pdf拆分整气死了&#xff0c;今天用python写一份pdf拆分的代码。 2.代码&#xff1a;&#xff08;计算机的可以去学习一下&#xff0c;自己改改&#xff09; pdf_split.py from PyPDF2 import PdfFileReader, PdfFileWriter# PDF文件分割 def split_pdf():…

pytorch nn.utils.rnn.pack_padded_sequence 分析

pack_padded_sequence 在nlp模型的forward方法中&#xff0c;可能有以下调用令读者疑惑 packed_embedded nn.utils.rnn.pack_padded_sequence(embedded, text_lengths, batch_firstTrue, enforce_sortedFalse)为什么要使用pack_padded_sequence&#xff1f; 参考 Pytorch中…

TDengine3.0:解决高基数问题的时序数据库设计思路

小 T 导读&#xff1a; 数据集的高基数&#xff08;High-Cardinality&#xff09;问题一直困扰着诸多主流的时序数据库&#xff08;Time Series Database&#xff0c;TSDB&#xff09;产品。一些数据库管理系统&#xff0c;在基数较低时表现良好&#xff1b;但是随着基数的增加…

vue2.x与vue3.x中自定义指令详解

&#x1f431;个人主页&#xff1a;不叫猫先生 &#x1f64b;‍♂️作者简介&#xff1a;前端领域新星创作者、华为云享专家、阿里云专家博主&#xff0c;专注于前端各领域技术&#xff0c;共同学习共同进步&#xff0c;一起加油呀&#xff01; &#x1f4ab;系列专栏&#xff…

m基于GA遗传算法的分件供送螺杆参数优化matlab仿真,优化参数包括螺杆总尺寸-最大圈数等

目录 1.算法描述 2.仿真效果预览 3.MATLAB核心程序 4.完整MATLAB 1.算法描述 首先介绍MATLAB部分的遗传算法的优化算法介绍&#xff1a; 遗传算法的原理 遗传算法GA把问题的解表示成“染色体”&#xff0c;在算法中也即是以二进制编码的串。并且&#xff0c;在执行遗传算法…

AI 对话模型被网友玩坏了!这次还可以运行 Docker 容器...

最近一款新的聊天 AI 被网友们玩疯了。它可以直接生成代码、可以给你的代码 debug 以及提出优化...可以模仿莎士比亚风格写作...还可以解答各种各样的问题&#xff0c;而且显然不只 10 岁小孩子的智商&#xff0c;感觉它已经把互联网上所有的公开资料都吸收并消化了。这就是 Op…

mssql(1433端口)介绍

mssql介绍 Microsoft SQL Server是一个关系型数据库&#xff0c;微软开发的管理系统。作为数据库服务器&#xff0c;它是一种软件产品&#xff0c;其主要功能是存储和检索其他软件应用程序所请求的数据&#xff0c;这些应用程序可以运行在同一台计算机上&#xff0c;也可以运行…