vis实现类知识图谱的拓扑图

vis.js可视化的出发点之一是它们可以处理动态数据,并允许对数据进行操作。为了实现这一点,vis.js包括一个基于灵活键/值 DataSetDataView处理非结构化JSON数据的功能。

  • 数据集DataSet,存储JSON的ID对象。可增删改查,过滤排序…

  • 资料检视DataView,提供数据集上经过过滤或格式化的视图。通过订阅DataView中的更改,获取过滤或格式化的数据,而无需一直指定过滤器和字段类型

  • 数据管道DataPipe,DataPipe从一个DataSet 获取数据DataView,对其进行转换并传入第二个 DataSet,用于强制数据类型,更改结构…

一、总结

Vis.js 是一个动态的,基于浏览器的可视化库。该库被设计为易于使用,能处理大量的动态数据。该库由以下几部分组成:

一是数据集和数据视图,基于灵活的键/值数据集,可以添加,更新和删除项目,订阅数据集变化;

二是时间轴,用于显示不同类型的时间轴数据,在时间轴上项目可以交互移动,缩放和操纵;

三是图形,使用节点和边显示一个交互式图形或网络。

二、配置&使用

1.vis-network 入门

vue安装

# v4.21.0 不再维护
npm install vis

# v9.0.1 建议使用下面这个
npm install vis-network

引入 vis-network

<template>
    <div id="mynetwork" class="mynetwork"></div>
</template>
<script>
//v4.21.0
import * as vis from "vis";
export default {
    data() {
        return { network: null };
    },
    mounted() {
        this.init();
    },
    methods: {
        init() {
            // 节点
            var nodes = new vis.DataSet([
                { id: 1, label: "Node 1" },
                { id: 2, label: "Node 2" },
                { id: 3, label: "Node 3" },
                { id: 4, label: "Node 4" },
                { id: 5, label: "Node 5" },
            ]);

            //  边
            var edges = new vis.DataSet([
                { id: 1, from: 1, to: 3 },
                { id: 2, from: 1, to: 3 },
                { id: 5, from: 3, to: 1 },
                { from: 1, to: 2 },
                { from: 2, to: 4 },
                { from: 2, to: 5 },
                { from: 3, to: 3 },
            ]);

            // create a network
            var container = document.getElementById("mynetwork");
            var data = {
                nodes: nodes,
                edges: edges,
            };
            var options = {};
            this.network = new vis.Network(container, data, options);
        },
    },
};
</script>

<style scoped>
.mynetwork {
    height: 100%;
}
</style>

传统html页面使用

<html>
<head>
    <script type="text/javascript" src="https://unpkg.com/vis-network/standalone/umd/vis-network.min.js"></script>
    <style type="text/css">
        #mynetwork {
            width: 600px;
            height: 400px;
            border: 1px solid lightgray;
        }
    </style>
</head>

<body>
<div id="mynetwork"></div>

<script type="text/javascript">
    // 创建节点数据数组
    var nodes = new vis.DataSet([
        { id: 1, label: "Node 1" },
        { id: 2, label: "Node 2" },
        { id: 3, label: "Node 3" },
        { id: 4, label: "Node 4" },
        { id: 5, label: "Node 5" }
    ]);

    // 创建边数据数组
    var edges = new vis.DataSet([
        { from: 1, to: 3 },
        { from: 1, to: 2 },
        { from: 2, to: 4 },
        { from: 2, to: 5 },
        { from: 3, to: 3 }
    ]);
    // 获取容器
    var container = document.getElementById('mynetwork');
    // 将数据赋值给vis 数据格式化器
    var data = {
        nodes: nodes,
        edges: edges
    };
    var options = {};
    // 初始化关系图
    var network = new vis.Network(container, data, options);
</script>
</body>
</html>

效果如下
在这里插入图片描述

事件监听

交互模块触发

let container = document.getElementById("mynetwork");
    let data = {
        nodes: this.nodes,
        edges: this.edges,
    };
    let options = {interaction: { hover: true }};

    network = new vis.Network(container, data, options);
    // 事件监听
    this.networkEvent();
},

// 拓扑网络监听事件
networkEvent() {
    //单击
    network.on("click", (e) => {});

    // 双击
    network.on("doubleClick", (e) => {});

    // 单击鼠标右键
    network.on("oncontext", (e) => {});

    // 节点悬停
    network.on("hoverNode", (e) => {});

    // 节点失焦
    network.on("blurNode", (e) => {});

    // 边悬停
    network.on("hoverEdge", (e) => {});

    // 边失焦
    network.on("blurEdge", (e) => {});

    //  鼠标拖动开始事件
    network.on("dragStart", (e) => {});
    
    //  鼠标拖动结束事件
    network.on("dragEnd", (e) => {});
    
    //  鼠标缩放事件
    network.on("zoom", (e) => {});
    //...
}
例:展开&收缩节点

需求:展开节点,收缩节点,收缩展开过的节点光晕颜色改变(区别未展开过的节点)

DataSet Methods

新增节点/边:nodes.add({…}),edges.add({…})

删除节点/边:nodes.remove({…}),edges.remove({…})


<template>
  <div id="mynetwork" class="mynetwork2"></div>
</template>

<script>
const vis = require("vis-network/dist/vis-network.min.js");
import _ from "lodash";

var network = null;
export default {
  data() {
    return {
      //创建节点对象
      nodes: new vis.DataSet([
        {
          id: "node-1",
          label: "Node 1",
          title: "1111",
          image: {
            selected: require("@/assets/people-opened.png"),
            unselected: require("@/assets/people-def.png"),
          },
        },
      ]),
      //创建连线对象
      edges: new vis.DataSet([
        // { id: "edge-1", from: "node-1", to: "node-2" },
      ]),
      removeNodes: [],
      removeEdges: [],
    };
  },
  mounted() {
    this.initNetwork();
  },
  methods: {
    // 初始化网络拓扑
    initNetwork() {
      let container = document.getElementById("mynetwork");
      let data = {
        nodes: this.nodes,
        edges: this.edges,
      };
      let options = {
        interaction: {
          hover: true,
        },
        layout: {
          randomSeed: 2, //配置每次生成的节点位置都一样,参数为数字1、2等
        },
        nodes: {
          // shape: "dot",
          // shape: "circularImage",
          // borderWidth: 2,
          shape: "image",
          size: 23,
          color: {
            // border: "rgba(255, 255, 255, 1)",
            background: "rgba(102, 51, 255, 1)",
          },
          font: {
            // color: "#fff",
          },
          shadow: {
            enabled: true,
            // color: "rgba(153, 102, 204, 1)",
            color: "rgba(153, 0, 204, 1)",
            size: 40,
            x: 0,
            y: 0,
          },
          chosen: {
            label: false,
            // node: function(values, id, selected, hovering) {
            node: function(values) {
              values.shadowColor = "rgba(255, 215, 0, 0.8)";
            },
          },
          // shapeProperties: {
          //     borderDashes: [5, 15],
          // },
        },
        edges: {
          hoverWidth: 0,
          shadow: true,
          arrows: {
            to: {
              enabled: true,
              type: "arrow",
            },
          },
          color: {
            color: "#525A81",
            highlight: "#FFBA30",
            hover: "#FFBA30",
            // inherit: "from",
            opacity: 1.0,
          },
          font: {
            // color: "#fff",
            size: 12, // px
            strokeWidth: 0, // px
          },
        },
        physics: {
          enabled: true,
          // minVelocity: 1 //(default: 1)一旦达到所有节点的最小速度,我们假设网络已经稳定,布局停止。
          timestep: 0.2,
          // wind: {
          //   x: 10,
          //   y: 10
          // }
        },
      };
      network = new vis.Network(container, data, options);
      // 事件监听
      this.networkEvent();
    },
    // 拓扑网络监听事件
    networkEvent() {
      //单击
      network.on("click", (e) => {
        console.log("单击:", e);
        console.log(this.edges.get(e.edges[0]));
        //只有在点击节点时才进行节点的增删
        if (!e.nodes.length) {
          return false;
        }
        var clickNodeList = this.nodes.get(e.nodes[0]);
        console.log(clickNodeList);
        if (!clickNodeList.open) {
          // //恢复过滤数据
          // this.recoverFilterData();
          this.prdData(e.nodes[0], e.edges[0], e.nodes[0]);
          // 判断是否有子节点
          let haschildState = network.getConnectedNodes(e.nodes[0], "to").length ? true : false;
          this.nodes.update({
            id: e.nodes[0],
            open: true,
            haschildren: haschildState,
            changEdge: e.edges[0],
          });
          if (e.edges[0]) {
            this.edges.update({
              id: e.edges[0],
              open: true,
              haschildren: haschildState,
              // length: 600,
            });
          }
          // 移动焦点到视图中心
          // 双击某一节点,画布放大至1:1比例,当前节点处于画布中心位置
          var option = {
            scale: 1.0,
            offset: { x: 0, y: 0 },
            animation: {
              duration: 1000,
              easingFunction: "easeInOutQuad",
            },
          };
          network.focus(e.nodes[0], option);
        } else {
          // 获取该节点下   未展开的节点的集合  和  从该节点发出的到未展开节点线的集合
          let nodeItems = this.nodes.get({
            filter: function(item) {
              return item.parentId === e.nodes[0] && !item.open && !item.haschildren;
            },
          });
          console.log("nodeItems", nodeItems);
          let edgesItem = this.edges.get({
            filter: function(item) {
              return (
                !item.open && item.from == e.nodes[0] && !item.haschildren
                // (item.to == e.nodes[0] ||
                //     item.from == e.nodes[0])
              );
            },
          });
          console.log("edgesItem", edgesItem);
          //需求: 只缩进当前节点一度关系中未展开的节点,一度关系点已展开关系的不缩进
          //缩进( 删除未展开子节点和子节点连线)
          // this.addNew(this.removeNodes, nodeItems);
          // this.addNew(this.removeEdges, edgesItem);
          this.nodes.remove(nodeItems);
          this.edges.remove(edgesItem);
          // 判断是否有子节点
          let haschildState = network.getConnectedNodes(e.nodes[0], "to").length ? true : false;
          this.nodes.update({
            id: e.nodes[0],
            open: false,
            shadow: {
              // color: "rgba(166, 26, 120, 1)",
              color: "rgba(81, 0, 255, 1)",
            },
            image: {
              selected: require("@/assets/people-opened.png"),
              unselected: require("@/assets/people-def.png"),
            },
            // clickNodeList.image.selected || clickNodeList.image, //已修改后的字段为image
            haschildren: haschildState,
          });
          if (clickNodeList.changEdge) {
            //初始中心节点无changEdge
            this.edges.update({
              id: clickNodeList.changEdge,
              open: false,
              haschildren: haschildState,
              // length: 280,
            });
          }
        }
      });
    },
    prdData(nodename, edgename, orgnode) {
      var len = 2;
      let nodes = [];
      let edges = [];
      let nodeName = nodename || "node";
      let edgeName = edgename || "edge";
      const relation = [
        "诈骗",
        "父亲",
        "母亲",
        "哥哥",
        "姐姐",
        "同事",
        "定向扩展",
        "违章",
        "妻子",
        "弟弟",
      ];
      const labelType = ["people", "car", "phone", "qq", "wx", "email"];
      for (var i = 1; i <= len; i++) {
        const randomIndex = Math.floor(Math.random() * 6);
        nodes.push({
          parentId: orgnode,
          id: nodeName + "-" + i,
          label: labelType[randomIndex],
          shape: "image",
          image: {
            selected: require("@/assets/people-opened.png"),
            unselected: require("@/assets/people-def.png"),
          },
          haschildren: false,
        });
        edges.push({
          id: edgeName + "-" + i,
          from: orgnode || "node-1",
          to: nodeName + "-" + i,
          label: relation[Math.floor(Math.random() * 10)],
          haschildren: false,
        });
      }
      // 避免添加重复数据导致错误
      this.buildData(nodes, edges);
    },
    // 构建数据
    buildNode(nodes) {
      nodes.forEach((item) => {
        // var node = {
        // ...
        //};
        this.nodes.add(item);
      });
      // console.log("---------", this.nodes.get());
    },
    buildEdge(edges) {
      edges.forEach((item) => {
        // var edge = {
        //  ...
        // };
        this.edges.add(item);
      });
    },
    buildData(n, e) {
      // 去重
      // console.log(this.nodes.get());
      //删除n中与第二个数组对象id相同的值
      let differenceN = _.differenceBy(n, this.nodes.get(), "id");
      let differenceE = _.differenceBy(e, this.edges.get(), "id");
      // console.log("differenceN:", differenceN, differenceE);
      this.buildNode(differenceN);
      this.buildEdge(differenceE);
    },
    addNew(arr, newarr) {
      for (const item of newarr) {
        arr.push(item);
      }
    },
    recoverFilterData() {
      // console.log("reset:", this.removeNodes);
      // console.log("reset:", this.removeEdges);
    },
  },
};
</script>

<style scoped>
.mynetwork2 {
  height: 100%;
  /* background-color: #2a004a; */
}
</style>


效果如下
在这里插入图片描述

功能点:

  1. 右键展开菜单,点击请求节点数据,或展开下级菜单

  2. 拖动节点或画布,菜单跟随节点

  3. 缩放画布,菜单同步缩放

  4. 对其它或者空白区域进行操作时,关闭菜单

自定义菜单
vis拓扑图 右键节点展开菜单

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值