位置: IT常识 - 正文

使用antv/G6在vue项目中开发较复杂样式流程图(antv g6 node 嵌套)

编辑:rootadmin
使用antv/G6在vue项目中开发较复杂样式流程图 使用antv/G6开发流程图前言一、什么是 G6?二、使用步骤1.安装依赖并导入2.初始化数据3.树图实现(1)首先配置自定义节点(2)自定义带箭头的贝塞尔曲线(连接线)(3)设置默认样式(4)配置树图并渲染(6)小地图插件4.一般图实现(1)自定义节点(2)配置图属性并渲染(3)自定义tooltip5.插件三、注意事项总结前言

推荐整理分享使用antv/G6在vue项目中开发较复杂样式流程图(antv g6 node 嵌套),希望有所帮助,仅作参考,欢迎阅读内容。

文章相关热门搜索词:ant-vue,antv g2 vue,ant vue3,antdv vue,ant vue3,antv g6 node 嵌套,antdv vue,antdv vue,内容如对您有帮助,希望把文章链接给更多的朋友!

设计师提供了一版样式较复杂的流程图,我搜了一些常用的vue-super-flow和vue-x6-flow等都只支持简单的样式。之前自己写过纯展示流程图不涉及太多交互,感觉还是找一个成熟的插件开发更适合,也方便其他同事参考,所以最后选择了用antv/G6自己个性化开发,总结了使用antv/G6在vue项目中开发常见流程图的过程。

示例如下:

一、什么是 G6?

G6官方文档链接 https://g6.antv.vision/zh/docs/manual/introduction G6 是一个图可视化引擎。它提供了图的绘制、布局、分析、交互、动画等图可视化的基础能力。旨在让关系变得透明,简单。让用户获得关系数据的 Insight。

基于 G6,用户可以快速搭建自己的 图分析 或 图编辑 应用。

G6 作为一款专业的图可视化引擎,具有以下特性: 优秀的性能:支持大规模图数据的交互与探索; 丰富的元素:内置丰富的节点与边元素,自由配置,支持自定义; 可控的交互:内置 10+ 交互行为,支持自定义交互; 强大的布局:内置了 10+ 常用的图布局,支持自定义布局; 便捷的组件:优化内置组件功能及性能; 友好的体验:根据用户需求分层梳理文档,支持 TypeScript 类型推断。 除了默认好用、配置自由的内置功能,元素、交互、布局均具有高可扩展的自定义机制。

二、使用步骤1.安装依赖并导入npm install --save @antv/g6import G6 from '@antv/g6';2.初始化数据

常用的主要是两大类,一般图和树图。

一般图的数据结构是:

data:{//节点nodes:[{id: 'XXX', label: 'XXX', key: 'XXX', text: 'XXX', img: require('XXX'),},...],// 连接线edges:[{ source: '起始id', target: '到达id',},...]}使用antv/G6在vue项目中开发较复杂样式流程图(antv g6 node 嵌套)

树图的数据结构是:

data:{id:'XXX',label: 'XXX', children: [ { id: 'XXX', label: 'XXX', children: [ { id: 'XXX', label: 'XXX', children: [] }, ...] }, ...],}

我一开始选定了用树图实现,实现了样式以后发现没有办法满足产品要求的能够隐藏/显示不同层级并且重新渲染层级关系的功能,最后又更换成了一般图的布局方式。

3.树图实现(1)首先配置自定义节点 G6.registerNode( 'icon-node', { options: { size: [90, 50], // 设置节点大小 stroke: '#91d5ff', // 设置节点边框颜色 fill: '#91d5ff', // 设置节点背景颜色 }, draw(cfg, group) { const styles = this.getShapeStyle(cfg); const { labelCfg = {} } = cfg; const w = styles.width; const h = styles.height; // 我的项目里需要区分多个节点显示不同的样式,我用key来区分 if (cfg.key != "ACTION") { const keyShape = group.addShape('rect', { attrs: { ...styles, x: -w / 2, y: -h / 2, }, }); /** * leftIcon 格式如下: * { * style: ShapeStyle; * img: '' * } */ if (cfg.leftIcon) { const { style, img } = cfg.leftIcon; // addShape是添加图形,可以添加不同的属性,具体配置可以看官方文档 group.addShape('rect', { attrs: { x: 1 - w / 2, y: 1 - h / 2, width: 38, height: styles.height - 2, fill: '#8c8c8c', ...style, }, }); group.addShape('image', { attrs: { x: 8 - w / 2, y: 8 - h / 2, width: 24, height: 24, img: img || 'https://www.yuucn.com/wp-content/uploads/2023/04/1682801894-8d347f4f69dd15b.png', }, name: 'image-shape', }); } if (cfg.label) { group.addShape('text', { attrs: { ...labelCfg.style, text: cfg.label, x: 50 - w / 2, y: 28 - h / 2, }, }); } return keyShape; } else { var keyShape = group.addShape('image', { attrs: { x: 8 - w / 2, y: 8 - h / 2, width: 24, height: 24, img: 'https://www.yuucn.com/wp-content/uploads/2023/04/1682801894-8d347f4f69dd15b.png', }, name: 'image-shape', }); group.addShape('text', { attrs: { fill: '#2f6bff', fontSize: 24, text: cfg.label, x: 8 - w / 2, y: 8 - h / 2, }, }); group.addShape('text', { attrs: { fill: '#333333', fontSize: 14, text: '项目', x: 8 - w / 2, y: h - 4, }, }); group.addShape('image', { attrs: { x: 40 - w / 2, y: h - 20, width: 14, height: 14, img: 'https://www.yuucn.com/wp-content/uploads/2023/04/1682801894-8d347f4f69dd15b.png', }, }); return keyShape; } }, update: undefined, // 自定义锚点,方便连线 getAnchorPoints: () => { return [ [0, 0.5], // 左侧中间 [1, 0.5], // 右侧中间 ]; }, }, 'single-shape', );(2)自定义带箭头的贝塞尔曲线(连接线) G6.registerEdge('flow-line', { draw(cfg, group) { const startPoint = cfg.startPoint; const endPoint = cfg.endPoint; const { style } = cfg; const shape = group.addShape('path', { attrs: { stroke: style.stroke, // endArrow: style.endArrow, // 需要箭头可以自己配置箭头 有开始箭头和结束箭头 path: [ // 用path路径配置带折点的连接线 M表示开始的坐标,L表示拐点的坐标 ['M', startPoint.x, startPoint.y], ['L', (startPoint.x + endPoint.x) / 2, (startPoint.y)], ['L', (startPoint.x + endPoint.x) / 2, endPoint.y], ['L', endPoint.x, endPoint.y], ], }, }); return shape; }, afterDraw(cfg, group) { const shape = group.get('children')[0]; const startPoint = shape.getPoint(0); //创建节点之间的圆圈,并为每一个设置样式 const circle = group.addShape('circle', { attrs: { x: startPoint.x + 92, y: startPoint.y, fill: '#cbdaff', r: 4, //圆圈大小 }, name: 'circle-shape', }); // 实现动态效果,从线的开始滑到结束 circle.animate( ratio => { const tmpPoint = shape.getPoint(ratio); return { x: tmpPoint.x, y: tmpPoint.y, }; }, { repeat: 1, //动画是否重复 duration: 3000, //一次动画持续时长 }, ); }, }, );(3)设置默认样式 // 设置默认开启状态样式 const defaultStateStyles = { hover: { stroke: '#2f6bff', }, };// 设置默认节点样式 const defaultNodeStyle = { fill: '#fff', stroke: '#cbdaff', radius: 5, shadowColor: '#cbdaff', // 阴影颜色 shadowBlur: 10, // 阴影范围 shadowOffsetX: 0, // 阴影偏移位置 shadowOffsetY: 5, cursor: 'pointer', // 鼠标移动到节点上的样式 };// 设置默认链接线样式 const defaultEdgeStyle = { stroke: '#cbdaff', // 结尾箭头样式 endArrow: { path: 'M 0,0 L 12, 6 L 9,0 L 12, -6 Z', fill: '#91d5ff', d: -60, // x轴上的偏移量 }, }; // 设置默认节点名称样式 const defaultLabelCfg = { style: { fill: '#333', fontSize: 14, }, };(4)配置树图并渲染const graph = new G6.TreeGraph({ container: 'html元素的id', width: XXX, // 渲染的宽度 height: XXX, // 渲染的高度 linkCenter: true, plugins: [minimap], // 配置插件 modes: { default: ['drag-canvas', 'zoom-canvas'], // 常用的拖拽和缩放 }, defaultNode: { type: 'icon-node', // 自定义节点名称,记得保持一致 size: [120, 40], //设置节点大小 style: defaultNodeStyle, // 配置过的默认样式,直接写在这里也可以 labelCfg: defaultLabelCfg, // 配置过的默认样式,直接写在这里也可以 }, defaultEdge: { type: 'flow-line', // 自定义连接线名称,记得保持一致 style: defaultEdgeStyle, // 配置过的默认样式,直接写在这里也可以 }, nodeStateStyles: defaultStateStyles, // 配置过的默认样式,直接写在这里也可以 edgeStateStyles: defaultStateStyles, // 配置过的默认样式,直接写在这里也可以 // 若数据中不存在节点位置,则默认为随机布局。配置布局类型及其参数。 layout: { // 类型 总共三种:径向:radial 有向分层:dagre 力导:force type: 'dagre', rankdir: 'LR', // 可选值:'TB' | 'BT' | 'LR' | 'RL',默认为图的中心 TB T,B,L,R 代表top,bottom,left,right },});graph.data(data); // 配置数据graph.render(); // 渲染流程图graph.fitView(); // 让画布内容适应视口,可选参数padding, rules, animate, animateCfg(6)小地图插件// 设置出现一个缩略地图const minimap = new G6.Minimap({ size: [150, 100], });4.一般图实现(1)自定义节点

一般图中获取节点属性获取不到节点的宽高,所以自己设置一个w、h,然后再刚刚的基础上加入一些细节

G6.registerNode( // 该新节点类型名称 'node', // 该新节点类型的定义 // 当有 extendedTypeName (第三个参数)时,没被复写的函数将会继承 extendedTypeName 的定义 { // cfg 节点身上所有的配置:包括label,size,x,y坐标等 // 这个方法,每渲染一个节点,执行一次 drawShape(cfg, group) { const styles = this.getShapeStyle(cfg); // ctg上的id key label 都可以决定当前节点的类型 // 可以根据标题长度设置节点的大小 // cfg.size = (cfg.label.length / 10) * 130; // if (cfg.label.length / 10 < 1) { // widthX = 120; // } else { // widthX = cfg.size; // } let w = 180, h = 50; // 根据设计图第一个节点主要是展示图片和个数,配置对应的形状 if (cfg.key == "0") { w = 100; h = 155; var keyShape = group.addShape('image', { attrs: { x: -w / 2, y: -h / 2, width: w, height: h, img: cfg.img, }, name: 'image-shape', }); group.addShape('circle', { attrs: { x: w / 2, y: 0, r: 2.5, fill: '#fff', stroke: '#cbdaff', lineWidth: 2, }, }); group.addShape('text', { attrs: { fill: '#2f6bff', fontSize: 24, text: cfg.label, textAlign: 'center', x: 0, y: -45, }, }); group.addShape('text', { attrs: { fill: '#333333', fontSize: 14, text: '项目', textAlign: 'center', x: 0, y: h / 2 - 30, }, }); return keyShape; } else { // 根据标题的长度判断节点的高度 var rowNum = 0 for (let i = 0; i < cfg.label.length; i++) { var j = i + rowNum if (i % 8 == 0 && i > 0 && j != cfg.label.length) { var newStr = cfg.label.slice(0, j) + "\n" + cfg.label.slice(j); cfg.label = newStr rowNum++; } } if (rowNum > 1) { h += (rowNum - 1) * 15 } cfg.size = [w, h] const rect = group.addShape('rect', { attrs: { x: -w / 2, y: -h / 2, width: w, height: h, fill: '#fff', stroke: '#a0d4ff', radius: 9, shadowColor: '#a0d4ff', // 阴影颜色 shadowBlur: 10, // 阴影范围 shadowOffsetX: 0, // 阴影偏移位置 shadowOffsetY: 5, cursor: 'pointer', // 鼠标移动 到节点上的样式 }, }); group.addShape('circle', { attrs: { x: 0 - w / 2, y: 0, r: 2.5, fill: '#fff', stroke: '#a0d4ff', lineWidth: 2, }, }); group.addShape('image', { attrs: { x: cfg.key != '1' ? 17 - w / 2 : 12 - w / 2, y: cfg.key != '1' ? -15 : -19, width: cfg.key != '1' ? 30 : 38, height: cfg.key != '1' ? 30 : 38, img: cfg.img, }, }); group.addShape('text', { attrs: { x: 60 - w / 2, y: 0, fill: '#333333', fontSize: 14, text: cfg.label, textBaseline: 'middle', }, }); return rect; } }, /** * 获取锚点(相关边的连入点) * @param {Object} cfg 节点的配置项 * @return {Array|null} 锚点(相关边的连入点)的数组,如果为 null,则没有控制点 */ // 自定义锚点 getAnchorPoints: () => { return [ [0, 0.5], // 左侧中间 [1, 0.5], // 右侧中间 ]; }, }, // 被继承的节点类型,可以是内置节点类型名,也可以是其他自定义节点的类型名。 // extendedTypeName 未指定时代表不继承其他类型的节点; // 例如基类 'single-node',或 'circle', 'rect' 等 'single-shape' );

连接线跟上面树图的配置一样即可,如果想要曲线可以自己修改

(2)配置图属性并渲染 const graph = new G6.Graph({ // 常用配置项 // 类型:Boolean;默认:'false'。图是否自适应画布。 fitView: true, // 类型:Number | Array;默认:0。图自适应画布时的四周留白像素值。fitView 为 true 时生效。 // fitViewPadding : 0 // 类型:Boolean;默认:'false'。是否平移图使其中心对齐到画布中心。v3.5.1 后支持。 fitCenter: true, // bloodView:流程图容器id container: 'projectView', width:'XXX', height:'XXX', //modes 交互行为相关 // 配置多种交互模式及其包含的交互事件的。 modes: { default: [ 'drag-canvas', 'zoom-canvas', // 如果没有特殊的样式可以直接用内置的tooltip // { // type: 'tooltip', // formatText(model) { // console.log(model) // const cfg = model.text; // const text = model.text; // // cfg.forEach((row) => { // // text.push(row.label + ':' + row.value + '<br>'); // // }); // return text; // }, // offsetX: 70, // offsetY: 20, // }, ], }, plugins: [tooltip], // 这次项目悬浮框用了插件 zoom: 1, // 若数据中不存在节点位置,则默认为随机布局。配置布局类型及其参数。 layout: { // 类型 总共三种:径向:radial 有向分层:dagre 力导:force type: 'dagre', // 'LR':从左至右布局; rankdir: 'LR', // 可选可选值:'TB' | 'BT' | 'LR' | 'RL',默认为图的中心 TB nodesep: 20, //节点之间的距离,水平布局时指垂直距离 ranksep: 40, //层级之间的距离,水平布局时指垂直距离 }, // defaultNode类型:Object。默认情况下全局节点的配置项,包括样式属性和其他属性 // G6 的内置节点包括 // circle圆形,rect长方形,ellipse椭圆,diamond菱形,triangle三角形, // star五角星,image图片,modelRect卡片,donut圆形(v4.2.5 起支持)。 defaultNode: { type: 'node', // 这里的type指向自定义节点 // size:300, size: [180, 50], style: defaultNodeStyle, labelCfg: defaultLabelCfg, }, // defaultEdge 类型:Object。默认情况下全局边的配置项,包括样式属性和其他属性 defaultEdge: { type: 'flow-line', style: { endArrow: true, lineWidth: 2, stroke: '#cbdaff', }, }, nodeStateStyles: defaultStateStyles, edgeStateStyles: defaultStateStyles, }); // 动态配置完层级后需要销毁后重新渲染,否则页面上会显示多个渲染的流程图 if (this.graph != null) { this.graph.destroy(); } this.graph = graph graph.data(data); // 渲染 graph.render(); // this.graph.zoomTo(1); // 缩放 参数为缩放的倍数,后面也可跟x,y坐标,将以传入坐标为中心 // this.graph.moveTo(0,height/2); // 采用绝对位移将画布移动到指定坐标(3)自定义tooltip

需求中的悬浮框需要展示该节点下面的所有项目及带有查看详情按钮,所以我使用了插件没有用内置的。

需要注意的一点是在getContent()里的代码一定要严格遵守编写规范,尤其注意后面一定要带';',否则会一直报错而且你找不到原因!!!

const tooltip = new G6.Tooltip({ offsetX: -400, // 偏移距离 offsetY: -850, trigger: 'click', // 触发方式 可选 mouseenter, getContent(e) { const outDiv = document.createElement('div'); outDiv.style.width = '270px'; outDiv.style.padding = '0 10px'; let ulText = ``; if (typeof (e.item.getModel().text) == 'object') { (e.item.getModel().text).map((item, index) => { ulText += `<li style='padding:5px 60px 5px 0; position: relative;word-break: break-all;white-space: nowrap;overflow: hidden;text-overflow: ellipsis;font-size: 12px;'>项目名称:<span style="color:#333">${item.label}</span><span type="text" style="color: #2f6bff;border:none;background:transparent;cursor:pointer; position: absolute;top: 50%;right: 0;transform: translateY(-50%);">查看详情</span></li>`; }); outDiv.innerHTML = `<div style="font-size:14px;color:#333;font-weight:bold;">${e.item.getModel().label}</div><ul style="margin:10px 0 0;padding:0;font-size:12px">` + ulText + `</ul>`; } else { outDiv.innerHTML = `<div style="font-size:14px;color:#333;font-weight:bold;">${e.item.getModel().label}</div>`; } return outDiv }, itemTypes: ['node'] });5.插件

G6 提供了一些可插拔的组件,包括: Legend (v4.3.0 起支持) SnapLine (v4.3.0 起支持) Grid Minimap ImageMinimap Edge Bundling Menu ToolBar TimeBar Tooltip Fisheye EdgeFilterLens

三、注意事项

如何让 IE 支持 G6

对于这类问题,我们在项目中只需要引入 babel-polyfill 即可,具体使用方法如下:

在主入门文件中引入 babel-polyfill ; 在 bable-loader 中加入如下代码:

{ test: /\.js$/, loader: 'babel-loader', include: [resolve('src'), resolve('node_modules/@antv/g6')]}

include 表示哪些目录中的 .js 文件需要进行 babel-loader;exclude 表示哪些目录中的 .js 文件不要进行 babel-loader。

总结

以上就是今天要讲的内容,本文仅仅简单介绍了antv/G6的使用,更多的示例及API请移步官方文档。

本文链接地址:https://www.jiuchutong.com/zhishi/297814.html 转载请保留说明!

上一篇:React--》超详细教程——React脚手架的搭建与使用(reactz)

下一篇:java前后端加密解密crypto-js(java前后端加密解密请求)

  • 教育培训行业是干什么的
  • 分公司可以独立签约吗
  • 什么记在借方
  • 民办非企业可以上市吗
  • 转出多交增值税最后怎么做平
  • 普通发票专用发票每张最高限额
  • 发票入不了账怎么办
  • 折旧已经计提完的固定资产如何盘点
  • 物业公司哪些费用需要公示
  • 生产企业成本核算流程及案例
  • 关于银行借贷的法律知识
  • 发票销项负数的会计分录怎么做?
  • 蔬菜营销方案案例范文
  • 农产品收购发票怎么抵扣
  • 合同无效后还能主张违约责任吗
  • 单位代扣代缴个人社保
  • 发票不能开怎么回事
  • 出口退税网上申报视频
  • 清包工开票详细名称
  • 小规模纳税人发票图片
  • 餐饮发票免税能报销吗
  • 新装的电脑开机慢是什么原因
  • 事业单位收到奖励款怎么入账
  • 进程rundll32
  • PHP:pg_convert()的用法_PostgreSQL函数
  • 土增税税
  • 金融企业风险资产处理相关税收政策
  • moments的用法
  • 厂房改造支出可以计入厂房价值吗
  • 公司基本户可以收款吗
  • 公司可以不再提取法定公积金
  • css3的模块结构和应用
  • 手把手教你安装技嘉b550 master主板
  • 深究Python中的asyncio库-线程池
  • 收取的标书收入如何入账
  • 餐厅如何计提固定成本
  • 代扣代缴的境外收入企业所得税可以抵减吗
  • 帝国cms如何使用
  • python项目开发案例集锦pdf百度网盘
  • phpcms使用教程
  • python元数据
  • 公司员工私车公用协议
  • 蔬菜和肉类是不是免税
  • 算税负是含税还是不含税
  • 技术服务费该怎么收
  • mysql在表中添加一个新的属性
  • 预开发票后涨价如何进行账务处理?
  • 数据库参数错误
  • 信用减值损失在贷方表示什么
  • 个税返还手续费政策
  • 税法中特许权费包括哪些
  • 进项税额抵扣如何做账
  • 支付系统安装
  • 不征税发票和零税率发票
  • 现金收账凭证
  • 手写发票还能用么
  • 存货跌价准备可以转回吗,分录怎么写
  • sqlserver索引的作用
  • 64位Win10系统安装Mysql5.7.11的方法(案例详解)
  • solaris的多线程实现方式
  • centos 搜索
  • 电子版win10怎么安装
  • win7无法登录桌面
  • 制作网站页面
  • 很不错的词语
  • cocos2d-js-min.js
  • unity shaders and effects cookbook
  • firefox允许弹出窗口
  • python中matplotlib绘制曲线
  • node.js详解
  • Python中断言语句
  • python搭建网站并在服务器上部署
  • jquery设置隐藏属性
  • javascript的介绍
  • 广东省国家税务总局班子成员
  • 广东电子发票开票软件?
  • 加计扣除减免税金额怎么填
  • 广东省国家税务局电子发票系统,网络设置
  • 个人所得税法全文完整版2023个人工薪规定
  • 酒精税收分类编码查询
  • 免责声明:网站部分图片文字素材来源于网络,如有侵权,请及时告知,我们会第一时间删除,谢谢! 邮箱:opceo@qq.com

    鄂ICP备2023003026号

    网站地图: 企业信息 工商信息 财税知识 网络常识 编程技术

    友情链接: 武汉网站建设