Antv G6 拖拽到已有节点上创建新节点,并避免节点重复

Antv G6 拖拽到已有节点上创建新节点,并避免节点重复

G6

使用 API

  • G6 坐标转换graph.getPointByClient(clientX, clientY)(x, y)
    可以将屏幕/页面坐标转换为渲染也就是画布坐标。

  • HTML 拖放 API:通过设置 HTMLdraggable 属性为 true,使元素可以被拖拽。这里可使用该 API 的 dragend 事件(当拖拽操作结束时触发),生成对应节点。

    例如,用户可使用鼠标选择可拖拽元素,将元素拖拽到可放置元素,并释放鼠标按钮以放置这些元素。拖拽操作期间,会有一个可拖拽元素的半透明快照跟随着鼠标指针。

  • G6 生成新节点graph.addItem(type, model, stack)
    新增元素(节点和边)。
    注意:将会直接使用 model 对象作为新增元素的数据模型,G6 内部可能会对其增加或修改一些必要的字段。若不希望原始参数被修改,建议在使用深拷贝后的 model

  • G6 查找节点graph.find(type, fn)
    根据具体规则查找单个元素。如果有符合规则的元素实例,则返回第一个匹配的元素实例,否则返回 undefined

    const findNode = graph.find('node', (node) => {
      return node.get('model').x === 100;
    });
    

步骤

  • 初始化 G6;
  • 拖拽按钮到已有节点上(:draggable="true");
  • 监听拖拽结束事件(@dragend="addNode(item.type, $event)");
  • 将页面坐标转换为拓扑区域坐标(this.graph.getPointByClient(e.x, e.y));
  • 获取被挂载的节点数据;
  • 获取新节点的坐标,并尽可能避免重复;
  • 生成新节点(this.graph.addItem('node', node); this.graph.addItem('edge', edge);

代码

<template>
  <div class="page">
    ...
    <!-- 图表区域 -->
    <div class="Graph-wrapper">
      <!-- 拖拽按钮 -->
      <div
        v-for="item in addNodeBtns"
        :key="item.type"
        class="item" 
       >
         <div 
           :class="['item-movement', item]"
           :draggable="true"
           @dragend="addNode(item.type, $event)"
         />
         <div>{{ item.name }}</div>
      </div>
      <!-- 拓扑图 -->
      <div id="graphContainer" style="width: 100%; height: 100%; position: absolute" />
    </div>
  </div>
</tempalte>

<script>
import G6 from "@antv/g6";

export default {
  name: 'Test',
  data() {
    return {
      ...
      addNodeBtns: [
        { type: 'a', name: 'testA' },
        { type: 'b', name: 'testB' },
      ],
    };
  },
  methods: {
    initGraph(data) {
      const container = document.getElementById('graphContainer');
      const graph = new G6.Graph({
	    container: 'graphContainer', // 图的 DOM 容器,可以传入该 DOM 的 id 或者直接传入容器的 HTML 节点对象。
	    width: container.clientWidth,
	    height: container.clientHeight,
	    animate: true, // 是否启用全局动画。
	    fitCenter: true, // 开启后,图将会被平移,图的中心将对齐到画布中心,但不缩放。优先级低于 fitView。
	    modes: {
	      default: ['drag-canvas', 'zoom-canvas', 'drag-node'],
	    },
	    layout: {
          type: 'dagre', // 层次布局。
          rankdir: 'LR', // 可选,布局的方向, 从左至右布局(默认为图的中心)。
          align: 'DL', // 可选,节点对齐方式,对齐到左下角(默认为中间对齐)。
          nodesep: 20, // 可选,节点间距(px)。在 rankdir 为 'TB' 或 'BT' 时是节点的水平间距;在rankdir 为 'LR' 或 'RL' 时代表节点的竖直方向间距。
          ranksep: 50, // 可选,层间距(px)。在 rankdir 为 'TB' 或 'BT' 时是竖直方向相邻层间距;在rankdir 为 'LR' 或 'RL' 时代表水平方向相邻层间距。
          controlPoints: true, // 可选, 是否保留布局连线的控制点。默认值:false。
        },
	  });
	  graph.get('canvas').set('localRefresh', false); // 关掉局部渲染,防止在缩放和拖动中会出现轨迹的线条
	  graph.data(data);
	  graph.render();
	  ...
	  this.graph = graph;
	},
	addNode(type, e) {
	  // 将屏幕坐标转为拓扑区域坐标
	  const point = this.graph.getPointByClient(e.x, e.y);
	  // 获取挂载的节点
	  const currentMountedNode = this.graph.find('node', (node) => {
	    return (node.get('model').x >= point.x - 30 && node.get('model').x <= point.x + 30) && (node.get('model').y >= point.y - 30 && node.get('model').y <= point.y + 30);
	  });
	  if(!currentMountedNode?.get('model')){ return; };
	  ...
	  // 获取新节点的坐标
	  const coordinate = this.getNodeCoordinate(currentMountedNode.get('model').x + 150, currentMountedNode.get('model').y);
	  const node = {
	    id: uuid, // 建议使用 uuid,尽量避免重复。
	    label: type, // 元素的文本标签,有该字段时默认会渲染 label 。
	    type: type, // 元素的类型。
	    x: coordinate.x,
	    y: coordinate.y,
	    ...
	  };
	  // 生成从挂载节点到新节点的连线
	  const edge = {
	    source: currentMountedNode.get('model').id, 
	    target: node.id,
	    ...
	  };
	  this.graph.addItem('node', node);
	  this.graph.addItem('edge', edge);
	},
	// 尽可能不生成重复位置的节点
	getNodeCoordinate(x, y) {
	  let currentX = x;
	  let currentY = y;
	  // 获取当前节点位置附近是否已有节点,如果有,则重新生成坐标。
	  const node = this.findNodeByCoordinate(currentX, currentY);
	  if(!node?.get('model')){
	    return {x, y};
	  }
	  currentX = x + 70;
	  currentX = y + 50;
	  return this.getNodeCoordinate(currentX, currentY);
	},
	// 获取当前坐标附近的节点
	findNodeByCoordinate(x, y) {
	  const node = this.graph.find('node', (node) => {
	    return (node.get('model').x >= point.x - 50 && node.get('model').x <= point.x + 50) && (node.get('model').y >= point.y - 30 && node.get('model').y <= point.y + 30);
	  });
	  return node;
	},
  };
  ...
}
</script>

END

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值