0


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')!, // 断言该值不为 null
    grid: {
      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.举个形象的例子来说明 
attrs

markup

的作用,可以想象你正在搭建一个房子,而这个房子的结构(墙壁、窗户、门等)就是

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 坐标偏移量
      },
    },
  })
}

步骤四(实际案例):画布内的节点相互连线

效果图如下

1.配置连接桩

要实现连线功能,首先要理解AntVX6的连接桩属性,在添加节点的时候,为节点配置上连接桩

官方解释:

首先我们将具有相同行为和外观的连接桩归为同一组,并通过

groups

选项来设置分组,该选项是一个对象

{ [groupName: string]: PortGroupMetadata }

,组名为键,值为每组连接桩的默认选项。

然后我们配置

items

items

是一个数组

PortMetadata[]

,数组的每一项表示一个连接桩,连接桩支持的选项如下。

个人理解:

group就是设置连接桩的属性,通过groups可以统一管理,记得要magnet: true,才可以进行连线,然后items是就是把设置好的连接桩放置在节点上(然后可以设置样式),个人认为也是相当于注册了一个HTML在节点上。

    ports: {
      groups: {
        group1: {
          position: {
            name: 'left',
            args: { x: 0, y: 0 },
          },
          attrs: {
            circle: {
              magnet: true, // 允许连线
              stroke: '#8f8f8f', // 设置连接桩的样式
              r: 5,
            },
          },
        },
        group2: {
          position: {
            name: 'right',
            args: { x: 0, y: 0 },
          },
          attrs: {
            circle: {
              magnet: true, // 允许连线
              stroke: '#8f8f8f', // 设置连接桩的样式
              r: 5,
            },
          },
        },
      },
      items: [
        {
          group: 'group1',
          args: {
            x: '60%',
            y: 32,
            angle: 45,
          },
        },
        {
          group: 'group2',
          args: {
            x: '60%',
            y: 32,
            angle: 45,
          },
        },
      ],
    },

添加节点完整代码:

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 坐标偏移量
      },
    },
    ports: {
      groups: {
        group1: {
          position: {
            name: 'left',
            args: { x: 0, y: 0 },
          },
          attrs: {
            circle: {
              magnet: true, // 允许连线
              stroke: '#8f8f8f', // 设置连接桩的样式
              r: 5,
            },
          },
        },
        group2: {
          position: {
            name: 'right',
            args: { x: 0, y: 0 },
          },
          attrs: {
            circle: {
              magnet: true, // 允许连线
              stroke: '#8f8f8f', // 设置连接桩的样式
              r: 5,
            },
          },
        },
      },
      items: [
        {
          group: 'group1',
          args: {
            x: '60%',
            y: 32,
            angle: 45,
          },
        },
        {
          group: 'group2',
          args: {
            x: '60%',
            y: 32,
            angle: 45,
          },
        },
      ],
    },
  })
}

2.在画布上设置连线交互

官方对连线的解释见链接:

https://x6.antv.antgroup.com/api/model/interaction#%E8%BF%9E%E7%BA%BF

连线交互的代码解释我已经写在注释,具体请看注释。

    //连线交互
    connecting: {
      snap: {
        radius: 50, //自动吸附,并设置自动吸附路径
      },
      allowBlank: false, // 是否允许连接到画布空白位置的点(就是能不能拉线连空白的地方)
      allowLoop: false, // 是否允许创建循环连线,即边的起始节点和终止节点为同一节点,就是能不能自我连线(箭头不能穿过仪器)
      allowNode:false, //是否允许边连接到节点(非节点上的连接桩),默认为 true 。(就是要让它必须连接到连接桩,连接到节点不行)
      allowEdge:false, //是否可以同一个起点终点,在箭头的线中间加一个箭头,就是一条线能一直加箭头
      allowMulti: true, // 是否可以一个起点连多个终点
      highlight: true, // 拖动边时,是否高亮显示所有可用的连接桩或节点,默认值为 false 。一般都会与 highlighting 联合使用。
    },
    //高亮器
    highlighting:{
      // 当连接桩可以被链接时,在连接桩外围渲染一个 2px 宽的红色矩形框
      magnetAvailable: {
        name: 'stroke',
        args: {
          padding: 4,
          attrs: {
            'stroke-width': 2,
            stroke: 'red',
          },
        },
      }
    }

完整代码如下:

onMounted(() => {
  graph.value = new Graph({
    width: 1800,
    height: 1200,
    panning:true,
    mousewheel:true,
    background: {
      color: '#F2F7FA',
    },
    container: document.getElementById('container')!, // 断言该值不为 null
    grid: {
      visible: true,
      type: 'doubleMesh',
      args: [
        {
          color: '#eee', // 主网格线颜色
          thickness: 1, // 主网格线宽度
        },
        {
          color: '#ddd', // 次网格线颜色
          thickness: 1, // 次网格线宽度
          factor: 4, // 主次网格线间隔
        },
      ],
    },
    //连线交互
    connecting: {
      snap: {
        radius: 50, //自动吸附,并设置自动吸附路径
      },
      allowBlank: false, // 是否允许连接到画布空白位置的点(就是能不能拉线连空白的地方)
      allowLoop: false, // 是否允许创建循环连线,即边的起始节点和终止节点为同一节点,就是能不能自我连线(箭头不能穿过仪器)
      allowNode:false, //是否允许边连接到节点(非节点上的连接桩),默认为 true 。(就是要让它必须连接到连接桩,连接到节点不行)
      allowEdge:false, //是否可以同一个起点终点,在箭头的线中间加一个箭头,就是一条线能一直加箭头
      allowMulti: true, // 是否可以一个起点连多个终点
      highlight: true, // 拖动边时,是否高亮显示所有可用的连接桩或节点,默认值为 false 。一般都会与 highlighting 联合使用。
    },
    //高亮器
    highlighting:{
      // 当连接桩可以被链接时,在连接桩外围渲染一个 2px 宽的红色矩形框
      magnetAvailable: {
        name: 'stroke',
        args: {
          padding: 4,
          attrs: {
            'stroke-width': 2,
            stroke: 'red',
          },
        },
      }
    }

  });
  //画布开启对齐线功能
  graph.value.use(new Snapline({
    enabled:true
  }))
  // 监听节点的鼠标进入事件,显示连接桩
  graph.value.on('node:mouseenter', () => {
    changePortsVisible(true);
  });
  // 节点点击事件
  graph.value.on('node:click', ({ node }) => {
    console.log('点击!!!', node);
    if (curSelectNode.value) {
      // 移除当前选中节点的工具
      curSelectNode.value.removeTools();

      if (curSelectNode.value !== node) {
        // 为当前点击的节点添加工具
        node.addTools([
          {
            name: 'boundary',
            args: {
              attrs: {
                fill: '#16B8AA',
                stroke: '#2F80EB',
                strokeWidth: 1,
                fillOpacity: 0.1,
              },
            },
          },
          {
            name: 'button-remove',
            args: {
              x: '100%',
              y: 0,
              offset: {
                x: 0,
                y: 0,
              },
            },
          },
        ]);
        curSelectNode.value = node; // 更新当前选中的节点
      } else {
        curSelectNode.value = null; // 如果点击相同节点,取消选中
      }
    } else {
      curSelectNode.value = node;
      // 添加工具到当前点击的节点
      node.addTools([
        {
          name: 'boundary',
          args: {
            attrs: {
              fill: '#16B8AA',
              stroke: '#2F80EB',
              strokeWidth: 1,
              fillOpacity: 0.1,
            },
          },
        },
        {
          name: 'button-remove',
          args: {
            x: '100%',
            y: 0,
            offset: {
              x: 0,
              y: 0,
            },
          },
        },
      ]);
    }
  });
  // 监听节点的鼠标离开事件,隐藏连接桩
  graph.value.on('node:mouseleave', () => {
    changePortsVisible(false);
  });

  // 监听连线悬浮进入事件
  graph.value.on('cell:mouseenter', ({ cell }) => {
    if (cell.shape === 'edge') {
      // 添加删除按钮工具
      cell.addTools([
        {
          name: 'button-remove',
          args: {
            x: '100%',
            y: 0,
            offset: {
              x: 0,
              y: 0,
            },
          },
        },
      ]);
      // 改变连线颜色
      cell.setAttrs({
        line: {
          stroke: '#409EFF',
        },
      });
      // 设置连线的层级,使其在最上层
      cell.setZIndex(99);
    }
  });

  // 监听连线悬浮离开事件
  graph.value.on('cell:mouseleave', ({ cell }) => {
    if (cell.shape === 'edge') {
      // 移除工具按钮
      cell.removeTools();
      // 恢复连线的颜色
      cell.setAttrs({
        line: {
          stroke: 'black',
        },
      });
      // 恢复连线的层级
      cell.setZIndex(1);
    }
  });
  // 将画布中元素缩小或者放大一定级别,让画布正好容纳所有元素,可以通过 maxScale 配置最大缩放级别
  // graph.value.zoomToFit({ maxScale: 4 })
});

3.设置节点删除与连线删除

声明:以下代码改写于CSDN博主:先知demons

直接添加至项目即可,开箱即用,博主还在研究addTools,解释敬请期待....

  const curSelectNode = ref<any>(null); // 当前选中的节点
//画布开启对齐线功能
  graph.value.use(new Snapline({
    enabled:true
  }))
  // 监听节点的鼠标进入事件,显示连接桩
  graph.value.on('node:mouseenter', () => {
    changePortsVisible(true);
  });
  // 节点点击事件
  graph.value.on('node:click', ({ node }) => {
    console.log('点击!!!', node);
    if (curSelectNode.value) {
      // 移除当前选中节点的工具
      curSelectNode.value.removeTools();

      if (curSelectNode.value !== node) {
        // 为当前点击的节点添加工具
        node.addTools([
          {
            name: 'boundary',
            args: {
              attrs: {
                fill: '#16B8AA',
                stroke: '#2F80EB',
                strokeWidth: 1,
                fillOpacity: 0.1,
              },
            },
          },
          {
            name: 'button-remove',
            args: {
              x: '100%',
              y: 0,
              offset: {
                x: 0,
                y: 0,
              },
            },
          },
        ]);
        curSelectNode.value = node; // 更新当前选中的节点
      } else {
        curSelectNode.value = null; // 如果点击相同节点,取消选中
      }
    } else {
      curSelectNode.value = node;
      // 添加工具到当前点击的节点
      node.addTools([
        {
          name: 'boundary',
          args: {
            attrs: {
              fill: '#16B8AA',
              stroke: '#2F80EB',
              strokeWidth: 1,
              fillOpacity: 0.1,
            },
          },
        },
        {
          name: 'button-remove',
          args: {
            x: '100%',
            y: 0,
            offset: {
              x: 0,
              y: 0,
            },
          },
        },
      ]);
    }
  });
  // 监听节点的鼠标离开事件,隐藏连接桩
  graph.value.on('node:mouseleave', () => {
    changePortsVisible(false);
  });

  // 监听连线悬浮进入事件
  graph.value.on('cell:mouseenter', ({ cell }) => {
    if (cell.shape === 'edge') {
      // 添加删除按钮工具
      cell.addTools([
        {
          name: 'button-remove',
          args: {
            x: '100%',
            y: 0,
            offset: {
              x: 0,
              y: 0,
            },
          },
        },
      ]);
      // 改变连线颜色
      cell.setAttrs({
        line: {
          stroke: '#409EFF',
        },
      });
      // 设置连线的层级,使其在最上层
      cell.setZIndex(99);
    }
  });

  // 监听连线悬浮离开事件
  graph.value.on('cell:mouseleave', ({ cell }) => {
    if (cell.shape === 'edge') {
      // 移除工具按钮
      cell.removeTools();
      // 恢复连线的颜色
      cell.setAttrs({
        line: {
          stroke: 'black',
        },
      });
      // 恢复连线的层级
      cell.setZIndex(1);
    }
  });

技术与工具分析:

工具一:对齐线工具

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

实现步骤 :根据官方的描述,对齐线是移动节点排版的辅助工具,我们提供了一个独立的插件包

@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')!, // 断言该值不为 null
    grid: {
      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。

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


本文转载自: https://blog.csdn.net/qq_58055766/article/details/142909427
版权归原作者 I like Code? 所有, 如有侵权,请联系我们删除。

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

还没有评论