当前位置: 首页 > news >正文

自定义复杂AntV/G6案例

一、效果图

在这里插入图片描述

二、源码

/**
*
* Author: me
* CreatDate: 2024-08-22
*
* Description: 复杂G6案例
*
*/
<template><div class="moreG6-wapper"><div id="graphContainer" ref="graphRef" class="graph-content"></div></div>
</template><script>
import G6 from "@antv/g6";
export default {name: "moreG6",components: {},props: {},data() {return {graph: null, // G6 实例graphData: {nodes: [],edges: [],},dataList: [{name: "第一阶段",aimData: [{title: "张三",aim: "战胜李四",type: 1,},{title: "李四",aim: "战胜张三",type: 2,},],eventData: [{time: "1948年11月6日",isLeft: true, // 在左边还是在右边dataList: {title: "发现王五正在收缩",type: 1,domain: "综合",describe: "发现王五正在收缩,当即转入追击",children: [{title: "张三攻占a区,没发现李四",type: 1,domain: "战斗",describe: "",},{title: "李四调动李白",type: 1,domain: "战斗",describe: "",},],},},{time: "1948年11月7日",isLeft: false,dataList: {title: "张三团守a州,同时向b州东进",type: 2,domain: "综合",describe: "张三团守a州,令小红、小兰两人回b州东进",children: [{title: "李四在b官邸",type: 2,domain: "战斗",describe: "",},],},},],},{name: "第二阶段",aimData: [{title: "小红",aim: "战胜王五",type: 1,},{title: "小兰",aim: "战胜王五",type: 1,},{title: "王五",aim: "战胜张三",type: 2,},],eventData: [{time: "1948年11月6日",isLeft: true,dataList: {title: "发现王五正在收缩",type: 1,domain: "综合",describe: "发现王五正在收缩,当即转入追击",children: [{title: "张三攻占a区,没发现李四",type: 1,domain: "战斗",describe: "",},{title: "李四调动李白",type: 1,domain: "战斗",describe: "",},{title: "李四造谣张三",type: 1,domain: "舆论",describe: "",},],},},{time: "1948年11月7日",isLeft: false,dataList: {title: "张三团守a州",type: 2,domain: "综合",describe: "张三团守a州,令小红、小兰两人回徐州东进",children: [{title: "李四在b官邸",type: 2,domain: "前进",describe: "",},{title: "小兰垄断李四的口粮",type: 2,domain: "商战",describe: "",},],},},],},{name: "第三阶段",},{name: "第四阶段",},],bgColor: "#0B1E4C",stageSize: [110, 40],aimSize: [150, 30],eventSize: [176, 46],eventContentSize: [182, 90],eventDataSize: [120, 40],stageSpace: 100,openSpace: 40,aimSpace: 150,eventSpace: 220,domainW: 28,};},mounted() {this.$nextTick(() => {this.initData();this.initGraph();this.drawGraph();});},methods: {/*** 根据数据生成节点和边数据*/initData() {const that = this;const dom = this.$refs.graphRef;const centerX = dom.offsetWidth / 2;this.dataList.forEach((stage, index) => {const pl = ((stage.partieList?.length || 0) / 2) * that.aimSize[1];let eventLen = 0;if (index === 0) {stage.y = 40 + pl;} else {eventLen = this.dataList[index - 1].eventData?.length || 0;stage.y =this.dataList[index - 1].y +eventLen * that.eventSpace +that.stageSpace * 2 +pl;}// 1.阶段总线const stageNde = {id: `stageNode${index}`, // 元素的 idlabel: stage.name, // 标签文字x: centerX,y: stage.y,type: "rect", // 元素的图形size: that.stageSize, // 元素的大小// 标签配置属性labelCfg: {positions: "center", // 标签在元素中的位置// 包裹标签样式属性的字段 style 与标签其他属性在数据结构上并行style: {fill: "#fff", // 填充色,这里是文字颜色fontSize: 16,// fontWeight: "bold",fontFamily: "黑体",},},// 包裹样式属性的字段 style 与其他属性在数据结构上并行style: {lineWidth: 2,fill: "#0F3664", // 元素的填充色stroke: "#fff", // 元素的描边色radius: 20, // 圆角},// 锚点anchorPoints: [[1, 0.5], // 右中[0.5, 1], // 下中],};this.graphData.nodes.push(stageNde);if (index !== 0) {const stageLine = {id: `stageLine${index}`,source: `stageNode${index - 1}`, // 和上面的保持一致target: `stageNode${index}`,type: "line", // 边形状style: {lineWidth: 2,color: "#fff",opacity: 0.7, // 边透明度},};this.graphData.edges.push(stageLine);}// 2.阶段-目标数据if (stage.aimData && stage.aimData.length) {const adl = stage.aimData.length;// 目标数据stage.aimData.forEach((aim, aimIndex) => {// aimNodeY 先大概吧,后面再重新计算优化一下const aimNodeY =stage.y + (that.aimSize[1] + 10) * aimIndex - pl - adl * 12;// 目标标题const aimNode = {id: `aimNode${index}${aimIndex}`,label: aim.title + "目标",x: centerX + that.stageSize[0] + that.openSpace * 2.5,y: aimNodeY,type: "rect",size: that.aimSize,labelCfg: {style: {textAlign: "center",fill: "#fff",fontSize: 14,fontFamily: "黑体",},},style: {lineWidth: 0,fill: aim.type === 1 ? "#E16E76" : "#1685F3",radius: [0, 15, 15, 0],},anchorPoints: [[0, 0.5],[1, 0.5],],};this.graphData.nodes.push(aimNode);// 目标内容const aimContentNode = {id: `aimContentNode${index}${aimIndex}`,label: aim.aim,x: centerX + that.stageSize[0] + that.openSpace * 6.5,y: aimNodeY,type: "rect",size: [0, 40],labelCfg: {positions: "left",style: {textAlign: "left",fill: "#fff",fontSize: 14,opacity: 0.7,fontFamily: "黑体",},},style: {lineWidth: 0,fill: that.bgColor,},anchorPoints: [[0, 0.5]],};this.graphData.nodes.push(aimContentNode);// 连线const aimLine = {id: `aimLine${index}${aimIndex}`,source: `stageNode${index}`,target: `aimNode${index}${aimIndex}`,type: "cubic-horizontal", // 边形状style: {lineWidth: 1,fill: "transparent", // 设置透明背景色opacity: 0.7,},};const aimContentLine = {id: `aimContentLine${index}${aimIndex}`,source: `aimNode${index}${aimIndex}`,target: `aimContentNode${index}${aimIndex}`,type: "line", // 边形状style: {lineWidth: 1,fill: "#ffffff",opacity: 0.7,},};this.graphData.edges.push(aimLine);this.graphData.edges.push(aimContentLine);});}// 3.时间事件数据if (stage.eventData && stage.eventData.length) {stage.eventData.forEach((event, eventIndex) => {event.x = event.isLeft? -centerX + that.stageSize[0] + that.openSpace * 4: centerX + that.stageSize[0] + that.openSpace * 4;event.y =stage.y +eventIndex * that.eventSpace +that.aimSize[1] +that.openSpace * 2.5; // 先大概吧,后面再重新计算优化一下// 时间const timeNode = {id: `timeNode${index}${eventIndex}`,label: event.time,x: event.isLeft ? centerX - 10 : centerX + 10,y: event.y - 16,type: "rect",size: [0, 40],labelCfg: {positions: event.isLeft ? "right" : "left",style: {textAlign: event.isLeft ? "right" : "left",fill: "#fff",fontSize: 14,opacity: 0.8,fontFamily: "黑体",},},style: {lineWidth: 0,fill: that.bgColor,},};this.graphData.nodes.push(timeNode);// 事件标题const eventNode = {id: `eventNode${index}${eventIndex}`,label: that.fittingString(event.dataList.title, 9),labelInit: event.dataList.title,x: event.isLeft? -event.x - that.domainW + 12: event.x + that.domainW - 12,y: event.y,type: "rect",size: that.eventSize,labelCfg: {positions: event.isLeft ? "right" : "left",style: {textAlign: "center",fill: "#fff",fontSize: 14,fontFamily: "黑体",},},style: {lineWidth: 0,fill: event.dataList.type === 1 ? "#E16E76" : "#1685F3",radius: event.isLeft ? [24, 0, 0, 24] : [0, 24, 24, 0],},anchorPoints: [[0, 0.5],[1, 0.5],],};this.graphData.nodes.push(eventNode);// 领域const realmNode = {id: `realmNode${index}${eventIndex}`,label: [...event.dataList.domain].join("\n"),x: event.isLeft? -event.x + that.openSpace * 2 + 6: event.x - that.openSpace * 2 - 6,y: event.y,type: "rect",size: [that.domainW, that.eventSize[1]],labelCfg: {positions: "center",style: {textAlign: "center",fill: "#fff",fontSize: 14,fontFamily: "黑体",},},style: {lineWidth: 1,fill: event.dataList.type === 1 ? "#E16E76" : "#1685F3",stroke: event.dataList.type === 1 ? "#EDA9AE" : "#76B7F8", // 元素的描边色},};this.graphData.nodes.push(realmNode);// 内容const eventContentNode = {id: `eventContentNode${index}${eventIndex}`,label: event.dataList.describe? that.divideString(event.dataList.describe, 11, 4): "暂无描述",labelInit: event.dataList.describe,x: event.isLeft ? -event.x + 9 : event.x - 9,y: event.y + that.eventSize[1] + 22,type: "rect",size: that.eventContentSize,labelCfg: {positions: "center",style: {textAlign: "center",fill: "#fff",fontSize: 14,fontFamily: "黑体",},},style: {lineWidth: 1,fill: "#3C4B70",stroke: "#5C6988", // 元素的描边色opacity: 0.9,},};this.graphData.nodes.push(eventContentNode);// 连线const eventLine = {id: `eventLine${index}${eventIndex}`,source: `stageNode${index}`,target: `eventNode${index}${eventIndex}`,type: "polyline", // 边形状style: {lineWidth: 1,fill: "transparent",opacity: 0.7,},};this.graphData.edges.push(eventLine);// 4.时间事件后面的列表if (event.dataList.children && event.dataList.children.length) {event.dataList.children.forEach((eventData, eventDataIndex) => {const x =event.x +that.eventSize[0] +that.openSpace * 3 +eventDataIndex *(that.eventDataSize[0] + that.openSpace * 1.4);const y = event.y + that.openSpace;// 标题const eventDataNode = {id: `eventDataNode${index}${eventIndex}${eventDataIndex}`,label: that.fittingString(eventData.title, 6),labelInit: eventData.title,x: event.isLeft? -x - that.domainW + 14: x + that.domainW - 14,y: y,type: "rect",size: that.eventDataSize,labelCfg: {positions: event.isLeft ? "right" : "left",style: {textAlign: "center",fill: "#fff",fontSize: 14,fontFamily: "黑体",},},style: {lineWidth: 0,fill: event.dataList.type === 1 ? "#E16E76" : "#1685F3",},anchorPoints: [[0.5, 0]],};this.graphData.nodes.push(eventDataNode);// 领域const realmNode02 = {id: `realmNode${index}${eventIndex}${eventDataIndex}02`,label: [...eventData.domain].join("\n"),x: event.isLeft ? -x + 60 : x - 60,y: y,type: "rect",size: [that.domainW, that.eventDataSize[1]],labelCfg: {positions: "center",style: {textAlign: "center",fill: "#fff",fontSize: 14,fontFamily: "黑体",},},style: {lineWidth: 1,fill: event.dataList.type === 1 ? "#E16E76" : "#1685F3",stroke: event.dataList.type === 1 ? "#EDA9AE" : "#76B7F8", // 元素的描边色},};this.graphData.nodes.push(realmNode02);// 内容const eventContentNode02 = {id: `eventContentNode${index}${eventIndex}${eventDataIndex}02`,label: eventData.describe? that.divideString(eventData.describe, 9, 3): "暂无描述",labelInit: eventData.describe,x: event.isLeft ? -x : x,y: y + that.eventDataSize[1] + 18,type: "rect",size: [that.eventDataSize[0] + that.domainW,that.eventContentSize[1] * 0.85,],labelCfg: {positions: "center",style: {textAlign: "center",fill: "#fff",fontSize: 14,fontFamily: "黑体",},},style: {lineWidth: 1,fill: "#3C4B70",stroke: "#5C6988",opacity: 0.9,},};this.graphData.nodes.push(eventContentNode02);// 连线const eventLine = {id: `eventLine${index}${eventIndex}${eventDataIndex}`,source: `eventNode${index}${eventIndex}`,target: `eventDataNode${index}${eventIndex}${eventDataIndex}`,type: "polyline", // 边形状style: {lineWidth: 1,fill: "transparent", // 透明色opacity: 0.7,},};this.graphData.edges.push(eventLine);});}});}});},/*** 初始化graph实例*/initGraph() {// 缩略图const minimap = new G6.Minimap({size: [window.innerWidth / 6, window.innerHeight / 4],});// 提示框const tooltip = new G6.Tooltip({offsetX: 10,offsetY: 10,size: [100, 100],itemTypes: ["node"], // 允许出现 tooltip 的 item 类型// 自定义 tooltip 内容getContent: (e) => {const outDiv = document.createElement("div");outDiv.style.width = "fit-content";outDiv.innerHTML = `<div style="max-width:200px;">${e.item.getModel().labelInit || e.item.getModel().label}</div>`;return outDiv;},// 哪些要设置提示框shouldBegin: (e) => {const id = e.item.getModel().id;const a = id.includes("eventNode");const b = id.includes("eventContentNode");const c = id.includes("eventDataNode");const is = a || b || c;return is;},});// 容器节点const dom = this.$refs.graphRef;// 配置参数const options = {container: "graphContainer", // 画布的容器idwith: dom.offsetWidth, // 画布宽度height: dom.offsetHeight, // 画布高度minZoom: 0.2,maxZoom: 5,// 图交互模式modes: {default: ["drag-canvas", "zoom-canvas"], // 允许拖拽画布、缩放画布},plugins: [tooltip, minimap], // 插件};// 实例化this.graph = new G6.Graph(options);// 自适应this.resizeView();// 监听窗口大小变化window.addEventListener("resize", this.resizeView);},/*** 画图*/drawGraph() {// 清除画布元素this.graph.clear();// 数据源this.graph.data(this.graphData);// 渲染this.graph.render();},/*** 自适应*/resizeView() {const dom = this.$refs.graphRef;if (dom && this.graph) {this.graph.changeSize(dom.offsetWidth, dom.offsetHeight); // 修改画布大小// this.graph.fitCenter(); // 移至中心}},/*** 字符串等长划分-入口事件* @param str 字符串* @param len 一行的长度* @param row 行数*/divideString(str, len, row = 3) {str = this.fittingString(str, len * row - 2);const result = this.divideHandle(str, len);return result.join("\n");},/*** 字符串等长划分* @param str 字符串* @param len 划分的长度*/divideHandle(str, len) {if (str.length <= len) {return [str];}const chunk = str.substring(0, len);const arr = [chunk, ...this.divideHandle(str.substring(len), len)];return arr;},/*** 截取字符串,超出多少len省略* @param str 字符串* @param len 长度*/fittingString(str, len) {if (str.length <= len) {return str;}const result = str.substring(0, len) + "…";return result;},},beforeDestroy() {this.graph && this.graph.destroy();},watch: {},
};
</script><style lang='scss' scoped>
.moreG6-wapper {height: 100%;background: #0b1e4c;.graph-content {width: 100%;height: 96%;::v-deep .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;}::v-deep .g6-minimap {position: absolute;right: 0;top: 61px;background-color: #0b1e4c;}}
}
</style>

三、记录说明

  • 当前使用版本:"@antv/g6": "^4.8.21"

  • 官方文档节点形状为shape,但是在不记得哪个版本后, shape 改为 type,当时整了好久,形状就是不生效,原来是字段改变了,这个坑要注意

  • 初步接触就有这么个界面需求,官方文档有点乱,且不全面,新手小白上路属实有点头皮发麻,后来也琢磨出来一些:

    • 官方案例实现不了的,自己画
    • 无非就是两个要点:节点、边
    • 像这案例一样的不能使用居中api的,得自己计算节点的位置
    • 实现不了的不影响使用的小功能,就干掉它,不要了…
  • 期间遇到还没解决的问题:

    • 使用image内置节点,自己的图片显示有问题
    • polyline样式的连线拐点问题,本来定在边角的锚点,但是拐点太多了不齐整,后来就直接锚点在中间了…
    • 本来有个节点收缩/展开的功能,后来没做了,这个后面再研究研究…

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • FreeRTOS学习——接口宏portmacro.h
  • 微波无源器件 功分器 4 一种用于天线阵列的紧凑宽带四路双极化波导功分器
  • 独立站技能树之建站33项自检清单 1.0丨出海笔记
  • 【STM32系统】基于STM32设计的SD卡数据读取与上位机显示系统(SDIO接口驱动、雷龙SD卡)——文末资料下载
  • golang学习笔记29——golang 中如何将 GitHub 最新提交的版本设置为 v1.0.0
  • 虚幻引擎 | (类恐鬼症)玩家和NPC语音聊天(下)
  • 观成科技:新版suo5隧道工具加密流量跟踪分析
  • # dbt source dbt source freshness命令详解
  • 【Linux 20】线程控制
  • IP包头分析
  • tomcat服务器
  • 关于支持向量机的一份介绍
  • Ubuntu 20.04安装pycharm2022及配置快捷方式
  • 09 Shell Scriptfor循环结构语句
  • 【乐企-业务篇】OFD对应xml实体对象定义
  • 【译】JS基础算法脚本:字符串结尾
  • [nginx文档翻译系列] 控制nginx
  • 【挥舞JS】JS实现继承,封装一个extends方法
  • 【刷算法】从上往下打印二叉树
  • 【译】React性能工程(下) -- 深入研究React性能调试
  • 230. Kth Smallest Element in a BST
  • C++入门教程(10):for 语句
  • eclipse(luna)创建web工程
  • ES10 特性的完整指南
  • flutter的key在widget list的作用以及必要性
  • maven工程打包jar以及java jar命令的classpath使用
  • OSS Web直传 (文件图片)
  • Spark学习笔记之相关记录
  • vue--为什么data属性必须是一个函数
  • 初识 beanstalkd
  • 关于extract.autodesk.io的一些说明
  • 前端相关框架总和
  • 如何用Ubuntu和Xen来设置Kubernetes?
  • 我的业余项目总结
  • ​如何使用QGIS制作三维建筑
  • ​数据结构之初始二叉树(3)
  • ​业务双活的数据切换思路设计(下)
  • # Maven错误Error executing Maven
  • # 利刃出鞘_Tomcat 核心原理解析(七)
  • # 移动硬盘误操作制作为启动盘数据恢复问题
  • #if 1...#endif
  • %3cli%3e连接html页面,html+canvas实现屏幕截取
  • (02)Cartographer源码无死角解析-(03) 新数据运行与地图保存、加载地图启动仅定位模式
  • (2024最新)CentOS 7上在线安装MySQL 5.7|喂饭级教程
  • (7)svelte 教程: Props(属性)
  • (十)【Jmeter】线程(Threads(Users))之jp@gc - Stepping Thread Group (deprecated)
  • .a文件和.so文件
  • .bat批处理(九):替换带有等号=的字符串的子串
  • .NET CLR Hosting 简介
  • .NET Entity FrameWork 总结 ,在项目中用处个人感觉不大。适合初级用用,不涉及到与数据库通信。
  • .NET 除了用 Task 之外,如何自己写一个可以 await 的对象?
  • .NET(C#、VB)APP开发——Smobiler平台控件介绍:Bluetooth组件
  • .NET教程 - 字符串 编码 正则表达式(String Encoding Regular Express)
  • ??如何把JavaScript脚本中的参数传到java代码段中
  • @ 代码随想录算法训练营第8周(C语言)|Day57(动态规划)