AntV G6

一个图可视化引擎。它提供了图的绘制、布局、分析、交互、动画等基础的图可视化能力。用于前端绘制拓扑结构

一、安装&引用

STEP1:安装

npm install --save @antv/g6

STEP2:导入

import G6 from '@antv/g6';

二、使用

Step1 创建容器
<template>
	<div>
    <div id="container"></div>
  </div>
</template>

三、使用总结

1.TreeGraph 树图

1)子树自动收缩问题

需求描述:从接口请求到数据之后,把数据追加到图表上形成链式结构;
数据结构:

const newData = [
	{
    uuid: 'test1'
  },
  {
    uuid: 'test2'
  }
]
问题1:追加数据之后子树会自动收缩起来,可能是因为id没有获取到

根据此数据结构需使用updateChildren(newData, 父节点)来追加数据
因为树图的数据结构需要有id字段且源数据结构是uuid,所以在配置时使用getId来处理id

const graph = new G6.TreeGraph({
    container: container as HTMLElement, // String | HTMLElement,必须,在 Step 1 中创建的容器 id 或容器本身
    width: width,
    height: height,
    linkCenter: true,
    plugins: [menu, tooltip],
    // 画布交互配置
    modes: {
      default: [
        {
          type: 'collapse-expand',
          trigger: 'dblclick',
        },
        'drag-canvas', // 拖拽画布
        'zoom-canvas', // 缩放画布
        'activate-relations', // 激活关系
      ],
    },
    // 配置节点的属性
    defaultNode: {
      type: 'image-node',
      size: 30,
      // 指定边连入节点的连接点的位置,可以为空
      anchorPoints: [
        [0, 0.5],
        [1, 0.5],
      ],
    },
    defaultEdge: {
      type: 'cubic-horizontal', // 默认边的类型为水平方向的三次曲线
    },
    layout: {
      type: 'compactBox', // 紧凑树
      direction: 'LR', // 根节点在左,往右布局 H / V / LR / RL / TB / BT
      getHGap: function getHGap() {
        return 50; // 每一层节点之间的间距
      },
      getVGap: function getVGap() {
        return 10; // 每个节点的垂直间距
      },
      getId: function getId(node: any) {
        return node.uuid; // 节点 id 的回调函数, 处理数据的id
      },
    },
  });
问题2:删除节点后再增加相同节点,子树不会展开但实际上已添加进去了;

删除节点后,可以使用setTimeout来延迟添加节点

2)自定义节点的label不显示

如果需要让节点显示label,有两种方式:
1.返回的数据结构中需有 label 字段,自定义节点中配置 text,配置fill颜色,此处获取的是默认节点样式

//图表初始化时配置节点的默认属性
defaultNode: {
      type: 'image-node',
      size: 30,
      labelCfg: {
        style: {
          fill: '#333', 
        },
      },
      anchorPoints: [
        [0, 0.5],
        [1, 0.5],
      ],
},

 // 自定义节点中的配置
 group.addShape('text', {
      attrs: {
         x: size[0] / 2,
         y: size[1] + 15,
         textAlign: 'center', // 文本在图形中的对齐方式
         textBaseline: 'middle', // 当前文本基线
         text: cfg.label,
         ...labelCfg?.style,
       },
       name: 'text-shape',
 });

2.返回的数据结构中无label字段,需要在graph.node中配置label,然后在自定义节点中做相应配置
自定义节点文件:

import G6 from '@antv/g6';
import _ from '@lodash';

export function initImageNode(imageUrl: string, name = 'node') {
  const img = new Image();
  img.src = imageUrl;
  const _name = `image-${name}`;
  img.onload = () => {
    G6.registerNode(
      // 自定义节点
      _name,
      {
        draw(cfg: any, group) {
          let { size } = cfg;
          const { labelCfg } = cfg;
          if (!size) {
            size = [40, 40];
          }
          if (_.isNumber(size)) {
            size = [size, size];
          }
          group.addShape('text', {
            attrs: {
              x: size[0] / 2,
              y: size[1] + 15,
              textAlign: 'center', // 文本在图形中的对齐方式
              textBaseline: 'middle', // 当前文本基线
              text: cfg.label,
              ...labelCfg?.style,
            },
            name: 'text-shape',
          });
          return group.addShape('image', {
            attrs: {
              img,
              width: size[0],
              height: size[1],
              ...cfg.style,
            },
            draggable: true,
            name: 'image-shape',
          });
        },
        update(cfg, node) {},
        options: {
          stateStyles: {
            active: {
              // 活跃
              opacity: 1,
              'text-shape': {
                opacity: 1,
                fill: 'rgb(95, 149, 255)',
              },
            },
            inactive: {
              opacity: 0.3,
              'text-shape': {
                opacity: 0.3,
                fill: '#333',
              },
            },
          },
        },
      },
      'single-node',
    );
  };
}

遇到的问题:
数据结构中已有label属性,且自定义节点中已对“text”做了配置。
问题1:只有节点高亮时才会显示active状态下的样式,且只有该节点更新之后才会显示label。
因为没有设置节点的文字颜色;
解决方法:
在graph.node()或defaultNode:{}中配置fill,然后自定义节点配置中获取文字样式
问题2:节点文字能正常显示之后,请求数据追加某个节点的子树数据之后该节点会显示两个label
解决方法
在自定义节点中添加“update(cfg, node) {}”方法更新节点

3)自定义节点svg图标,根据节点类型渲染不同的图标样式

方式一:
在自定义节点时,动态获取节点图标的src,根据节点类型去修改图标路径

let nodeType = '';
if (['targetIp', 'sourceIp'].includes(node.fieldsKey)) {
      nodeType = ipIcon;
    } else if (node.fieldsKey === 'targetPort') {
      nodeType = portIcon;
    } else if (node.fieldsKey === 'protocols') {
      nodeType = protocolIcon;
    }
return group.addShape('image', {
    attrs: {
       img: nodeType,
       width: size[0],
      height: size[1],
       ...cfg.style,
    },
     draggable: true,
     name: 'image-shape',
});

问题:
如果只自定义一种节点样式,然后根据节点类型去获取不同图标的路径(修改img的src),当鼠标经过节点时会重复加载图标文件;
方式二:
自定义多个节点样式,修改节点的类型

export function initImageNode(imageUrl: string, name= 'node') {
	const img = new Image();
  img.src = imageUrl;
  const _name = `image-${name}`; // 不同的节点类型对应不同的图标路径
  img.onload = () => {
    G6.registerNode(
      // 自定义节点
      _name,
      {
        draw(cfg: any, group) {
          let { size } = cfg;
          const { labelCfg } = cfg;
          if (!size) {
            size = [40, 40];
          }
          if (_.isNumber(size)) {
            size = [size, size];
          }
          group.addShape('text', {
            attrs: {
              x: size[0] / 2,
              y: size[1] + 15,
              textAlign: 'center', // 文本在图形中的对齐方式
              textBaseline: 'middle', // 当前文本基线
              text: cfg.label,
              ...labelCfg?.style,
            },
            name: 'text-shape',
          });
          return group.addShape('image', {
            attrs: {
              img,
              width: size[0],
              height: size[1],
              ...cfg.style,
            },
            draggable: true,
            name: 'image-shape',
          });
        },
        update(cfg, node) {},
        options: {},
      },
      'single-node',
    );
  }
}

#导入svg文件

# 导入四种节点图标svg文件
import nodeIcon from '@/assets/images/node.svg';
import portIcon from '@/assets/images/port.svg';
import protocolIcon from '@/assets/images/protocol.svg';
import ipIcon from '@/assets/images/ip.svg';

#自定义多个不同的节点图标样式

 # 加载不同图表的自定义节点样式
onBeforeMount(async () => {
  await initImageNode(nodeIcon, 'node');
  await initImageNode(ipIcon, 'ip');
  await initImageNode(portIcon, 'port');
  await initImageNode(protocolIcon, 'protocol');
});

onMounted(async () => {
// 初始化图表
  nextTick(() => {
    initGraph();
  });
});

# 在初始化图表方法中定义节点样式
graph.node(function (node: any) {
    const id = node.id;
    let nodeType = '';
    if (['targetIp', 'sourceIp'].includes(node.fieldsKey)) {
      nodeType = 'image-ip';
    } else if (node.fieldsKey === 'targetPort') {
      nodeType = 'image-port';
    } else if (node.fieldsKey === 'protocols') {
      nodeType = 'image-protocol';
    }
    return {
      id: id,
      type: nodeType || 'image-node',
      extraData: {
        ...node,
      },
    };
  });
4) 节点高亮效果

鼠标移入节点时,设置相关的节点和边的高亮效果,并添加边信息
鼠标移出节点时,取消边信息的展示,取消高亮状态
image.png
1.对图表的画布交互配置中,设置 “activate-relations”

// 画布交互配置
    modes: {
      default: [
        'drag-canvas', // 拖拽画布
        'zoom-canvas', // 缩放画布
        'activate-relations', // 激活关系
      ],
    },

2.单独处理鼠标移入移出节点效果

# 鼠标进入节点:高亮相关边和节点,显示边信息
  graph.on('node:mouseenter', (e: G6GraphEvent) => {
    // 根据节点id获取当前节点所有相关边
    const model = e.item.getModel() as any; // 节点模型
    if (!model.extraData) return;
    const inEdgeLabel = state.topologyQuery.find(
      (item) => item.key === (model?.extraData as NodeType)?.fieldsKey,
    )?.label;
    const node = state.graph.findById(model.id);
    const inEdges = node.getInEdges(); // 入边
    const outEdges = node.getOutEdges(); // 出边
    // 给入边添加label:
    inEdges.forEach((edge: any) => {
      const edgeModel = edge.getModel();
      const item = graph.findById(edgeModel.id);
      graph.updateItem(item, {
        label: inEdgeLabel,
      });
    });
   
    /**
     * 给出边添加label:
     * 根节点取usedQuery[0], 其余节点找子节点的fieldsKey, 如果没有则取当前查询条件state.currentQuery
     */
    // 匹配到当前节点的子节点元素实例
    const item = state.graph.find('node', (n: any) => {
      return n.get('model').pid === model.id;
    });
    const sonFieldKey = item?.getModel()?.fieldsKey;
    const outData = sonFieldKey ?? state.currentQuery.condition;
    const outCondition = model.id === 'default' ? state.usedQuery?.[0]?.condition : outData;
    const outEdgeLabel = state.topologyQuery.find((i) => i.key === outCondition)?.label;
    outEdges.forEach((edge: any) => {
      const edgeModel = edge.getModel();
      const it = graph.findById(edgeModel.id);
      graph.updateItem(it, {
        label: outEdgeLabel,
      });
    });

    const nodes = graph.getNodes(); // 所有的节点
    const neighbors = state.graph.getNeighbors(model.id).map((n: any) => n.getModel().id);

    /**
     * 设置相关节点状态:acitve为true
     * 设置不相关节点状态:inactive为true, active为false
     */
    _.forEach(nodes, (n) => {
      if ([...neighbors, model.id].includes(n.getModel().id)) {
        graph.setItemState(n, 'active', true); // 设置相关节点的 active 状态为 true
      } else {
        graph.setItemState(n, 'active', false); // 设置不相关节点的 inactive 状态为 false (透明)
        graph.setItemState(n, 'inactive', true);
      }
    });
  });

# 鼠标移出节点时,取消边信息的展示,取消高亮状态
  graph.on('node:mouseleave', (e: G6GraphEvent) => {
    const nodeElement = document.getElementsByClassName('g6-component-tooltip');
    (nodeElement[0] as HTMLElement).style.display = 'none'; // 不展示tooltip
    graph.setItemState(e.item, 'hover', false); // 设置当前节点的 hover 状态为 false
    // 取消显示边信息
    const model = e.item.getModel();
    const node = state.graph.findById(model.id);
    const edges = node.getEdges();
    // 取消边的label
    edges.forEach((edge: Edge) => {
      const edgeModel = edge?.getModel();
      const item = graph.findById(edgeModel.id as string);
      graph.updateItem(item, {
        label: '',
      });
    });
    // 取消节点的active状态
    const nodes = graph.getNodes();
    _.forEach(nodes, (n) => {
      graph.setItemState(n, 'active', false);
      graph.setItemState(n, 'inactive', false);
    });

    _.forEach(edges, (edge) => {
      graph.setItemState(edge, 'active', false);
      graph.setItemState(edge, 'inactive', false);
    });
  });

2.设置图表工具栏(放大、缩小……)

image.png

	# 图表部分
    <div v-if="state.graphData?.length" class="graph_main flex-1">
      <div ref="graphRef" class="h-100% w-100%" id="graph-wrap"></div>
    </div>
    <el-empty v-else class="h-100% w-100% flex-1" :description="t('common.noData')" />
    # 工具栏部分
    <div id="flow-toolbar-wrap" v-if="state.graphData?.length">
      <div class="flow-toolbar">
        <el-link class="toolbar-item" :underline="false" @click="toolbarClick('zoomIn')">
          {{ t('topologyAnalysis.graph_zoomIn') }}
        </el-link>
        <el-link class="toolbar-item" :underline="false" @click="toolbarClick('zoomOut')">
          {{ t('topologyAnalysis.graph_zoomOut') }}
        </el-link>
        <el-divider direction="vertical" />
        <el-link class="toolbar-item" :underline="false" @click="toolbarClick('zoomReset')">
          {{ t('topologyAnalysis.graph_zoomReset') }}
        </el-link>
        <el-link class="toolbar-item" :underline="false" @click="toolbarClick('fitView')">
          {{ t('topologyAnalysis.graph_fitView') }}
        </el-link>
      </div>
    </div>

 # 工具栏操作方法
function toolbarClick(action: string) {
  if (!state.graph) return;
  if (action === 'fitView') {
    state.graph?.fitView();
    return;
  }

  const zoom = state.graph.getZoom();
  if (action === 'zoomIn') {
    if (zoom > 10) return;
    state.graph?.zoom(1.2);
    state.graph?.fitCenter();
    return;
  }
  if (action === 'zoomOut') {
    if (zoom < 0.4) return;
    state.graph?.zoom(0.8);
    state.graph?.fitCenter();
    return;
  }
  if (action === 'zoomReset') {
    state.graph?.zoomTo(1);
    state.graph?.fitCenter();
  }
}

# 画布部分设置网格背景样式
.graph_main {
  background-image: linear-gradient(90deg, rgba(60, 10, 30, 0.04) 3%, transparent 0),
  linear-gradient(1turn, rgba(60, 10, 30, 0.04) 3%, transparent 0);
  background-size: 20px 20px;
  background-position: 50%;
  background-repeat: repeat;
  margin: 0 10px;
  :deep(.minimap-container) {
    position: absolute;
    right: 10px;
    top: 100px;
    background-color: rgba(0, 0, 0, 0.1);
    box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -4px rgba(0, 0, 0, 0.1);
    .g6-minimap-viewport { // 缩略图样式
      outline: 2px solid hsla(213, 100%, 55%, 0.5);
    }
  }
}

# 工具栏样式
#flow-toolbar-wrap {
  width: 100%;
  display: flex;
  margin-bottom: 5px;
  align-items: center;
  justify-content: center;

  .flow-toolbar {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    height: 40px;
    box-shadow: var(--el-box-shadow-light);
    background-color: #fff;
    padding: 0 16px;
    z-index: 1;
    .toolbar-item {
      user-select: none;
    }
  }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: ANTV G6是一款智能电视机顶盒,它不仅可以通过网络观看在线视频,还提供了离线文档的功能。 ANTV G6的离线文档功能允许用户在没有网络连接的情况下访问和浏览本地存储的文档文件。用户可以通过USB接口或者其他存储设备将文档文件导入到G6中,然后在离线状态下查看和编辑这些文档。 离线文档功能使得用户可以在没有网络的情况下继续处理文档工作,提高了工作和学习的效率。无论是在出差、旅行或者没有网络覆盖的地方,用户都可以依靠ANTV G6来查看和编辑文档,保证工作的顺利进行。 ANTV G6不仅支持常见的文档格式,如Word、Excel和PowerPoint,还支持PDF、TXT等多种格式。用户可以通过文件管理系统在G6中浏览并打开各种格式的文件,进行阅读、编辑和保存。 此外,ANTV G6的离线文档功能还提供了一些便捷的操作选项,如快速搜索、书签、阅读模式等。用户可以根据自己的需求和喜好进行设置,提高使用的便捷性和个性化。 总之,ANTV G6的离线文档功能为用户提供了更加便利和灵活的文档处理方式,无论是在有网络的情况下,还是在没有网络的境中,用户都可以通过G6进行文档工作,提升工作和学习的效率。 ### 回答2: ANTV G6是一款智能电视盒子,它可以通过连接到网络收看各种在线视频内容。但是,ANTV G6也支持离线播放,即使在没有网络连接的情况下,用户也可以通过存储在盒子内部的离线文档来观看内容。 离线文档功能使得用户可以在没有网络时仍然享受丰富的内容。用户可以下载电影、电视剧、纪录片等视频文件,并将其保存在ANTV G6的存储空间内。用户还可以下载各种格式的音乐文件,创建自己的音乐库。此外,用户还可以下载电子书、漫画等文档文件,并通过ANTV G6的离线文档功能进行阅读。 ANTV G6的离线文档功能具有很高的灵活性和可定制性。用户可以根据自己的口味和需求,选择并下载自己喜欢的内容。而且,用户可以通过文件管理器轻松管理和排序这些离线文档,让自己的媒体库更加整洁有序。 总的来说,ANTV G6的离线文档功能为用户提供了便捷的娱乐方式。无论是在没有网络的地方、网络出现故障或者想要隐私地享受内容,用户都可以通过离线文档功能满足自己的需求。ANTV G6的离线文档功能成为用户对于这款智能电视盒子的一个重要选择因素。 ### 回答3: ANTV G6是一款智能电视盒子,它可以连接到电视并提供多种功能。ANTV G6支持离线文档功能,这意味着用户可以在没有网络连接的情况下访问和查阅文档。 离线文档功能非常实用,特别是在没有网络连接或网络不稳定的情况下。用户可以事先下载需要的文档,然后在没有网络时随时打开并查看。这样,即使用户没有互联网,也可以方便地查阅重要资料或文件。 ANTV G6的离线文档功能支持多种文档格式,如PDF、DOC、PPT等常见格式。用户可以通过安装相应的文档阅读器应用程序来打开和阅读这些文档。除了查看文档外,用户还可以进行一些基本的操作,如搜索、添加书签、标注等。 另外,ANTV G6的离线文档功能还提供了快速跳转和目录导航等便捷功能,使得用户能够更方便地浏览和定位到所需的内容。此外,ANTV G6还支持对文档进行分享,用户可以将文档分享给其他设备或其他用户。 总的来说,ANTV G6的离线文档功能为用户提供了便捷的文档访问和查阅体验。无论用户身处何地,只要有ANTV G6,就能够随时打开和浏览各种文件。这对于那些需要频繁查阅文档并且经常处于没有网络连接的境中的用户来说,无疑是一项非常有用且实用的功能。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值