antv x6连接桩hover效果(使用element-plus Tooltip组件实现展示连接桩名字)

因为官网的演示案例是react+antd,而我的是vue3+element,实现效果如下图


具体实现代码如下,在实例化X6的时候通过onPortRendered这个钩子函数里面使用createVNode去给每一个连接桩节点添加自定义的dom,在结合element UI的组件去使用

1首先引入需要用到的函数

import { ref, onMounted, createVNode, render } from "vue";
import { Graph, Shape, Markup } from "@antv/x6";
import { ElTooltip } from "element-plus";

2在创建节点的时候,使用 portMarkup: [Markup.getForeignObjectMarkup()],portMarkup 是 AntV X6 中用于定义连接桩(ports)外观的属性。使用 Markup.getForeignObjectMarkup() 可以将连接桩定义为 foreignObject,这样可以在连接桩中嵌入任意的 HTML 或 SVG 内容

 const srcList = [
    "https://fuss10.elemecdn.com/a/3f/3302e58f9a181d2509f3dc0fa68b0jpeg.jpeg",
    "https://fuss10.elemecdn.com/1/34/19aa98b1fcb2781c4fba33d850549jpeg.jpeg",
    "https://fuss10.elemecdn.com/0/6f/e35ff375812e6b0020b6b4e8f9583jpeg.jpeg",
    "https://fuss10.elemecdn.com/9/bb/e27858e973f5d7d3904835f46abbdjpeg.jpeg",
    "https://fuss10.elemecdn.com/d/e6/c4d93a3805b3ce3f323f7974e6f78jpeg.jpeg",
    "https://fuss10.elemecdn.com/3/28/bbf893f792f03a54408b3b7a7ebf0jpeg.jpeg",
    "https://fuss10.elemecdn.com/2/11/6535bcfb26e4c79b48ddde44f4b6fjpeg.jpeg",
  ];
  const imgNodes = srcList.map((item) => {
    return graph.value.createNode({
      shape: "image", //可选值:Rect Circle Ellipse Polygon Polyline Path Image HTML TextBlock BorderedImage EmbeddedImage InscribedImage Cylinder
      imageUrl: item,
      attrs: {
        body: {
          fill: "#f5f5f5",
          stroke: "#d9d9d9",
          strokeWidth: 1,
        },
      },
      width: 100,
      height: 60,
      portMarkup: [Markup.getForeignObjectMarkup()],
      ports: { ...customPorts },
    });
  });

3在配置连接桩时不要使用circle,要使用fo,具体配置如图

4 在onPortRendered钩子函数里添加如下代码

onPortRendered(args) {
      const selectors = args.contentSelectors;
      const container = selectors && selectors.foContent;
      if (container) {
        const portName = args.port.label && args.port.label.text;
        const dom = createVNode(
          ElTooltip,
          {
            effect: "dark",
            content: portName,
            placement: "top-start",
          },
          [
            createVNode("div", {
              class: "createVNode",
            }),
          ]
        );
        render(dom, container);
      }
//记得添加样式 
::v-deep .createVNode {
  width: 100%;
  height: 100%;
  border: 2px solid #31d0c6;
  border-radius: 100%;
  background: #fff;
  box-sizing: border-box;
}

完结

完整代码如下

<template>
  <div class="home">
    <div class="left" id="stencil"></div>
    <div class="right" id="container"></div>
    <div class="tool"></div>
  </div>
</template>
<script setup>
import { ref, onMounted, createVNode, render } from "vue";
import { Graph, Shape, Markup } from "@antv/x6";
import { Stencil } from "@antv/x6-plugin-stencil";
import { Snapline } from "@antv/x6-plugin-snapline";
import { basicPorts, customPorts } from "./ports";
import { NodeGroup } from "./shape";
import { ElMessage, ElTooltip } from "element-plus";
const stencil = ref(null);
const graph = ref(null);
onMounted(() => {
  initGraph();
});
const initGraph = () => {
  const container = document.getElementById("container");
  graph.value = new Graph({
    container: container,
    width: container.offsetWidth,
    height: container.offsetHeight,
    autoResize: true,
    background: {
      color: "#fff", // 设置画布背景颜色
    },
    grid: {
      size: 10, // 网格大小
      visible: true, // 是否显示网格
      type: "doubleMesh", // 网格类型,双网格
      args: [
        {
          color: "#cccccc", // 网格颜色
          thickness: 1, // 网格线条宽度
        },
      ],
    },
    scroller: {
      enabled: false, // 是否启用滚动
      pageVisible: false, // 是否显示分页
      pageBreak: false, // 是否显示分页断开线
      pannable: false, // 是否允许拖动画布
    },
    mousewheel: {
      enabled: true, // 是否启用鼠标滚轮缩放
      modifiers: ["ctrl", "meta"], // 按下哪些键可以使用滚轮缩放
      minScale: 0.5, // 最小缩放比例
      maxScale: 2, // 最大缩放比例
    },
    connecting: {
      anchor: "center", // 连接点位置
      connectionPoint: "anchor", // 连接点类型
      allowBlank: true, // 允许连接到空白处
      highlight: true, // 高亮可连接的连接点
      snap: true, // 是否自动吸附
      allowMulti: true, // 是否允许相同起始和终止节点之间创建多条边
      allowNode: false, // 是否允许连接到节点
      allowBlank: false, // 是否允许连接到空白点
      allowLoop: false, // 是否允许创建循环连线
      allowEdge: false, // 是否允许连接到另一个边
      highlight: true, // 是否高亮显示可用连接点或节点
      connectionPoint: "anchor", // 连接点类型
      anchor: "center", // 锚点位置
      createEdge() {
        // X6 的 Shape 命名空间中内置 Edge、DoubleEdge、ShadowEdge 三种边
        return new Shape.Edge({
          attrs: {
            line: {
              stroke: "#000",
              strokeWidth: 2,
              strokeDasharray: null,
              targetMarker: null,
            },
            // outline: {
            //   stroke: "#73d13d",
            //   strokeWidth: 15,
            // },
          },
          router: {
            name: "manhattan",
          },
        });
      },

      validateConnection({
        sourceView,
        targetView,
        sourceMagnet,
        targetMagnet,
      }) {
        if (sourceView === targetView) {
          return false; // 禁止自身连接
        }
        if (!sourceMagnet) {
          return false; // 禁止无连接点的起始连接
        }
        if (!targetMagnet) {
          return false; // 禁止无连接点的终止连接
        }
        return true;
      },
    },
    onPortRendered(args) {
      const selectors = args.contentSelectors;
      const container = selectors && selectors.foContent;
      if (container) {
        const portName = args.port.label && args.port.label.text;
        const dom = createVNode(
          ElTooltip,
          {
            effect: "dark",
            content: portName,
            placement: "top-start",
          },
          [
            createVNode("div", {
              class: "createVNode",
            }),
          ]
        );
        render(dom, container);
      }
      console.log(container);
    },
    panning: {
      enabled: true, // 是否启用画布拖拽平移
      modifiers: "shift", // 按住 shift 键才能平移画布
    },
    resizing: true, // 是否启用节点缩放
    rotating: true, // 是否启用节点旋转
    selecting: {
      enabled: true, // 是否启用选择功能
      multiple: true, // 是否允许多选
      rubberband: true, // 是否启用框选
      movable: true, // 是否允许移动
      showNodeSelectionBox: true, // 是否显示节点选择框
    },
    snapline: true, // 是否启用对齐线
    history: true, // 是否启用历史记录
    clipboard: {
      enabled: true, // 是否启用剪贴板功能
    },
    keyboard: {
      enabled: true, // 是否启用键盘快捷键
    },
  });
  graph.value.use(
    new Snapline({
      enabled: true,
    })
  );
  initStencil();
  // nodeAddEvent();
};
const initStencil = () => {
  const stencilDom = document.getElementById("stencil");
  stencil.value = new Stencil({
    title: "选择栏",
    target: graph.value,
    stencilGraphWidth: 280,
    collapsable: true,
    groups: [
      {
        name: "basic",
        title: "基础节点",
        graphHeight: 180,
      },
      {
        name: "custom-image",
        title: "系统设计图",
        graphHeight: 400,
      },
      {
        name: "dian-image",
        title: "KM",
        graphHeight: 600,
      },
    ],
    container: stencilDom,
  });
  stencilDom.appendChild(stencil.value.container);

  // stencil.value.load([rect1], "custom-image");
  initNode();
};
const initNode = () => {
  // 基础节点
  const r1 = graph.value.createNode({
    shape: "flow-chart-rect",
    attrs: {
      body: {
        rx: 24,
        ry: 24,
      },
      text: {
        text: "起始节点",
      },
    },
  });
  const r2 = graph.value.createNode({
    shape: "flow-chart-rect",
    attrs: {
      text: {
        text: "流程节点",
      },
    },
  });
  const r3 = graph.value.createNode({
    shape: "flow-chart-rect",
    width: 52,
    height: 52,
    angle: 45,
    attrs: {
      "edit-text": {
        style: {
          transform: "rotate(-45deg)",
        },
      },
      text: {
        text: "判断节点",
        transform: "rotate(-45deg)",
      },
    },
    ports: {
      groups: {
        top: {
          position: {
            name: "top",
            args: {
              dx: -26,
            },
          },
        },
        right: {
          position: {
            name: "right",
            args: {
              dy: -26,
            },
          },
        },
        bottom: {
          position: {
            name: "bottom",
            args: {
              dx: 26,
            },
          },
        },
        left: {
          position: {
            name: "left",
            args: {
              dy: 26,
            },
          },
        },
      },
    },
  });
  const r4 = graph.value.createNode({
    shape: "flow-chart-rect",
    width: 70,
    height: 70,
    attrs: {
      body: {
        rx: 35,
        ry: 35,
      },
      text: {
        text: "链接节点",
      },
    },
  });
  const srcList = [
    "https://fuss10.elemecdn.com/a/3f/3302e58f9a181d2509f3dc0fa68b0jpeg.jpeg",
    "https://fuss10.elemecdn.com/1/34/19aa98b1fcb2781c4fba33d850549jpeg.jpeg",
    "https://fuss10.elemecdn.com/0/6f/e35ff375812e6b0020b6b4e8f9583jpeg.jpeg",
    "https://fuss10.elemecdn.com/9/bb/e27858e973f5d7d3904835f46abbdjpeg.jpeg",
    "https://fuss10.elemecdn.com/d/e6/c4d93a3805b3ce3f323f7974e6f78jpeg.jpeg",
    "https://fuss10.elemecdn.com/3/28/bbf893f792f03a54408b3b7a7ebf0jpeg.jpeg",
    "https://fuss10.elemecdn.com/2/11/6535bcfb26e4c79b48ddde44f4b6fjpeg.jpeg",
  ];
  const imgNodes = srcList.map((item) => {
    return graph.value.createNode({
      shape: "image", //可选值:Rect Circle Ellipse Polygon Polyline Path Image HTML TextBlock BorderedImage EmbeddedImage InscribedImage Cylinder
      imageUrl: item,
      attrs: {
        body: {
          fill: "#f5f5f5",
          stroke: "#d9d9d9",
          strokeWidth: 1,
        },
      },
      width: 100,
      height: 60,
      portMarkup: [Markup.getForeignObjectMarkup()],
      ports: { ...customPorts },
    });
  });
  stencil.value.load([r1, r2, r3, r4], "basic");
  stencil.value.load(imgNodes, "custom-image");
  // 节点被添加到画布显示连接桩
  graph.value.on("node:added", ({ node }) => {
    const ports = node.getPorts();
    ports.forEach((port) => {
      node.portProp(port.id, "attrs/circle/style/visibility", "visible");
    });
  });
};
const edgeCrlorDom = ref(null);
const curSelectNode = ref(null);
const nodeAddEvent = () => {
  graph.value.on("node:port:click", ({ e, node, view }) => {
    e.stopPropagation();
  });
  graph.value.on("node:click", ({ e, x, y, node, view }) => {
    if (e.target.classList.contains("x6-port-body")) {
      return;
    }
    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: "#000",
              stroke: "#000",
              strokeWidth: 1,
              fillOpacity: 0.1,
            },
          },
        },
        {
          name: "button-remove",
          args: {
            x: "100%",
            y: 0,
            offset: {
              x: 0,
              y: 0,
            },
          },
        },
      ]);
    }
  });
  // 连线绑定悬浮事件显示删除
  graph.value.on("cell:mouseenter", ({ cell }) => {
    if (cell.shape == "edge") {
      cell.addTools([
        {
          name: "vertices",
          // args: {
          //   attrs: { fill: "#8f8f8f" },
          // },
        },
        {
          name: "segments",
          // args: {
          //   snapRadius: 20,
          //   attrs: { fill: "#8f8f8f" },
          // },
        },
        {
          name: "button-remove",
          args: {
            x: "100%",
            y: 0,
            offset: { x: 0, y: 0 },
          },
        },
        {
          name: "button",
          args: {
            markup: [
              {
                tagName: "circle",
                selector: "button",
                attrs: {
                  r: 5,
                  stroke: "#fe854f",
                  "stroke-width": 2,
                  fill: "white",
                  cursor: "pointer",
                },
              },
              {
                tagName: "text",
                textContent: "点击换色",
                selector: "icon",
                attrs: {
                  fill: "#fe854f",
                  "font-size": 5,
                  "text-anchor": "middle",
                  "pointer-events": "none",
                  y: "0.3em",
                },
              },
            ],
            distance: -40,
            onClick({ view }) {
              const edge = view.cell;
              edgeCrlorDom.value = null;
              edgeCrlorDom.value = edge;
              drawer.value = true;
            },
          },
        },
      ]);

      cell.setAttrs({
        line: {
          stroke: "#409EFF",
        },
      });

      cell.zIndex = 99; // 保证当前悬停的线在最上层,不会被遮挡
    }
  });

  graph.value.on("cell:mouseleave", ({ cell }) => {
    if (cell.shape === "edge") {
      cell.removeTools();
      cell.setAttrs({
        line: {
          // stroke: radio.value ? `${radio.value}` : "black",
          stroke: "black",
        },
      });
      cell.zIndex = 100; // 保证未悬停的线在下层,不会遮挡悬停的线
    }
  });
};
</script>
<style scoped>
.home {
  color: #000;
  display: flex;
  height: 100%;
}
.left {
  width: 280px;
  height: 100%;
  background-color: aqua;
  overflow: auto;
  position: relative;
  border-right: 1px solid rgba(0, 0, 0, 0.08);
  box-sizing: border-box;
}
.right {
  flex: 1;
  height: 100%;
}
.tool {
  width: 280px;
  height: 100%;
  
}
::v-deep .createVNode {
  width: 100%;
  height: 100%;
  border: 2px solid #31d0c6;
  border-radius: 100%;
  background: #fff;
  box-sizing: border-box;
}
</style>

  • 12
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值