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

Vue3+TypeScript+AntVX6实现Web组态(从技术层面与实现层面进行分析)内含实际案例教学

摘要

      用Vue3+TypeScript+AntVX6实现Web组态(从技术层面与实现层面进行分析),包含画布创建、节点设计、拖拽实现(实际案例),后续文章持续更新。

注:本文章可以根据目录进行导航

文档支持

AntVX6使用文档

https://x6.antv.antgroup.com/tutorial/getting-started

AntVX6接口参数文档

https://x6.antv.antgroup.com/api/graph/graph

SVG基础文档

https://developer.mozilla.org/zh-CN/docs/Web/SVG/Tutorial/Introduction

大致描述

个人认为以下图片为AntVX6的一些基础关键(详细请见官方文档)

1.提供了画布的参数修改=>方便面板的构建

2.提供了节点的修改=>可以对节点进行增、删、改,并且可以定制化操作(增代表增加节点、删代表删除节点、改代表修改节点的属性)

3.元素式Cell是节点Node、边Edge的基类,也就是Node、Edge继承于Cell(Cell有的属性Node、Edge都有)

元素、节点、边对应参数截图(节点的学习关键是学习元素的参数,详细见API文档):

具体实现

步骤一:绘制画布

 完整代码如下(使用Vue3+TypeScript构建)

<div id="container"></div>const graph = ref<Graph | null>(null);
onMounted(() => {graph.value = new Graph({width: 1800,height: 1200,panning:true,mousewheel:true,background: {color: '#F2F7FA',},container: document.getElementById('container')!, // 断言该值不为 nullgrid: {visible: true,type: 'doubleMesh',args: [{color: '#eee', // 主网格线颜色thickness: 1, // 主网格线宽度},{color: '#ddd', // 次网格线颜色thickness: 1, // 次网格线宽度factor: 4, // 主次网格线间隔},],},});
});

代码解释:

1.我把graph画布单独定义出来,这样就可以定义更多的自定义属性(要记住单独定义完以后要通过graph.value才可以访问里面的属性)。

2.设置画布的大小width、height(官方提供了自动大小autoResize属性,但是在我代码上一直有一些小bug所以就用自定义的宽和高,没有用自动设置的这个参数,需要的可自行研究)

3.Graph 中通过 panning 和 mousewheel 配置来实现缩放与平移,鼠标按下画布后移动时会拖拽画布,滚动鼠标滚轮会缩放画布。

4.background为背景色(官方提供自定义背景,并且可以放置图片)

5.配置绘制画布对应的页面区域,并且加上!断言不为空(解决TS报可能为空的错误)

container: document.getElementById('container')!, // 断言该值不为 null

6.设置网格grid(可以直接复制,目前已知作用是让画布更好看)

7.附上对画布尺寸、位置进行操作一些常用的 API

最终的画布效果


                                                                                                                                                        

步骤二:节点设计

节点本身构造

        节点本身构造难点:markup与attrs两个参数,所以我们重点分析。

以下为官方对markup与attrs的解释:

以下是作者本人对这两个参数的理解:

        1.首先两者关系是:attrs⊂markup(attrs包含于markup,也就是首先要记住attrs是markup中的属性)

        2.举个形象的例子来说明 attrsmarkup 的作用,可以想象你正在搭建一个房子,而这个房子的结构(墙壁、窗户、门等)就是 markup,而你为这些结构上色、装饰的细节(颜色、边框、材质等)就是 attrs

  • markup:定义了房子的组成部分,比如墙、窗户、门等。你可以通过它告诉 X6:房子有哪些部分,每个部分是什么类型(是矩形?是图片?是文本?)。
  • attrs:用来决定这些部分的样子。你可以为墙刷上白色油漆、为窗户加上边框、为门安装一个红色的把手。

       3. 其实简单理解就是:markup就是定义当前节点或边具有哪些部分,attrs就是改的markup中的对应部分。

        4.注意:若加上了markup参数,在 AntV X6 中,markup 是用来定义节点的结构和内容的,控制着节点渲染时使用的 SVG 或 HTML 元素。如果你在 markup 中传递了空数组([]),X6 不会自动生成任何内容,因此即使你定义了 shape 和 imageUrl,也不会有任何元素被渲染出来。

      attrs: {},markup: [],

以下代码则正确显示节点。若移除 markup: 如果你移除 markup 属性,X6 将使用默认的标记来渲染节点,这样 shape: 'image' 和 imageUrl 的配置会生效,图像将会被渲染出来。

markup对应参数如下

官方对markup参数的解释

 

作者本人理解如下:

1.tagName

        tagName:就比如这个代码例子,可以这么理解,tagName代表创建一个 <rect> 元素,所以如果你要创建一个矩形,你会使用 tagName: 'rect',要创建文本,则使用 tagName: 'text'。

        站在html上理解:也就是相当于tagName:'rect' = <rect></rect>,tagName:'text' = <text></text>。

      markup: [{tagName: 'rect',},],
2.selector

        selector:​该元素的唯一选择器,通过选择器为该元素指定属性样式。​

  • markup 部分定义了节点的结构,规定了有哪些元素,比如 rect, image, text等
  • 每个元素通过 selector 连接到 attrs 对象中对应的属性。
  const commonAttrs = {btnText: {fontSize: 14,fill: 'red',text: 'x',refX: '88%',y: -35,cursor: 'pointer',pointerEvent: 'none',},};attrs: commonAttrs,markup: [{tagName: 'text',selector: 'btnText',  // 用 `btnText` 作为选择器},]

节点设置流程(此处以添加节点的方式解析节点设置):

1.设置节点大小与位置

根据Api的说明,节点的大小与位置设置为position、size

    const node = graph.value.addNode({position:{x: 290,y: 150,},size:{width: 150,height: 150,},})

但是根据使用说明文档发现,可以直接使用x、y、width、height字段(亲测两个都可以实现,并且效果是一样的)

测试方法:console.log(source.prop())两个写法输出都一致。

2.设置节点类型(此处用自定义图片方式)

以下为官方提供的节点形状:

以下为图片形状的设计代码:

        首先设置shape为image,让节点为图片状,而后在markup上注册image区域(因为有后续自定义需要所以定义了markup,不定义也可以,但是需删除markup字段)。

        而后设置图片的路径,并且定义自定义标签,此处在外部定义label,会导致而后所有在markup上注册的text都为相同的标签,但是可以在attrs自定义标签,并用selector选择器进行选择即可解决。

 import Ceyear4082PImg from '@/assets/InstrumentLibImage'const node = graph.value.addNode({position:{x: 290,y: 150,},size:{width: 150,height: 150,},shape: 'image',imageUrl: Ceyear4082PImg,label:'图片名',markup: [{tagName: 'image',selector: 'image',},{tagName: 'text',selector: 'label',},],attrs: {label:{refX: 0.5,refY: '100%',refY2: 4,textAnchor: 'middle',textVerticalAnchor: 'top',},},})

规范写法如下: 

    const node = graph.value.addNode({position:{x: 290,y: 150,},size:{width: 150,height: 150,},shape: 'image',markup: [{tagName: 'image',selector: 'image',},{tagName: 'text',selector: 'label',},],attrs: {image: {href: Ceyear4082PImg // 设置图片的 URL},label: {text: "图片名", // 设置文本内容refX: 0.5, // 文本相对于节点位置的 X 坐标refY: '100%', // 文本相对于节点位置的 Y 坐标,100% 表示节点的底部refY2: 4, // Y 坐标偏移量textAnchor: 'middle', // 文本水平对齐方式textVerticalAnchor: 'top', // 文本垂直对齐方式},},})

上述有个小bug(当图片的高度不同的时候,label是显示在节点大小的底部,如果图片很矮则会间隔很大,改节点的大小则会显得很小。)

步骤三(实际案例):拖拽外部图片进入画布

实现原理:在图片上加入拖拽监听,在放置于画布区域的时候检测放入的内容,计算位置后生成对应节点。

图片区域加入draggable="true", @dragend="ondragEnd($event, item)",两个代码,首先让图片为可以拖拽,然后在拖拽结束调用自定义方法。

@dragover.prevent 是 Vue.js 中的指令,用于监听 dragover 事件并阻止其默认行为。

作用解释:

  1. dragover 事件

    • 当某个元素或节点正在被拖动,并且鼠标指针进入到某个目标元素上时,会触发 dragover 事件。这个事件默认情况下会阻止元素作为拖拽目标的行为。
  2. .prevent 修饰符

    • Vue.js 提供的 .prevent 修饰符会调用 event.preventDefault(),即阻止默认行为。在 dragover 事件中,默认行为是浏览器不允许该元素作为放置目标。
  3. 为什么使用 @dragover.prevent

    • 拖放操作中,目标元素必须明确表示它可以接受拖动的内容。默认情况下,dragover 事件是不会允许放置行为的,必须通过 event.preventDefault() 来阻止默认行为,使目标元素能够正确接收拖动操作。
    • 比如,当你希望将某个元素拖动到目标区域时,需要通过 @dragover.prevent 来告诉浏览器:该元素可以作为有效的拖放目标,从而允许你后续使用 drop 事件进行拖放。
                    <el-image:src="item.imgSrc"style="object-fit: contain; cursor: grab;height: 100px;"draggable="true"@dragend="ondragEnd($event, item)"></el-image>
            <div id="container" @dragover.prevent></div>

拖拽后放置调用ondragEnd方法,获取当前拖动物体在页面的位置,并通过AntVX6画布的pageToLocal(...)将页面坐标转换为画布本地坐标(目前只实现以鼠标的位置为坐标系原点方法,若有更好的方法,欢迎讨论)。
注:HTML的坐标系是原点往负半轴延申,也就是常规坐标系的反着。

在拖拽放置的方法里调用添加节点方法,将传入的X、Y、图片信息,设置到添加节点方法里,实现拖拽功能。

 

    //****拖拽后放置****//
const ondragEnd = (event:DragEvent,item:any) => {const { x, y } = graph.value!.pageToLocal(event.pageX, event.pageY); // 将页面坐标转换为画布本地坐标。addDragNode(x, y, item);
}//****添加节点进画布****//
const addDragNode=(x:number,y:number,item:any)=>{const node = graph.value!.addNode({id:item.title,position:{x: x,y: y,},size:{width: 150,height: 100,},shape: 'image',markup: [{tagName: 'image',selector: 'image',},{tagName: 'text',selector: 'label',},],attrs: {image:{href:item.imgSrc},label: {fontSize:10,text: item.title, // 设置文本内容refX: 0.5, // 文本相对于节点位置的 X 坐标refY: '100%', // 文本相对于节点位置的 Y 坐标,100% 表示节点的底部refY2: 1, // Y 坐标偏移量},},})
}

技术与工具分析:

工具一:对齐线工具

以下图片为官方的效果图,功能就是放置节点的时候有个对齐线。

实现步骤 :根据官方的描述,对齐线是移动节点排版的辅助工具,我们提供了一个独立的插件包 @antv/x6-plugin-snapline 来使用这个功能,所以先导包,将对应所需包导入。

npm install @antv/x6-plugin-snapline --save

具体代码如下,要在画布设置的时候将其添加进去。 

  graph.value.use(new Snapline({
    enabled:true
  }))

import { Snapline } from '@antv/x6-plugin-snapline'const graph = ref<Graph | null>(null);
onMounted(() => {graph.value = new Graph({width: 1800,height: 1200,panning:true,mousewheel:true,background: {color: '#F2F7FA',},container: document.getElementById('container')!, // 断言该值不为 nullgrid: {visible: true,type: 'doubleMesh',args: [{color: '#eee', // 主网格线颜色thickness: 1, // 主网格线宽度},{color: '#ddd', // 次网格线颜色thickness: 1, // 次网格线宽度factor: 4, // 主次网格线间隔},],},});graph.value.use(new Snapline({enabled:true}))// 将画布中元素缩小或者放大一定级别,让画布正好容纳所有元素,可以通过 maxScale 配置最大缩放级别// graph.value.zoomToFit({ maxScale: 4 })
});

技术分析:

节点如果设置了id属性,那么添加相同id的节点时候会添加失败,因为已存在相同的id。

文档持续更新,敬请期待.............


http://www.mrgr.cn/news/50512.html

相关文章:

  • 加盟模式如何运营?有哪些好的技巧和方法!
  • 开放式耳机哪个品牌好?盘点开放式蓝牙耳机排行榜前五名
  • 基于YOLOv9的空中飞鸟识别检测系统(附项目源码和数据集下载)
  • 2024双11买什么东西比较好?双11必买好物清单推荐
  • 创建Python GUI的方法
  • pytorch resnet源码分析
  • JavaScript全面指南(五)
  • 现在的 AI 产品,有多难做?
  • 前端vue-获取验证码和重新获取验证码倒计时
  • C#学习笔记(二)
  • Snowflake算法js(实现)
  • 无废话➕全干货之必备开题报告模板‼️
  • 三、Spring Boot集成Spring Security之过滤器链详解
  • win10/11 下安装Ubuntu 22.04(桌面版)双系统教程
  • STM32_实验2_printf函数重定向输出
  • 矩阵相关算法
  • 抖音电商推出运费险优惠 预计为商家一年降本超40亿元
  • ARM64使能kdump
  • 012集——CAD图中线段坐标导出到txt(CAD—C#二次开发入门)
  • 浅谈Java之接口