可视化—AntV G6实现节点连线及展开收缩分组

17 篇文章 1 订阅
6 篇文章 0 订阅

AntV 是蚂蚁金服全新一代数据可视化解决方案,主要包含数据驱动的高交互可视化图形语法G2,专注解决流程与关系分析的图表库 G6、适于对性能、体积、扩展性要求严苛的场景。

demo使用数字模拟真实的节点及分组数据。combo内的nodes亦是使用随机数生成,节点之前的连线edges由节点从小到大依次连接 ,大家在用的时候,可以注意一下连线对象的 sourcetarget 两个属性即可

安装模块依赖 :npm install @antv/g6

main.js 中引入,并绑定Vue原型方法

import G6 from '@antv/g6';
import Vue from 'vue';

Vue.prototype.G6 = G6;

创建Graph.vue

<template>
  <div>
    <div id="drawGraph"></div>
  </div>
</template>

<script>
let graphG = null
export default {
  mounted() {
    this.initData();
  },
  methods: {
    initData() {
      let combos = [
        { id: '100-600', label: '100-600', type: 'root' },
        { id: '100-200', label: '100-200' },
        { id: '200-300', label: '200-300' },
        { id: '300-400', label: '300-400' },
        { id: '400-500', label: '400-500' },
        { id: '500-600', label: '500-600' },
      ]
      let edges = [
        { source: '100-200', target: '100-600' },
        { source: '200-300', target: '100-600' },
        { source: '300-400', target: '100-600' },
        { source: '400-500', target: '100-600' },
        { source: '500-600', target: '100-600' },
      ]
      let data = { combos, edges }
      this.makeRelationData(data);
    },
    // 分组 点 连线处理
    makeRelationData(data) {
      if (graphG) {
        graphG.destroy();
      }
      let drawGraph = document.getElementById("drawGraph");
      this.graphWidth = drawGraph.scrollWidth;
      this.graphHeight = drawGraph.scrollHeight || 1200;
      let origin = [this.graphWidth / 2, 100];
      let row = 150, clo = 180;
      let combos = data.combos
      let row_clo = Math.floor(Math.sqrt(combos.length));
      for (let i = 0; i < combos.length; i++) {
        let rowindex = Math.floor(i / row_clo) + 1;
        let cloindex = (i % row_clo) + 1;
        // 分组默认样式设置
        if (combos[i].type === 'root') {
          combos[i].x = this.graphWidth / 3
          combos[i].y = this.graphHeight / 3
          combos[i].style = {
            fill: "#B19693",
            opacity: 0.4,
            cursor: "pointer",
          };
        } else {
          // 分组定位
          combos[i].x = origin[0] + clo * cloindex;
          combos[i].y = origin[1] + row * rowindex;
          if (i % 2 === 1) {
            combos[i].y += 40;
          }
          combos[i].style = {
            fill: "#FAD069",
            opacity: 0.5,
            cursor: "pointer",
          }
        }
      }
      this.drawQfast(data)
    },
    drawQfast(data) {
      graphG = new this.G6.Graph({
        container: "drawGraph",
        width: this.graphWidth,
        height: this.graphHeight,
        groupByTypes: false,
        modes: {
          default: [
            { type: "zoom-canvas", enableOptimize: true, optimizeZoom: 0.2 },
            { type: "drag-canvas", enableOptimize: true },
            { type: "drag-node", enableOptimize: true, onlyChangeComboSize: true },
            { type: "drag-combo", enableOptimize: true, onlyChangeComboSize: true },
            { type: "brush-select", enableOptimize: true },
          ],
        },
        defaultEdge: {
          type: 'cubic-horizontal',
          lineWidth: 1,
          style: {
            endArrow: true,
            stroke: "#FAD069",
          },
        },
        edgeStateStyles: {
          hover: {
            lineWidth: 2,
          },
        },
        defaultNode: {
          type: "circle",
          size: 15,
          labelCfg: {
            position: "bottom",
            style: {
              fontSize: 15,
            },
          },
        },
        defaultCombo: {
          type: "circle",
          opacity: 0,
          lineWidth: 1,
          collapsed: true,
          labelCfg: {
            position: "top",
            refY: 5,
            style: {
              fontSize: 16,
            },
          },
        },
      });
      graphG.data(data);
      graphG.render(); // 渲染图
      graphG.zoom(0.8);  // 如果觉得节点大,可以缩放整个图
      graphG.on("edge:mouseenter", (e) => {
        graphG.setItemState(e.item, "hover", true);
      });

      graphG.on("edge:mouseleave", (e) => {
        graphG.setItemState(e.item, "hover", false);
      });

      graphG.on("combo:dblclick", (e) => {
        e.item._cfg.model.type = e.item._cfg.model.type === "rect" ? "circle" : "rect";  // 分组形状,方圆切换
        e.item._cfg.model.labelCfg.refY = e.item._cfg.model.type === "rect" ? -20 : 5;   // 切换形状,改变label定位

        const comboId = e.item._cfg.model.id
        graphG.collapseExpandCombo(comboId);

        // 分组收缩时,删除分组内的连线和节点
        if (e.item._cfg.model.collapsed) {   // 收缩 
          let newedges = e.item.getEdges();
          let newNodes = e.item.getNodes();
          for (let j = 0; j < newedges.length; j++) {
            graphG.removeItem(newedges[j]);
          }
          for (let i = 0; i < newNodes.length; i++) {
            graphG.removeItem(newNodes[i]);
          }
          data.edges.forEach(edge => {
            graphG.addItem("edge", edge);
          });
        } else {   // 展开
          // 分组展开时, 添加节点和连线,并给分组内的节点 添加位置信息
          let origin = [e.item._cfg.model.x, e.item._cfg.model.y];  // 获取当前分组combs的坐标
          let row = 110, clo = 150;
          // 生成(10-20)随机数个 随机数 模拟展开分组内的节点
          let randomCount = Math.floor(Math.random() * 10) + 10;
          let row_clo = Math.floor(Math.sqrt(randomCount));
          let nodes = []
          for (let i = 0; i < randomCount; i++) {
            let min = comboId.split('-')[0] - 0
            let max = comboId.split('-')[1] - 0
            let randomNum = Math.floor(Math.random() * (max - min)) + min;
            if (nodes.indexOf(randomNum) > -1) {
              i--
              continue;
            }
            nodes.push(randomNum)
            let rowindex = Math.floor(i / row_clo);
            let cloindex = i % row_clo;
            let y = origin[1] + row * rowindex
            let node = {
              label: randomNum,
              id: randomNum.toString(),
              comboId: comboId,
              style: {
                fillOpacity: 0.5,
                cursor: "pointer",
                fill: randomNum % 5 == 0 ? "#81C7D4" : "#986DB2"
              },
              x: origin[0] + clo * cloindex,
              y: i % 2 == 0 ? y + 40 : y
            }
            graphG.addItem("node", node); // 将节点添加至分组 
          }     
          nodes.sort((a, b) => a - b)  // 将分组内的数字排序,从小到大依次连接,模拟真实数据
          for (let i = 0; i < nodes.length - 1; i++) {
            let edge = {
              source: nodes[i].toString(),
              target: nodes[i + 1].toString(),
              lineWidth: 1,
              style: {
                lineDash: [2, 2],
                lineWidth: 0.5,
                stroke: "#00AA90"
              }
            }
            graphG.addItem("edge", edge);  // 添加连线   将分组内的数字排序,从小到大依次连接
          }
        }
      });
    },
  }
};
</script>

收缩图:

展开图:

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值