Antv/X6

这里的背景是以vue3.2+antv/x6 2.8 为背景的学习,官网的文档是使用react书写的案例,需要摸索的地方还是有很多,下面开始

前提

假设你已经创建了vue ,如果不清楚怎么创建vue项目,建议前往官网学习Vue。使用组合式API开发,为了简便,没有使用路由等东西,就是使用TS + Less,但是 TS 使用的很烂,有问题拜托指出,谢谢。

快速上手

接下来我们就开始使用

安装 X6

# npm
$ npm install @antv/x6 --save

# yarn
$ yarn add @antv/x6

初始化画布

创建容器

首先我们要创建我们的画布容器,我们使用了idcontainerdiv来当我们的容器

<template>
  <div class="graph">
    <!-- 1.创建容器 -->
    <div id="container" class="container"></div>
  </div>
</template>

引入 antv/x6

<script setup lang="ts">
// 1.引入vue和antv/x6
import { ref, onMounted } from "vue";
import { Graph } from "@antv/x6";

// 2.定义一个变量来接受这个
const graph = ref();
// 3. 在 onMounted 生命周期中才能访问到DOM节点,需要注意一下。
onMounted(() => {
  // 4.定义画布
  graph.value = new Graph({
    container: document.getElementById("container") as HTMLElement,
    width: 800,
    height: 600,
    background: {
      color: "#F2F7FA", // 设置画布的背景颜色。
    },
  });
});
</script>

在上面的代码块中,我们定义了一个画布,以下对一些参数做一些解释。

  • container,目标容器
  • width,画布的宽度
  • height,画布的高度
  • background,画布的背景,比如这里我们设置了背景的颜色

到这里我们应该在浏览器中就有一个画布了。

image-20230405223008142

渲染节点和边

X6 支持 JSON 格式数据,该对象中 nodes 代表节点数据,edges 代表边数据,可以使用 attrs 属性来定制节点和边的样式(可以类比 CSS)。

定义节点和边的信息

<script setup lang="ts">
// 定义节点和边的信息
const data = {
  nodes: [
    {
      id: "node1",
      shape: "rect",
      x: 40,
      y: 40,
      width: 100,
      height: 40,
      label: "hello",
      attrs: {
        // body 是选择器名称,选中的是 rect 元素
        body: {
          stroke: "#8f8f8f",
          strokeWidth: 1,
          fill: "#fff",
          rx: 6,
          ry: 6,
        },
      },
    },
    {
      id: "node2",
      shape: "rect",
      x: 160,
      y: 180,
      width: 100,
      height: 40,
      label: "world",
      attrs: {
        body: {
          stroke: "#8f8f8f",
          strokeWidth: 1,
          fill: "#fff",
          rx: 6,
          ry: 6,
        },
      },
    },
  ],
  edges: [
    {
      shape: "edge",
      source: "node1",
      target: "node2",
      label: "x6",
      attrs: {
        // line 是选择器名称,选中的边的 path 元素
        line: {
          stroke: "#8f8f8f",
          strokeWidth: 1,
        },
      },
    },
  ],
};
</script>

下面我们来解释一下上面一坨代码的含义,其实大部分内容是重复的。我们从节点(nodes)开始.

nodes 解释
  • id,节点的唯一标识,如果不指定节点,那么 x6 会自动给我们生成 ID。
  • shape,指定节点的图形,x6 给我们提供了很多的内置节点,比如我们这里使用的就是矩形。
  • x,x 轴,在画布中水平向右为 x 轴正方向,垂直 x 轴向下为 y 轴正方向。此处指的就是节点在画布中 x 轴的位置。
  • y,在画布中 y 轴的位置。
  • width,节点的宽度
  • height,节点的高度
  • label,节点中间的描述性文字
  • attrs,可以理解为css,是一个对象。比如我们这里body是选择器的名称,选中的是 rect 元素
    • stroke,节点的矩形框描边颜色
    • strokeWidth,节点的矩形框描边宽度
    • fill,节点的矩形框填充颜色
    • rx,节点的矩形直角的边框圆角水平半径
    • ry,节点的矩形直角的边框圆角垂直半径
edges 解释
  • shape,指定边的类型

  • source,源节点或者起始节点

  • target,目标节点

  • label,标签,或者简单说就是边上的文字

  • attrsline 是选择器名称,选中的边的 path 元素

渲染

当我们定义好节点之后,我们就要开始渲染了。因为在Vue3之中,在onMounted之中才能拿到 Dom 节点,所以我们也需要再onMounted之中,才能拿到 Dom 赋值。

<script setup lang="ts">
// 1.引入vue和antv/x6
import { ref, onMounted } from "vue";
import { Graph } from "@antv/x6";

// 2.定义一个变量来接受这个
const graph = ref();
// 3. 在 onMounted 生命周期中才能访问到DOM节点,需要注意一下。
onMounted(() => {
  // 4.定义画布
  graph.value = new Graph({
    container: document.getElementById("container") as HTMLElement,
    width: 800,
    height: 600,
    background: {
      color: "#F2F7FA", // 设置画布的背景颜色。
    },
  });
  // 5.加载数据
  graph.value.fromJSON(data);
});
</script>

然后去看浏览器,应该会有如下的画面:

image-20230409141559243

我们在上面使用的时候,看到graph.value.fromJSON(data);这种用法,总是先.value之后再使用对应的方法,其实这个是因为我们创建变量的时候,使用的是ref创建的变量,使用ref创建的变量,只要不是在模版中使用,都是需要.value的。

到这里,快速上手就完毕了,就是这么简单。

画布

画布布是在 X6 中用于展示和编辑图形的区域,类似于绘图板的概念。通过画布,可以创建、移动、编辑和删除图形,包括节点、连接线、文本等。

画布自适应

我们在实例化Graph对象的时候,可以通过设置 widthheight 固定画布大小,如果不设置,就会以画布容器大小初始化画布。但是我们有可能会遇到页面的 resize 事件,导致画布容器大小改变,导致画布元素显示异常。

resize 事件是 JavaScript 中一个针对浏览器窗口大小改变的事件。当用户调整浏览器窗口大小时,浏览器会触发 resize 事件,允许开发者在窗口大小改变时执行相关的代码。

window.addEventListener("resize", function () {
  // 处理窗口大小改变事件
});

这时候,我们就需要设置autoResize来解决。

<script setup lang="ts">
// 1.引入vue和antv/x6
import { ref, onMounted } from "vue";
import { Graph } from "@antv/x6";

// 2.定义一个变量来接受这个
const graph = ref();
// 3. 在 onMounted 生命周期中才能访问到DOM节点,需要注意一下。
onMounted(() => {
  // 4.定义画布
  const graph = new Graph({
    container: document.getElementById("container"),
    autoResize: true,
  });
  // 5.加载数据
  graph.value.fromJSON(data);
});
</script>

设置好之后,查看效果

image-20230410203658420

当我们拉动浏览器的宽度的时候,我们发现,画布左右侧始终有一定的空间,画布会自适应变化宽度。

设置背景和网格

antv/x6之中,可以通过 backgroundgrid 两个配置来设置画布的背景以及网格。

背景

首先我们来看背景,他有多种情况。

color

背景颜色,支持所有 CSS background-color 属性的取值,如:

  • 'red',颜色关键字
  • '#f5f5f5',十六进制符号
  • 'rgba(255, 255, 128, 0.5)'函数符,红绿蓝透明度
  • 'hsla(50, 33%, 25%, 0.75)'

HSL圆柱坐标系统是指一种颜色空间表示方式,其中 HSL 代表色相、饱和度和亮度,圆柱坐标则表示这些参数的变化。

​ 在HSL圆柱坐标系统中,色相是指颜色的种类或类别,如红色、黄色等,它沿着一个 360 度的色轮上变化。饱和度表示颜色的强度或纯度,取值范围为 0 到 1,0 表示灰度,1 表示完全饱和。亮度则表示颜色的亮暗程度,取值范围同样为 0 到 1,0 表示黑色,1 表示白色。最后一个值也是透明度,取值范围 0 到 1。

  • 'radial-gradient(shape size at position, red, green)'

radial-gradientCSS3中用来创建径向渐变效果的函数,使用 radial-gradient 函数可以创建从一个中心点向周围扩散的渐变效果。

shape可以是circle(圆形)或ellipse(椭圆形),size可以是closest-side(最近的边)、closest-corner(最近的角)、farthest-side(最远的边)或farthest-corner(最远的角),position表示渐变的中心点位置(可以是一个像素值或一个百分比),start-color表示起始颜色,last-color表示终止颜色。

比如:

background: radial-gradient(circle, #ff9d9d, #fe2d2d);

这将创建一个从中心向外扩散的圆形渐变效果,起始颜色为#ff9d9d,终止颜色为#fe2d2d

image

背景图片的 URL 地址,这里需要值得注意的我们需要先导入,然后再使用

<script setup lang="ts">
import bgcImage from "@/antv-x6/bg2.b4f49dec.png";

onMounted(() => {
  graph.value = new Graph({
    container: document.getElementById("container") as HTMLElement,
    width: 800,
    height: 600,
    background: {
      image: bgcImage,
    },
    autoResize: true,
  });
  graph.value.fromJSON(data);
});
</script>
position

背景图片位置,支持所有 CSS background-position 属性的取值,默认为 'center'

size

背景图片大小,支持所有 CSS background-size 属性的取值,默认为 'auto auto'

repeat

背景图片重复方式,支持所有 CSS background-repeat 属性的取值,默认为 'no-repeat' 不平铺

另外,还支持以下几个预定义值:

画布拖拽(平移)和缩放

画布的拖拽和缩放通过 panningmousewheel 配置来实现这两个功能

const graph = new Graph({
  ...,
  panning: true,
  mousewheel: true
})

节点

X6 是基于 SVG 的渲染引擎,可以使用不同的 SVG 元素渲染节点和边,非常适合节点内容比较简单的场景。如果我们需要复杂的场景,那么我们可以通过vue节点来实现。

添加节点

我们在最初的时候在setup中定义了一个graph属性,这个graph我们通过new创建了画布的对象。新增节点可以通过addNode方法,传入一个对象。

graph.value.addNode({
  shape: "rect",
  label: "矩形",
  x: 100,
  y: 40,
  width: 100,
  height: 40,
});
  • shape,节点的图形
  • x,在画布中 x 轴的的位置
  • y,在画布中的 y 轴的位置
  • width,元素的宽度
  • height,元素的高度
  • label,矩形中间的文字

内置节点

Antv/X6之中,也内置了很多节点,比如圆形,椭圆,矩形等等。

shape 名称 描述
rect 矩形
circle 圆形
ellipse 椭圆
polygon 多边形
polyline 折线
path 路径
image 图片
html HTML 节点,使用 foreignObject 渲染 HTML 片段。

矩形

graph.value.addNode({
  shape: "rect",
  label: "矩形",
  x: 100,
  y: 40,
  width: 100,
  height: 40,
});

image-20230412202630050

圆形

graph.value.addNode({
  shape: "circle",
  label: "矩形",
  x: 100,
  y: 40,
  width: 100,
  height: 40,
});

image-20230412202717215

定制节点

可以通过 markupattrs 来定制节点的形状和样式,markup 可以类比 HTMLattrs 类比 CSS。强烈建议仔细阅读 markupattrs 文档。

但是个人认为这个适合做一些简单的节点,如果复杂的节点,推荐使用vue节点,后面会介绍到。

修改节点

在我们创建完成之后,还能够对节点进行一定的修改,我们还可以通过 API 修改节点的所有属性。我们会常用到下面两个方法:

  • node.prop(path, value),详细使用见 prop
  • node.attr(path, value),详细使用见 attr
const node = graph.addNode({
  shape: "rect",
  width: 100,
  height: 40,
  x: 100,
  y: 100,
  label: "edge",
});
console.log(node.prop());

比如:

source.prop("size", { width: 120, height: 50 }); // 修改元素的宽高
source.attr("rect/fill", "#ccc"); // 修改填充色,等价于 source.prop('attrs/rect/fill', '#ccc')

通常情况下,真实场景并不会像上面的示例,还给节点一个单独的名称,在实际过程中,我们一般是配合事件一起来进行使用,比如我们选中某个节点想要设置其边框颜色,他会自动监听画布中的点击事件,然后去进行对应的操作。

graph.value.on("cell:click", ({ e, x, y, cell, view }) => {
  // cell 就是我们所需要的节点对象
  cell.attr("body/stroke", "red");
});

或者说还有情况就是给画布中的所有对象都设置

const nodes = this.graph.getNodes(); // 获取画布中的所有节点
nodes.forEach((node) => {
  const color = Color.random().toHex();
  node.attr("body/fill", color);
});

如何添加边

graph.value.addEdge({
  shape: "edge",
  source: "node1",
  target: "node2",
});
属性名 类型 默认值 描述
source TerminalData - 源节点或起始点。其与节点的 ID 相同
target TerminalData - 目标节点或目标点。其与节点的 ID 相同
vertices Point.PointLike[] - 路径点。
router RouterData - 路由。
connector ConnectorData - 连接器。
labels Label[] - 标签。
defaultLabel Label 默认标签 默认标签。

上面代码就是添加边的方法,sourcetarget的值需要与节点的ID一致就可以将两个节点进行连接,如果你只是看看能不能创建边,其实还有种写法

graph.value.addEdge({
  shape: "edge",
  source: [100, 100],
  target: [400, 100],
});

数组代表的是x轴和y轴。

image-20230417162450440

vertices

路径点。边从起始点开始,按顺序经过路径点,最后到达终止点。

graph.value.addEdge({
  source: "rect1",
  target: "rect2",
  vertices: [
    { x: 100, y: 200 },
    { x: 300, y: 120 },
  ],
});

image-20230417162610575

router

路由 router 将对 vertices 进一步处理,并在必要时添加额外的点,然后返回处理后的点。

orth

正交路由,该路由在路径上添加额外的一些点,使边的每一条线段都水平或垂直正交。

graph.value.addEdge({
  source,
  target,
  vertices: [
    { x: 100, y: 200 },
    { x: 300, y: 120 },
  ],
  router: {
    name: "orth",
    args: {
      padding: {
        left: 50,
      },
    },
  },
});

image-20230417163941802

那两个圆圈的地方就是我们自己设置的vertices,其他的都是定义orth路由,自动生成的。

其他的版本可以看官网

manhattan

曼哈顿路由 'manhattan' 路由是正交路由 'orth' 的智能版本,该路由由水平或垂直的正交线段组成,并自动避开路径上的其他节点(障碍)。

同时需要注意的是,manhattan 路由的特性是自动避开路径中的障碍物,如果出现无法避开的情况,就会自动降级到 orth 路由,此时为了让开发者能够发现问题,在控制台增加了 warn:Unable to execute manhattan algorithm, use orth instead

这个在最新的官网已经注明了。

如果你看着警告不爽,解决办法就是忽略参与计算的节点,但是这样就相当于没有manhattan就白用了一样。看自己选择吧。

labels 和 defaultLabel
graph.addEdge({
      source,
      target,
      attrs: {
        line: {
          stroke: '#8f8f8f',
          strokeWidth: 1,
        },
      },
      labels: [
        {
          attrs: {
            label: {
              text: '40%', // 字体
              stroke: '#aaa', // 颜色
            },
          },
          position: 0.4, // 在线上的位置
        },
        {
          attrs: {
            label: {
              text: '60%',
              stroke: '#aaa',
            },
          },
          position: 0.6,
        },
      ],
    })
  }

image-20230417164818737

定制边

基类派生

定制箭头有两种方式,一种是使用从基类派生出我们的边,并重写某些样式和方法。详细可以看antv/x6 1.0 版本,在 2.0 版本中没有明确说明这种方式。

import { Shape } from "@antv/x6";

//1. 派生出自己的边样式
const MyEdge = Shape.Edge({
  markup: [
    {
      tagName: "path",
      selector: "wrap",
      attrs: {
        fill: "none",
        cursor: "pointer",
        stroke: "transparent",
        strokeLinecap: "round",
      },
    },
    {
      tagName: "path",
      selector: "line",
      attrs: {
        fill: "none",
        pointerEvents: "none",
      },
    },
  ],
  attrs: {
    wrap: {
      connection: true,
      strokeWidth: 10,
      strokeLinejoin: "round",
    },
    line: {
      connection: true,
      stroke: "#333333",
      strokeWidth: 2,
      strokeLinejoin: "round",
      targetMarker: {
        tagName: "path",
        d: "M 10 -5 0 0 10 5 z",
      },
    },
  },
});
//2. 将边注册,这里用的就是Graph,不是实例后的画布对象 第一个参数是自定义的,只要在创建边的时候对应就行
Graph.registerEdge("edge1", MyEdge);

//3. 使用
graph.addEdge({
  shape: "edge1",
  source,
  target,
});

箭头

对于边两端的箭头,antv/x6提供了内置箭头和自定义箭头两种方式

内置箭头

antv/x6提供了以下的几种箭头。

实心箭头(block)、经典箭头(classic)、菱形箭头(diamond)、交叉箭头(cross)、async、path、圆形箭头(circle)、圆形和加号箭头(circlePlus)、椭圆箭头(elipse)

定制箭头

工厂方法

Graph原型上有个registerMarker方法,可以帮助我们自定义箭头

// 1.注册
Graph.registerMarker("image", (args: ImageMarkerArgs) => {
  const { imageUrl, imageWidth, imageHeight, ...attrs } = args;
  return {
    ...attrs, // 原样返回非特殊涵义的参数
    tagName: "image", // 使用 <image> 标签渲染箭头,其余键值对都将作为该元素的属性。
    width: imageWidth,
    height: imageHeight,
    "xlink:href": imageUrl,
  };
});

//2. 使用
edge.attr({
  line: {
    sourceMarker: {
      name: "image",
      imageUrl:
        "http://cdn3.iconfinder.com/data/icons/49handdrawing/24x24/left.png",
      imageWidth: 24,
      imageHeight: 24,
      y: -12,
    },
    targetMarker: {
      name: "image",
      imageUrl:
        "http://cdn3.iconfinder.com/data/icons/49handdrawing/24x24/left.png",
      imageWidth: 24,
      imageHeight: 24,
      y: -12,
    },
  },
});
直接生成

我们可以通过实例对象上的暴露的方法直接生成边的同时,指定对应的样式,起始箭头和终止箭头使用了相同的 d 属性,这是因为x6会自动计算箭头方向,简单来说,在定义箭头时,只需要定义一个向左指向坐标原点的箭头即可。

graph.addEdge({
  shape: "edge",
  sourece: [100, 100],
  target: [500, 500],
  attrs: {
    line: {
      sourceMarker: {
        tagName: "path",
        d: "M 20 -10 0 0 20 10 Z",
      },
      targetMarker: {
        tagName: "path",
        fill: "yellow", // 使用自定义填充色
        stroke: "green", // 使用自定义边框色
        strokeWidth: 2,
        d: "M 20 -10 0 0 20 10 Z",
      },
    },
  },
});

连接桩


文章作者: 小笨
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 小笨 !
评论
  目录