vue3项目使用@antv/g6实现可视化流程功能

文章目录

  • 项目需求
  • 一、需要解决的问题
  • 二、初步使用
    • 1.动态数据-组件封装(解决拖拽会留下痕迹的问题,引用图片,在节点右上角渲染图标,实现,事现旋转动画,达到loading效果)
    • 2.文本太长,超出部分显示(...),如下函数返回新的文本和文本宽度
    • 3.根据某些字段的值给线增加动画,并在线上渲染文本
    • 4.自定义按钮,实现局部区域点击
    • 5.开启自带的操作栏
    • 5.鼠标悬浮展示数据


项目需求

antv/G6 - 4.8.24 版本地址
实现一个流程图,根据不同阶段、不同功能、不同状态来显示图形
1、线,需要根据状态展示不同的颜色和动画效果
2、节点部分区域需要点击功能
3、文本太长需要显示…(三个点)
4、不同状态的节点需要使用不同icon(svg图片)
5、根据需求,采用G6缩进树的布局方式,缩进树地址
6、鼠标悬浮需要展示详情数据
7、需要操作栏快速缩放还原比例

一、需要解决的问题

1、4xx版本,节点拖拽会留下痕迹,由于我画布是白色的底,所以使用官方提供的解决方案,就是在节点最底层画一个白色的矩形(图形后画的会覆盖先画)

二、初步使用

1.动态数据-组件封装(解决拖拽会留下痕迹的问题,引用图片,在节点右上角渲染图标,实现,事现旋转动画,达到loading效果)

由于旋转会绕着节点的中心点,所以需要将节点的中心点移到右上角图形的中心
假设:右上角图形中心点距离顶部和在右边的距离是12,则中心点设置为(-w + 12,-12)
vue3代码如下(示例):

<template><divid="mountNode"ref="mountNodeRef"></div>
</template>
<script setup lang="ts">
import { ref,reactive } from 'vue'
import G6 from '@antv/g6'
import runImg from '@/assets/run.svg'const treeGraph = reactive<any>({graph: {},
})interface DataType{id:stringchildren:DataType[]
}const drawerImg= (cfg: any, group: any, w: number, h: number) => {// 图片let imgswitch (cfg.status) {case StatusType.ING:img = runImgbreakcase StatusType.ABNORMAL:img = abnormalImgbreakcase StatusType.END:img = successImgbreakdefault:img = waitImg}const image = group.addShape('image', {attrs: {x: -8,y: -8,width: 16,height: 16,img, // import 引入的图片},name: 'image-shape',})// 旋转动画if (cfg.status === StatusType.ING) {image.animate((ratio: any) => {// 每一帧的操作,入参 ratio:这一帧的比例值(Number)。返回值:这一帧需要变化的参数集(Object)。// 旋转通过矩阵来实现// 当前矩阵(矩阵文档中有描述)const matrix = [1, 0, 0, 0, 1, 0, 0, 0, 1]// 目标矩阵const toMatrix = G6.Util.transform(matrix, [['r', ratio * Math.PI * 2]])// 返回这一帧需要的参数集,本例中只有目标矩阵return {matrix: toMatrix,}},{repeat: true, // 动画重复duration: 3000,easing: 'easeLinear',})}
}// 注册自定义节点
G6.registerNode('card-node', {draw: function drawShape(cfg: any, group) {// 获取初始化时defaultNode设置的宽高const w = cfg.size[0]const h = cfg.size[1]// 中心点坐标(默认是节点左上角),这里设置成图形中心(影响图像旋转等功能)// const centerX =  -w / 2// const centerY = -h / 2// 中心点坐标(默认是节点左上角),这里设置成节点右上角距离顶部和右边12的位置const centerX = -w + 12const centerY = -12const r = 10 // 边的倒角 radiusconst color = '#004CFE' // 文本颜色const baseColor = '#001043' // 文本颜色const backgroundColor = 'rgba(0,76,254,0.2)' // 填充颜色// 主图,容器矩形,画白色容器矩形,防止拖拽产生的痕迹const shape = group.addShape('rect', {attrs: {x: centerX,y: centerY,width: w,height: h,shadowColor: 'rgba(0,0,0,0.16)',shadowOffsetX: 0,shadowOffsetY: 0,shadowBlur: 4,radius: r, // 4个角都设置圆角fill: '#fff',},name: 'main-box', // 必须,用来操作图行,需要唯一// draggable: true, // 只用为true,图形才可以拖拽,同时需要配置modes中开启拖拽功能,如果上层重叠有图形,重叠的图形也需要开启该属性})// 之后添加的图形会默认覆盖在之前添加的图形上面// 新增图形,矩形头部group.addShape('rect', {attrs: {x: centerX,y: centerY,width: w,height: 28,fill: baseColor ,radius: [r, r, 0, 0], // 左上和右上设置圆角,左下和右下不变},name: 'header-box',// draggable: true,})// 矩形头部文本group.addShape('text', {attrs: {x: centerX + 8,y: centerY + 14,lineHeight: 20,text: cfg.text, // 节点数据text字段fill: color,textBaseline: 'middle', // 文本垂直居中},name: 'title',// draggable: true,})// 右上角图标drawerImg(cfg, group, w, h)// 有子数据的矩形添加收起/展开的按钮cfg.children &&group.addShape('marker', {attrs: {x: 12,y: h / 2 - 12,r: 6,cursor: 'pointer',symbol: cfg.collapsed ? G6.Marker.expand : G6.Marker.collapse,// G6 自带的标记stroke: '#666',lineWidth: 1,fill: '#fff',},name: 'collapse-icon',})return shape},setState(name, value, item: any) {// 开启缩进树的节点按钮,响应节点点击事件,展开、收起子节点树if (name === 'collapsed') {const marker = item.get('group').find((ele: any) => ele.get('name') === 'collapse-icon')const icon = value ? G6.Marker.expand : G6.Marker.collapsemarker.attr('symbol', icon)}},
})// 初始化图形实例
const initGraph = () => {const width = mountNodeRef.value.scrollWidthconst height = mountNodeRef.value.scrollHeightconst graph = new G6.TreeGraph({container: 'mountNode', // String | HTMLElement,必须,容器 id 或容器本身width, // Number,必须,图的宽度height, // Number,必须,图的高度plugins: [tooltip, toolbar], // 添加tooltip// 画布配置modes: {default: ['drag-canvas', 'zoom-canvas'], // 允许拖拽画布、放缩画布(没有添加节点拖拽)},defaultNode: {type: 'card-node',// 自定义node节点size: [132, 98],},defaultEdge: {type: 'cubic-horizontal',style: {endArrow: true,},},// 基本布局配置layout: {type: 'indented', // 布局模式(缩进树布局)direction: 'LR', // 布局方向dropCap: false,indent: 260, // 图形水平间距getHeight: () => {return 100 // 图形垂直间距},},})toRaw(treeGraph).graph = graph
}onMounted(() => {if (mountNodeRef.value) {// 初始化图形,渲染需要在异步数据更新之后initGraph()}
})// 模拟数据
// const data = {
//   id: 'A',
//   text:'我是文本超级长的文本给个省略号',
//   status:'ING',
//   children: [
//     {
//       id: 'A1',
//       text:'我是文本',
//       status:'ING',
//       children: [{ id: 'A11', text:'我是文本', }, { id: 'A12',  text:'我是文本', }],
//     },
//     {
//       id: 'A2',
//       text:'我是文本',
//       children: [
//         {
//           id: 'A21',
//           text:'我是文本',
//           children: [{ id: 'A211',  text:'我是文本', }, { id: 'A212',  text:'我是文本', }],
//         },
//         {
//           id: 'A22',
//           text:'我是文本',
//         },
//       ],
//     },
//   ],
// };// 监听数据变化渲染图形
watch(() => props.data,(value) => {toRaw(treeGraph).graph.data(value)toRaw(treeGraph).graph.render() // 渲染图toRaw(treeGraph).graph.fitView() // 布局// 监听节点点击toRaw(treeGraph).graph.on('node:click', (e: any) => {/*** 控制展开收起的小图标事件* collapse-icon 是创建图形的name,将点击响应确定在一定的范围*/if (e.target.get('name') === 'collapse-icon') {e.item.getModel().collapsed = !e.item.getModel().collapsedtoRaw(treeGraph).graph.setItemState(e.item, 'collapsed', e.item.getModel().collapsed)toRaw(treeGraph).graph.layout()}})// 可视窗口变化,更新视图if (typeof window !== 'undefined') {window.onresize = () => {if (!toRaw(treeGraph).graph || toRaw(treeGraph).graph.get('destroyed')) returnif (!mountNodeRef.value || !mountNodeRef.value.clientWidth || !mountNodeRef.value.clientHeight) returntoRaw(treeGraph).graph.changeSize(mountNodeRef.value.clientWidth, mountNodeRef.value.clientHeight)toRaw(treeGraph).graph.fitView()}}}
)
</script>

2.文本太长,超出部分显示(…),如下函数返回新的文本和文本宽度

// 计算文本宽度,和超出显示三个点
const truncateText = (text: string, maxWidth: number, fontSize = 12, fontFace = 'Microsoft YaHei') => {// 创建一个临时canvas来测量文本宽度const tempCanvas = document.createElement('canvas')const tempCtx = tempCanvas.getContext('2d')!tempCtx.font = fontSize + 'px ' + fontFace// 计算文本宽度let textWidth = tempCtx.measureText(text).width// 如果文本宽度超出最大宽度,则截断并添加省略号if (textWidth > maxWidth) {// 尝试去除一个字符,然后重新测量,直到文本宽度小于或等于最大宽度while (textWidth > maxWidth) {text = text.slice(0, -1) // 移除最后一个字符并添加省略号textWidth = tempCtx.measureText(text).width}return {width: textWidth,text: text + '...', // 移除最后一个字符并添加省略号}} else {return {width: textWidth,text,}}
}// 用例,修改上文 - 矩形头部文本
G6.registerNode('card-node', {draw: function drawShape(cfg: any, group) {// ...其他配置// 矩形头部文本const { text } = truncateText(cfg.text, 100)group.addShape('text', {attrs: {x: centerX + 8,y: centerY + 14,lineHeight: 20,// text: cfg.text, // 节点数据text字段text: text,fill: color,textBaseline: 'middle', // 文本垂直居中},name: 'title',// draggable: true,})}
})

3.根据某些字段的值给线增加动画,并在线上渲染文本

需要修改defaultEdge配置,代码如下(示例):


const lineDash = [4, 2, 1, 2]
G6.registerEdge('line-dash',{afterDraw(cfg: any, group: any) {// 获取图形组中的第一个图形,在这里就是边的路径图形const shape = group.get('children')[0]// 由于没有直接的线数据,需要根据线上的源节点或者目标节点的id来获取,节点的数据// 这里获取目标节点的模型数据const targetModel = toRaw(treeGraph).graph.findById(cfg.target).getModel()if (targetModel.status && targetModel.status === 'ING') {// 增加动画let index = 0// Define the animationshape.animate(() => {index++if (index > 9) {index = 0}const res = {lineDash,lineDashOffset: -index,}return res},{repeat: true, // whether executes the animation repeatlyduration: 3000, // the duration for executing once})}},},'cubic-horizontal' // extend the built-in edge 'cubic-horizontal'
)const initGraph = () => {const graph = new G6.TreeGraph({// ...其他配置defaultEdge: {type: 'line-dash',// 自定义线段style: {lineWidth: 2,stroke: '#bae7ff',endArrow: true,},// 线上文本的样式配置labelCfg: {autoRotate: true,style: {fill: '#1890ff',fontSize: 14,background: {fill: '#ffffff',padding: [2, 2, 2, 2],radius: 2,},},},},})
}

线上配置文本需要在graph.render() 之前,修改上文中的watch

watch(() => props.data,(value) => {// 设置各个边样式及其他配置,以及在各个状态下节点的 KeyShape 的样式。toRaw(treeGraph).graph.edge(function (edge: any) {const targetItem = toRaw(treeGraph).graph.findById(edge.target as string).getModel()const config: any = {}//  存在流量if (targetItem.status) {if (targetItem.status === 'ERROR') {config.style = {stroke: 'red',}}config.label = targetItem.status}return config})// ...其他配置toRaw(treeGraph).graph.render() // 渲染图})

4.自定义按钮,实现局部区域点击

1、按钮由一个矩形节点和文本节点组成,上文G6.registerNode增加配置
2、节点点击,锁定局部区域,graph的node:click事件

G6.registerNode('card-node', {draw: function drawShape(cfg: any, group) {// ...其他配置// 按钮矩形区域group.addShape('rect', {attrs: {x: -52,y: h - 38,width: 64,height: 26,fill: 'rgba(35,131,228,0.1)',radius: [4, 0, r, 0],cursor: 'pointer',},name: 'btn',draggable: true,})group.addShape('text', {attrs: {x: -20,y: h - 25,text: '查看详情',fill: '#2383E4',fontSize: 12,fontFamily: textFontFace,textAlign: 'center', // 文本水平居中textBaseline: 'middle', // 文本垂直居中cursor: 'pointer',},name: 'btn-text',draggable: true,})}
})toRaw(treeGraph).graph.on('node:click', (e: any) => {// 点击了查看详情if (e.target.get('name') === 'btn-text' || e.target.get('name') === 'btn') {const model = e.item.getModel()// 获取数据console.log(model)}
})

5.开启自带的操作栏

const toolbar = new G6.ToolBar()
const graph = new G6.TreeGraph({plugins: [..., toolbar], // 添加tooltip
})

5.鼠标悬浮展示数据

const graph = new G6.TreeGraph({plugins: [..., tooltip], // 添加tooltip
})const tooltip = new G6.Tooltip({offsetX: 10,offsetY: 10,// 允许出现 tooltip 的 item 类型itemTypes: ['node'],shouldBegin: (e: any) => {const model = e.item.getModel()const type = e.item.getType()// if (type === 'node' && model.id !== 'custom') {//  return true// }return false},// 自定义 tooltip 内容getContent: (e: any) => {const model = e.item.getModel()let outDiv = document.createElement('div')outDiv.style.width = 'fit-content'outDiv.innerHTML = `<h4 style="font-size:16px;font-weight:bold;margin-bottom:6px">节点详情</h4><ul style="font-size:14px;"><li>type: ${model.nodeType}</li><li>code: ${model.code}</li><li>name: ${model.name}</li></ul>`return outDiv},
})

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

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

相关文章

Fluent udf编译的一些注意事项

Fluent udf编译的一些注意事项 参考链接&#xff1a;1.fluent UDF编译环境处理_哔哩哔哩_bilibili 2.【觉兽课堂】ANSYS FLUENT UDF教学02&#xff1a;UDF语法及编写 小白入门必备_哔哩哔哩_bilibili #1 需要注意的内容 ##1.1 修改vs的路径 在fluent路径中&#xff0c;打开ud…

Golang笔记:使用serial包进行串口通讯

文章目录 目的使用入门总结 目的 串口是非常常用的一种电脑与设备交互的接口。这篇文章将介绍golang中相关功能的使用。 本文使用的包为 &#xff1a;go.bug.st/serial https://pkg.go.dev/go.bug.st/serial https://github.com/bugst/go-serial 另外还有一些常见的包如&…

cpp入门(命名空间,输入输出与缺省参数)

目录 cpp关键字 命名空间 命名空间的使用 1.加名称及作用域限定符 2.使用using将命名空间中某个成员引入 3.展开命名空间 注意 输入输出 缺省参数 cpp关键字 命名空间 定义命名空间&#xff0c;需要使用到namespace关键字&#xff0c;后面跟命名空间的名字&#xff0c…

【odoo | JavaScript | ES6】浅谈前端导入(import)和导出(export)

概要 前端开发中的导入&#xff08;import&#xff09;和导出&#xff08;export&#xff09;是指在JavaScript模块系统中引入和输出代码的机制。ES6&#xff08;ECMAScript 2015&#xff09;引入了这种模块化系统&#xff0c;使开发者可以更加高效地组织和管理代码。 导出 导…

素数普遍公式与哥德巴赫猜想

详见百度百科【素数普遍公式】 公元前250年同样是古希腊的数学家埃拉托塞尼提出一种筛法&#xff1a; &#xff08;一&#xff09;“要得到不大于某个自然数N的所有素数&#xff0c;只要在2---N中将不大于 的素数的倍数全部划去即可”。 &#xff08;二&#xff09;将上面的…

NodeJs实现对本地 mysql 数据库的增删改查

写在前面 今天我们接着写nodejs对数据库的操作&#xff0c;今天实现简单的增删改查&#xff0c;读之前请先移步到这里NodeJs 连接本地 mySql 数据库获取数据,避免后续一些代码出险阅读断层。 安装 nodemon npm install nodemon因为 nodejs 的服务是本地启动&#xff0c;避免后…

FPGA开发Vivado安装教程

前言 非常遗憾的一件事情是&#xff0c;在选修课程时我避开了FPGA&#xff0c;选择了其他方向的课程。然而&#xff0c;令我没有想到的是&#xff0c;通信项目设计的题目竟然使用FPGA&#xff0c;这简直是背刺。在仅有的半个月时间里&#xff0c;准备这个项目确实是非常紧张的…

故障模式与影响分析(FMEA)

故障模式与影响分析FMEA 故障模式与影响分析&#xff08;FMEA&#xff09;是一种系统性的风险评估方法&#xff0c;主要用于识别潜在的产品或过程故障模式&#xff08;即系统或组件失效的方式&#xff09;&#xff0c;以及这些故障对系统性能的影响程度。FMEA通过分析可能的故…

高考英语3500词

DAY1 DAY2 DAY3 DAY4 DAY5 DAY6 DAY7 DAY8 DAY9 DAY10 DAY11 DAY12 DAY13 DAY14 DAY15 DAY16 DAY17 DAY18 DAY19 DAY20 DAY21 DAY22 DAY23 DAY24 DAY25 DAY26 DAY27 DAY28 DAY29 DAY30 DAY31 DAY32 DAY33 DAY34 DAY35 DAY36 DAY37 DAY38 DAY39 DAY40

android 是Application类先运行还是AndroidManifest.xml中action先运行?Application类先运行

android 是Application类先运行还是AndroidManifest.xml中action先运行 在Android应用启动过程中&#xff0c;Application类和AndroidManifest.xml中的action存在一个严格的初始化顺序。具体来说&#xff1a; Application类&#xff1a;在应用启动时&#xff0c;系统会首先实例…

《数字图像处理》实验报告二:直方图均衡化

一、实验任务与要求 1、实验任务&#xff1a; 读取图像&#xff0c;绘制直方图/归一化直方图&#xff08;hist/histogram, subplot&#xff09; 使用直方图均衡化函数&#xff08;histeq&#xff09;自己实现直方图均衡化函数 2、要求&#xff1a; 提交 m 文件&#xff…

树莓派只是玩具?看看上海国际嵌入式展上用树莓派做的高大上产品!

三天的 Embedded World China 上海国际嵌入式展圆满落幕&#xff01;这次展会上海晶珩、英国 Raspberry Pi、工业自动化软件领导者 CODESYS 集体亮相&#xff0c;为观众带来了精彩纷呈的视听盛宴。在展会现场&#xff0c;树莓派团队带来了最新的 Raspberry Pi AI Kit 场景演示&…

网络配置(IP、NETMASK、GATEWAY、DNS、DHCP)

参考&#xff1a; 初学Linux之网络配置(IP、NETMASK、GATEWAY、DNS、DHCP)-CSDN博客【学习笔记】网关 & 路由_网关和路由-CSDN博客【学习笔记】计算机网络 IP地址与MAC地址_根据mac分配ip-CSDN博客【学习笔记】TCP 和 UDP 协议_tcp 发送 syn 应答没有syn ack-CSDN博客 一…

java版CRM客户关系管理系统Spring Cloud alibaba Spring Boot成长型企业必备的客户关系管理系统

鸿鹄CRM客户关系管理系统是一款基于Java语言开发的CRM系统&#xff0c;采用了Spring Cloud Alibaba、Spring Boot、MybatisPlus、Redis和VUE3 ElementUI等技术&#xff0c;构建了一个微服务架构。该系统具有以下功能模块&#xff1a; 一、待办事项 1、今日需联系客户&#xf…

P2P去中心化网络的重点组件

P2P去中心化网络的重点组件 P2P&#xff08;Peer-to-Peer&#xff09;去中心化网络是一种网络架构&#xff0c;其中所有参与者&#xff08;节点&#xff09;都具有平等地位&#xff0c;直接相互通信&#xff0c;而无需中央协调机构。以下是P2P去中心化网络的关键组件及其详细介…

C# .NET 8 SQL 批量插入 Dapper、BulkCopy 和表值参数

介绍 在 .NET 应用程序中处理大型数据集通常需要将数据高效地插入到 SQL Server 中。本文探讨了使用 C# 和 .NET 8 批量插入数据的三种流行技术&#xff1a;Dapper、SqlBulkCopy 和表值参数 (TVP)。我们将比较它们的性能、易用性和对不同场景的适用性。 1. Dapper Bulk Inser…

论Lambda架构及其应用

摘要&#xff1a; 2023年3月&#xff0c;我公司承担了本市教育局智慧教育云平台的项目&#xff0c;该平台旨在为教育机构和学生提供一个在线学习与教学的环境&#xff0c;包含课程管理、在线学习、资源共享、互动交流等功能。在项目中我担任系统架构师&#xff0c;全面负责项目…

实在RPA的硬件交互功能:U盾机械臂组件

一、为什么需要U盾机械臂&#xff1f; 在使用银行U盾&#xff08;USB Key&#xff09;进行操作时&#xff0c;涉及到许多手动确认步骤&#xff0c;特别是按下U盾上的确认按键。这种手动操作是自动化过程中的一个瓶颈。为了实现完全自动化&#xff0c;需要一种方法来自动按下U…

代码随想录算法训练营:17/60

非科班学习算法day17 | LeetCode654:最大二叉树 &#xff0c;Leetcode617:合并二叉树 &#xff0c;Leetcode700:二叉搜索树中的搜索&#xff0c;Leetcode98&#xff1a;验证二叉搜索树 目录 介绍 一、基础概念补充&#xff1a; 1.二叉搜索树 二、LeetCode题目 1.LeetCode…

价格!六安市各地双软(软件企业、软件产品)办理流程步骤及申报材料、时间

六安市各地双软&#xff08;软件企业、软件产品&#xff09;办理流程步骤材料 &#xff08;更多问题详情可以查看小编主页方式&#xff09; 第一步&#xff1a;办理软件企业认定 1.打开办理软件企业认定的中心网站&#xff0c;然后注册并登录&#xff0c;下载双软认定申报表…