d3.js 关系图谱

背景

接着就是关系图谱了

技术点

使用d3主要需要掌握svg,jquery,cytoscape和d3这几个知识点。
本次使用的是 v5版本的。

案例

company.png

实现效果

gx.png

实现代码

HTML

<template>
<div id="relation_graph" :style="[layout]">
    <div id="MainCy" :style="[layout]"></div>
    <div id="MainD3" scale="1" class="no-padding tp-container">
      <svg
        version="1.1"
        xmlns="http://www.w3.org/2000/svg"
        xmlns:xlink="http://www.w3.org/1999/xlink"
        xml:space="preserve"
      ></svg>
    </div>
  </div>
</template>

JS

<script>
import gx01 from "./D3JSON/gx01.json";
import $ from "./js/jquery-3.1.1.min";
import * as d3 from "./js/d3.v5";
import cytoscape from "cytoscape";
export default {
  data(){
    return{
      
      layout:{ width:"", height:""}
    }
  },
  created(){
    
  },
  mounted() {
    this.relationship();
  },
  methods: {
    relationship() {
      this.layout.width = document.body.clientWidth + 'px'; //tree容器宽
    this.layout.height = document.body.clientHeight + 'px'; //tree容器高

      /** 网页当前状态判断 (解决没布局完就切换页面造成点聚集在一起)*/
      var hidden, state, visibilityChange;
      if (typeof document.hidden !== "undefined") {
        hidden = "hidden";
        visibilityChange = "visibilitychange";
        state = "visibilityState";
      } else if (typeof document.mozHidden !== "undefined") {
        hidden = "mozHidden";
        visibilityChange = "mozvisibilitychange";
        state = "mozVisibilityState";
      } else if (typeof document.msHidden !== "undefined") {
        hidden = "msHidden";
        visibilityChange = "msvisibilitychange";
        state = "msVisibilityState";
      } else if (typeof document.webkitHidden !== "undefined") {
        hidden = "webkitHidden";
        visibilityChange = "webkitvisibilitychange";
        state = "webkitVisibilityState";
      }
      // 声明变量
      var cy;
      var id;
      var activeNode;
      var graphDatas; //接口返回的原始数据
      var _rootData, _rootNode; //原始数据转换成的graph数据,根节点数据

      var _COLOR = {
        node: { person: "#FD485E", company: "#4ea2f0", current: "#ff9e00" },
        border: { person: "#FD485E", company: "#128BED", current: "#EF941B" },
        line: { invest: "#fd485e", employ: "#4ea2f0", legal: "#4ea2f0" },
      };
      var _currentKeyNo,
        _companyRadius = 35,
        _personRadius = 15,
        _circleMargin = 10,
        _circleBorder = 3,
        _layoutNode = {},
        _isFocus = false;
      var _maxChildrenLength = 0;
      /** 解决浏览器标签切换排列问题 */
      var _isNeedReload = false;
      var _isGraphLoaded = false;
      document.addEventListener(
        visibilityChange,
        function () {
          // console.log(document[state]);
          if (document[state] == "visible") {
            if (_isNeedReload) {
              $("#MainCy").html("");
              $("#TrTxt").removeClass("active");
              _currentKeyNo = "e6b98b6db9ac8bbee4906e92fc01e4c9";
              console.log("初始化2")
              getData(_currentKeyNo);
            }
            // document.title = "hidden-not-loaded";
            document.title = "企信查";

          } else {
            if (!_isGraphLoaded) {
              _isNeedReload = true;
            }
          }
        },
        false
      );
      /** end 解决浏览器标签切换排列问题 */
      $(document).ready(function () {
        console.log("初始化1")
        // 获取数据
        _currentKeyNo = "e6b98b6db9ac8bbee4906e92fc01e4c9";
        getData(_currentKeyNo);

        // 监听容器的滚动事件
        document
          .getElementById("MainCy")
          .addEventListener("wheel", function (e) {
            // console.log(e.wheelDelta);
            var type = 1;
            if (e.wheelDelta > 0) {
              type = 1;
            } else {
              type = 2;
            }
            maoScale(type);
          });
      });
      function getData(_currentKeyNo) {
        // 模拟数据
        let graphDatas = gx01.data.graphDatas
        _rootData = getRootData(graphDatas);
        domUpdate(_rootData);
      }

      // 图谱、筛选面板更新
      function domUpdate(graphData) {
        console.log(graphData,"筛选面板更新");
        getD3Position(graphData);

        setTimeout(function () {
          drawGraph(transformData(graphData));
        }, 500);

        selPanelUpdateList(graphData.nodes, graphData.links, true);
      }

      // ---------------------------- domUpdate 调用的方法 ---- Begin
      function getD3Position(graph) {
        getLayoutNode(graph);

        function filterLinks1(graph) {
          // 筛选用于布局的links
          var layoutLinks = [];
          for (var i = 0; i < graph.links.length; i++) {
            var link = graph.links[i];
            var sourceLevel = link.sourceNode.layout.level;
            var targetLevel = link.targetNode.layout.level;
            var sourceNode = link.sourceNode;
            var targetNode = link.targetNode;
            //            sourceNode.layout.isSetLink = false;
            //            targetNode.layout.isSetLink = false;

            //            if(!sourceNode.layout.isSetLink && !targetNode.layout.isSetLink){
            if (
              (sourceLevel == 1 && targetLevel == 2) ||
              (sourceLevel == 2 && targetLevel == 1)
            ) {
              //                    sourceNode.layout.isSetLink = true;
              //                    targetNode.layout.isSetLink = true;
              layoutLinks.push(link);
            }
            if (
              (sourceLevel == 2 && targetLevel == 3) ||
              (sourceLevel == 3 && targetLevel == 2)
            ) {
              //                    sourceNode.layout.isSetLink = true;
              //                    targetNode.layout.isSetLink = true;
              layoutLinks.push(link);
            }
            //            }
          }

          layoutLinks.forEach(function (link, i) {
            if (link.targetNode.layout.level == 3) {
              layoutLinks.forEach(function (alink, j) {
                if (
                  alink.linkId != link.linkId &&
                  (alink.targetNode.nodeId == link.targetNode.nodeId ||
                    alink.sourceNode.nodeId == link.targetNode.nodeId)
                ) {
                  layoutLinks.splice(j, 1);
                }
              });
            }

            if (link.sourceNode.layout.level == 3) {
              layoutLinks.forEach(function (alink, j) {
                if (
                  alink.linkId != link.linkId &&
                  (alink.targetNode.nodeId == link.sourceNode.nodeId ||
                    alink.sourceNode.nodeId == link.sourceNode.nodeId)
                ) {
                  layoutLinks.splice(j, 1);
                }
              });
            }
          });

          return layoutLinks;
        }

        function filterLinks2(graph) {
          // 筛选用于布局的links
          var layoutLinks = [];
          for (var i = 0; i < graph.links.length; i++) {
            var link = graph.links[i];
            var sourceLevel = link.sourceNode.layout.level;
            var targetLevel = link.targetNode.layout.level;
            var sourceNode = link.sourceNode;
            var targetNode = link.targetNode;

            if (
              (sourceLevel == 1 && targetLevel == 2) ||
              (sourceLevel == 2 && targetLevel == 1)
            ) {
              layoutLinks.push(link);
            }
            if (
              (sourceLevel == 2 && targetLevel == 3) ||
              (sourceLevel == 3 && targetLevel == 2)
            ) {
              layoutLinks.push(link);
            }
          }

          return layoutLinks;
        }

        function initD3Data(graph) {
          //
          function getIndex(val, arr) {
            var index = 0;
            for (var i = 0; i < arr.length; i++) {
              var obj = arr[i];
              if (val == obj.nodeId) {
                index = i;
                break;
              }
            }
            return index;
          }

          /*封装符合d3的数据*/
          for (var i = 0; i < graph.nodes.length; i++) {
            var node = graph.nodes[i];
            node.id = node.nodeId;
          }

          for (var i = 0; i < graph.links.length; i++) {
            var link = graph.links[i];
            link.source = getIndex(link.sourceNode.nodeId, graph.nodes);
            link.target = getIndex(link.targetNode.nodeId, graph.nodes);
            link.index = i; //
          }

          graph.layoutLinks = filterLinks1(graph);

          // 围绕节点最大数值
          setSingleLinkNodes(graph.layoutLinks);
          graph.nodes.forEach(function (node, i) {
            if (
              node.layout.singleLinkChildren.length &&
              _maxChildrenLength < node.layout.singleLinkChildren.length
            ) {
              _maxChildrenLength = node.layout.singleLinkChildren.length;
            }
          });
          //console.log('围绕节点最大数值:' + _maxChildrenLength);
        }

        initD3Data(graph); //

        var width = $("#MainD3 svg").width();
        var height =  $("#MainD3 svg").height();

        var strength = -600,
          distanceMax = 330,
          theta = 0,
          distance = 130,
          colideRadius = 35,
          distanceMin = 400;
        // 根据节点数量调节
        if (graph.nodes.length < 50) {
          strength = -800;
          distanceMax = 400;
        } else if (graph.nodes.length > 50 && graph.nodes.length < 100) {
          strength = -800;
          distanceMax = 350;
          distance = 130;
          colideRadius = 35;
        } else if (graph.nodes.length > 100 && graph.nodes.length < 150) {
          strength = -900;
          distanceMax = 450;
        } else if (graph.nodes.length > 150 && graph.nodes.length < 200) {
          strength = -1000;
          distanceMax = 500;
        } else if (graph.nodes.length > 200) {
          strength = -1600;
          distanceMax = 500;
          (theta = 0.6), (distance = 100), (colideRadius = 35);
        }
        // 根据围绕数量调节
        if (_maxChildrenLength > 50 && _maxChildrenLength < 100) {
          strength = -2000;
          distanceMax = 500;
        } else if (_maxChildrenLength > 1000 && _maxChildrenLength < 2000) {
          strength = -4000;
          distanceMax = 1500;
        }

        d3.forceSimulation(graph.nodes)
          .force(
            "charge",
            d3
              .forceManyBody()
              .strength(strength)
              .distanceMax(distanceMax)
              .theta(theta)
          )
          .force("link", d3.forceLink(graph.layoutLinks).distance(distance))
          .force("center", d3.forceCenter(width / 2, height / 2))
          .force(
            "collide",
            d3.forceCollide().radius(function () {
              return colideRadius;
            })
          );
        //.on('tick',ticked);
      }
      //设置符合Layout的node
      function getLayoutNode(graphData) {
        var layoutNode = {
          current: _rootNode,
          level1: [],
          level2: [],
          level3: [],
          level4: [],
          level5: [],
          other: [],
        };

        graphData.nodes.forEach(function (node, i) {
          switch (node.layout.level) {
            case 1:
              layoutNode.level1.push(node);
              break;
            case 2:
              layoutNode.level2.push(node);
              break;
            case 3:
              layoutNode.level3.push(node);
              break;
            case 4:
              layoutNode.level4.push(node);
              break;
            case 5:
              layoutNode.level5.push(node);
              break;
            default:
              layoutNode.other.push(node);
              break;
          }
        });

        _layoutNode = layoutNode;
        console.log(_layoutNode);
        return layoutNode;
      }

      // 数据处理:设置唯一孩子
      function setSingleLinkNodes(links) {
        function isSingleLink(nodeId, links) {
          var hasLinks = 0;
          var isSingle = true;
          for (var i = 0; i < links.length; i++) {
            var link = links[i];
            if (
              link.targetNode.nodeId == nodeId ||
              link.sourceNode.nodeId == nodeId
            ) {
              hasLinks++;
            }
            if (hasLinks > 1) {
              isSingle = false;
              break;
            }
          }

          return isSingle;
        } // isSingleLink

        links.forEach(function (link, i) {
          if (isSingleLink(link.sourceNode.nodeId, links)) {
            link.targetNode.layout.singleLinkChildren.push(link.sourceNode);
          }
          if (isSingleLink(link.targetNode.nodeId, links)) {
            link.sourceNode.layout.singleLinkChildren.push(link.targetNode);
          }
        });
      }
      // 绘制图谱
      function drawGraph(elements) {
        _currentKeyNo,
          (_companyRadius = 35),
          (_personRadius = 15),
          (_circleMargin = 10),
          (_circleBorder = 3);
        cy = cytoscape({
          container: document.getElementById("MainCy"),
          motionBlur: false,
          textureOnViewport: false,
          wheelSensitivity: 0,
          elements: elements,
          minZoom: 0.4,
          maxZoom: 5,
          zoomingEnabled: true, //是否可缩放,改为false图谱的位置会靠左不居中
          userZoomingEnabled: false, //是否允许用户事件(例如鼠标轮、按下缩放)缩放图形.缩放的编程更改不受此选项的影响  -- 这里改为false,然后通过自定义事件来控制图谱的缩放
          layout: {
            name: "preset",
            componentSpacing: 40,
            nestingFactor: 12,
            padding: 10,
            edgeElasticity: 800,
            stop: function (e) {
              //解决浏览器标签切换排列问题
              if (document[state] == "hidden") {
                _isNeedReload = true;
                //                        console.log('stop _isNeedReload=true');
              } else {
                _isNeedReload = false;
              }
              setTimeout(function () {
                if (document[state] == "hidden") {
                  _isGraphLoaded = false;
                  console.log("stop _isGraphLoaded=false");
                } else {
                  _isGraphLoaded = true;
                }
              }, 1000);
            },
          },
          style: [
            {
              selector: "node",
              style: {
                shape: "ellipse",
                width: function (ele) {
                  //当前节点有图片
                  if (
                    ele.data("type") == "Person" &&
                    _currentKeyNo == ele.data("keyNo") &&
                    ele.data("hasImage")
                  ) {
                    return 80;
                  }
                  //有图片
                  if (ele.data("hasImage") && ele.data("type") == "Person") {
                    return 60;
                  }
                  //普通
                  if (ele.data("type") == "Company") {
                    return 60;
                  }
                  return 45;
                },
                height: function (ele) {
                  //当前节点有图片
                  if (
                    ele.data("type") == "Person" &&
                    _currentKeyNo == ele.data("keyNo") &&
                    ele.data("hasImage")
                  ) {
                    return 80;
                  }
                  //有图片
                  if (ele.data("hasImage") && ele.data("type") == "Person") {
                    return 60;
                  }
                  //普通
                  if (ele.data("type") == "Company") {
                    return 60;
                  }
                  return 45;
                },
                "background-color": function (ele) {
                  return ele.data("color");
                },
                "background-fit": "cover",
                "background-image": function (ele) {
                  var hasImage = ele.data("hasImage");
                  var keyNo = ele.data("keyNo");
                  var type = ele.data("type");
                  if (hasImage && type == "Person") {
                    return "/proxyimg_" + keyNo + ".jeg";
                  } else {
                    return "none";
                  }
                },
                // 'background-image-crossorigin': 'use-credentials',
                "border-color": function (ele) {
                  return ele.data("borderColor");
                },
                "border-width": function (ele) {
                  if (ele.data("hasImage") && ele.data("type") == "Person") {
                    return 3;
                  } else {
                    return 1;
                  }
                },
                "border-opacity": 1,
                label: function (ele) {
                  var label = ele.data("name");
                  var length = label.length;

                  if (length <= 5) {
                    // 4 5 4排列
                    return label;
                  } else if (length >= 5 && length <= 9) {
                    return (
                      label.substring(0, length - 5) +
                      "\n" +
                      label.substring(length - 5, length)
                    );
                  } else if (length >= 9 && length <= 13) {
                    return (
                      label.substring(0, 4) +
                      "\n" +
                      label.substring(4, 9) +
                      "\n" +
                      label.substring(9, 13)
                    );
                  } else {
                    return (
                      label.substring(0, 4) +
                      "\n" +
                      label.substring(4, 9) +
                      "\n" +
                      label.substring(9, 12) +
                      ".."
                    );
                  }
                },
                "z-index-compare": "manual",
                "z-index": 20,
                color: "#fff",
                //'padding-top':0,
                padding: function (ele) {
                  if (ele.data("type") == "Company") {
                    return 3;
                  }
                  return 0;
                },
                "font-size": 12,
                //'min-height':'400px',
                //'ghost':'yes',
                //'ghost-offset-x':300,
                //'font-weight':800,
                //'min-zoomed-font-size':6,
                "font-family": "microsoft yahei",
                "text-wrap": "wrap",
                "text-max-width": 60,
                "text-halign": "center",
                "text-valign": "center",
                "overlay-color": "#fff",
                "overlay-opacity": 0,
                "background-opacity": 1,
                "text-background-color": "#000",
                "text-background-shape": "roundrectangle",
                "text-background-opacity": function (ele) {
                  if (ele.data("hasImage") && ele.data("type") == "Person") {
                    return 0.3;
                  } else {
                    return 0;
                  }
                },
                "text-background-padding": 0,
                "text-margin-y": function (ele) {
                  //当前节点有图片
                  if (
                    ele.data("type") == "Person" &&
                    _currentKeyNo == ele.data("keyNo") &&
                    ele.data("hasImage")
                  ) {
                    return 23;
                  }
                  // 有图片
                  if (ele.data("hasImage") && ele.data("type") == "Person") {
                    return 16;
                  }
                  //
                  if (ele.data("type") == "Company") {
                    return 4;
                  }
                  return 2;
                },
              },
            },
            {
              selector: "edge",
              style: {
                "line-style": function (ele) {
                  return "solid";
                  /*if(ele.data('data').obj.type == 'INVEST'){
                                        return 'solid';
                                    } else {
                                        return 'dashed'
                                    }*/
                },
                "curve-style": "bezier",
                "control-point-step-size": 20,
                "target-arrow-shape": "triangle-backcurve",
                "target-arrow-color": function (ele) {
                  return ele.data("color");
                },
                "arrow-scale": 0.5,
                "line-color": function (ele) {
                  //return '#aaaaaa';
                  return ele.data("color");
                },
                label: function (ele) {
                  return ele.data("label");
                },
                "text-opacity": 0.8,
                "font-size": 12,
                "background-color": function (ele) {
                  return "#ccc";
                  // return ele.data("color");
                },
                width: 0.3,
                "overlay-color": "#fff",
                "overlay-opacity": 0,
                "font-family": "microsoft yahei",
              },
            },
            {
              selector: ".autorotate",
              style: {
                "edge-text-rotation": "autorotate",
              },
            },
            {
              selector: ".nodeActive",
              style: {
                /*'background-color':function (ele) {
                                    if(ele.data("category")==1){
                                        return "#5c8ce4"
                                    }
                                    return "#d97a3a";
                                },*/
                //'z-index':300,
                "border-color": function (ele) {
                  return ele.data("color");
                },
                "border-width": 10,
                "border-opacity": 0.5,
              },
            },
            {
              selector: ".edgeShow",
              style: {
                color: "#999",
                "text-opacity": 1,
                "font-weight": 400,
                label: function (ele) {
                  return ele.data("label");
                },
                "font-size": 10,
              },
            },
            {
              selector: ".edgeActive",
              style: {
                "arrow-scale": 0.8,
                width: 1.5,
                color: "#330",
                "text-opacity": 1,
                "font-size": 12,
                "text-background-color": "#fff",
                "text-background-opacity": 0.8,
                "text-background-padding": 0,
                "source-text-margin-y": 20,
                "target-text-margin-y": 20,
                //'text-margin-y':3,
                "z-index-compare": "manual",
                "z-index": 1,
                "line-color": function (ele) {
                  return ele.data("color");
                },
                "target-arrow-color": function (ele) {
                  return ele.data("color");
                },
                label: function (ele) {
                  /*if(ele.data('data').obj.type == 'INVEST'){
                                        return 'solid';
                                    } else {
                                        return 'dashed'
                                    }*/
                  return ele.data("label");
                },
              },
            },
            {
              selector: ".hidetext",
              style: {
                "text-opacity": 0,
                label: function (ele) {
                  return "";
                },
              },
            },
            {
              selector: ".dull",
              style: {
                "z-index": 1,
                opacity: 0.2,
              },
            },
            {
              selector: ".nodeHover",
              style: {
                shape: "ellipse",
                "background-opacity": 0.9,
              },
            },
            {
              selector: ".edgeLevel1",
              style: {
                label: function (ele) {
                  return ele.data("label");
                },
              },
            },
            {
              selector: ".edgeShowText",
              style: {
                label: function (ele) {
                  return ele.data("label");
                },
              },
            },
            {
              selector: ".lineFixed", // 加载完成后,加载该类,修复线有锯齿的问题
              style: {
                "overlay-opacity": 0,
              },
            },
          ],
        });
        /**
         * 鼠标点击节点后触发
         */
        cy.on("click", "node", function (evt) {
          console.log(evt,"111111");
          if (evt.target._private.style["z-index"].value == 20) {
            // 非暗淡状态
            _isFocus = true;
            var node = evt.target;

            highLight([node._private.data.id], cy);

            if (node.hasClass("nodeActive")) {
              activeNode = null;
              $("#company-detail").hide();
              node.removeClass("nodeActive");
              cy.collection("edge").removeClass("edgeActive");
            } else {
              var nodeData = node._private.data;
              if (nodeData.type == "Company") {
                showDetail2(nodeData.keyNo, "company_muhou3");
                cy.collection("node").addClass("nodeDull");
              } else {
                showDetail2(nodeData.keyNo, "company_muhou3", "person");
                cy.collection("node").addClass("nodeDull");
              }

              activeNode = node;
              cy.collection("node").removeClass("nodeActive");

              cy.collection("edge").removeClass("edgeActive");
              node.addClass("nodeActive");
              node.neighborhood("edge").removeClass("opacity");
              node.neighborhood("edge").addClass("edgeActive");
              node.neighborhood("edge").connectedNodes().removeClass("opacity");
            }
            //_firstTab = false;
          } else {
            _isFocus = false;
            activeNode = null;
            cy.collection("node").removeClass("nodeActive");
            $(".tp-detail").fadeOut();
            cancelHighLight();
          }
        });
        /**
         * 鼠标按下时触发
         */
        cy.on("vmousedown", "node", function (evt) {
          console.log(evt,"鼠标按下")
          var node = evt.target;
          if (!_isFocus) {
            highLight([node._private.data.id], cy);
          }
        });
        /**
         * 鼠标抬起/触摸结束时触发
         */
        cy.on("tapend", "node", function (evt) {
          if (!_isFocus) {
            cancelHighLight();
          }
        });
        var showTipsTime = null;
        /**
         * 鼠标悬停在节点上
         */
        cy.on("mouseover", "node", function (evt) {
          console.log(evt,"悬停")
          if (evt.target._private.style["z-index"].value == 20) {
            // 非暗淡状态
            //
            $("#Main").css("cursor", "pointer");

            //
            var node = evt.target;
            node.addClass("nodeHover");
            if (!_isFocus) {
              cy.collection("edge").removeClass("edgeShow");
              cy.collection("edge").removeClass("edgeActive");
              node.neighborhood("edge").addClass("edgeActive");
            }

            // 提示
            clearTimeout(showTipsTime);
            //if(node._private.data.name.length > 13 || (node._private.data.keyNo[0] == 'p' && node._private.data.name.length > 3) || node._private.data.layout.revel > 2){
            if (
              node._private.data.name.length > 13 ||
              (node._private.data.keyNo &&
                node._private.data.keyNo[0] == "p" &&
                node._private.data.name.length > 3)
            ) {
              showTipsTime = setTimeout(function () {
                var name = node._private.data.name;

                // 显示在节点位置
                /*var tipWidth = name.length * 12 + 16;
                                var x = node._private.data.d3x + 655 - (tipWidth / 2);
                                var y = node._private.data.d3y + 598;
                                if(node._private.data.type == 'Person'){
                                    y = node._private.data.d3y + 590;
                                }*/

                // 显示在鼠标位置
                var event = evt.originalEvent || window.event;
                var x = event.clientX + 10;
                var y = event.clientY + 10;

                var html =
                  "<div class='tips' style='font-size:12px;background:white;box-shadow:0px 0px 3px #999;border-radius:1px;opacity:1;padding:1px;padding-left:8px;padding-right:8px;display:none;position: absolute;left:" +
                  x +
                  "px;top:" +
                  y +
                  "px;'>" +
                  name +
                  "</div>";
                $("body").append($(html));
                $(".tips").fadeIn();
              }, 600);
            }
          }
        });
        /**
         * 鼠标移开节点
         */
        cy.on("mouseout", "node", function (evt) {
          $("#Main").css("cursor", "default");

          // 提示
          $(".tips").fadeOut(function () {
            $(".tips").remove();
          });

          clearTimeout(showTipsTime);

          //
          var node = evt.target;
          node.removeClass("nodeHover");
          if (!_isFocus) {
            cy.collection("edge").removeClass("edgeActive");
            /*if(moveTimeer){
                            clearTimeout(moveTimeer);
                        }*/
            /*moveTimeer = setTimeout(function() {
                            cy.collection("edge").addClass("edgeActive");
                            //cy.collection("edge").addClass("edgeShow");
                        }, 300);
                        if(activeNode){
                            activeNode.neighborhood("edge").addClass("edgeActive");
                        }*/
          }
        });
        /**
         * 鼠标悬停在连线上
         */
        cy.on("mouseover", "edge", function (evt) {
          console.log("鼠标悬停在连线上")
          if (!_isFocus) {
            var edge = evt.target;
            /*if(moveTimeer){
                            clearTimeout(moveTimeer);
                        }*/
            cy.collection("edge").removeClass("edgeActive");
            edge.addClass("edgeActive");
            /*if(activeNode){
                            activeNode.neighborhood("edge").addClass("edgeActive");
                        }*/
          }
        });
        /**
         * 鼠标离开连线
         */
        cy.on("mouseout", "edge", function (evt) {
          if (!_isFocus) {
            var edge = evt.target;
            edge.removeClass("edgeActive");
            // moveTimeer = setTimeout(function() {
            //     cy.collection("edge").addClass("edgeActive");
            //     //cy.collection("edge").addClass("edgeShow");
            // }, 400);
            if (activeNode) {
              activeNode.neighborhood("edge").addClass("edgeActive");
            }
          }
        });
        /**
         * 鼠标点击连线时触发
         */
        cy.on("click", "edge", function (evt) {
          _isFocus = false;
          activeNode = null;
          cy.collection("node").removeClass("nodeActive");
          $(".tp-detail").fadeOut();
          cancelHighLight();
        });
        /**
         * 点击画布,节点全部恢复高亮
         */
        cy.on("click", function (event) {
          var evtTarget = event.target;
          if (evtTarget === cy) {
            _isFocus = false;
            activeNode = null;
            cy.collection("node").removeClass("nodeActive");
            $(".tp-detail").fadeOut();
            cancelHighLight();
            focusCancel();
            filterReset();
          }
        });
        /**
         * 图谱缩放后触发
         */
        cy.on("zoom", function () {
          console.log("缩放zoom")
          console.log(cy.zoom());
          if (cy.zoom() < 0.5) {
            cy.collection("node").addClass("hidetext");
            cy.collection("edge").addClass("hidetext");
          } else {
            cy.collection("node").removeClass("hidetext");
            cy.collection("edge").removeClass("hidetext");
          }

          // 加载完成后,加载该类,修复线有锯齿的问题
          setTimeout(function () {
            cy.collection("edge").removeClass("lineFixed");
            cy.collection("edge").addClass("lineFixed");
          }, 200);
        });
        /**
         * 图谱被移动/拖拽后触发
         */
        cy.on("pan", function () {
          // 加载完成后,加载该类,修复线有锯齿的问题
          setTimeout(function () {
            cy.collection("edge").removeClass("lineFixed");
            cy.collection("edge").addClass("lineFixed");
          }, 200);
        });

        // 定位
        cy.nodes().positions(function (node, i) {
          // 保持居中
          if (node._private.data.keyNo == _currentKeyNo) {
            var position = cy.pan();
            cy.pan({
              x: position.x - node._private.data.d3x,
              y: position.y - node._private.data.d3y,
            });
          }

          //
          return {
            x: node._private.data.d3x,
            y: node._private.data.d3y,
          };
        });

        cy.ready(function () {
          if (!$("#TrTxt").hasClass("active")) {
            $("#TrTxt").click();
          }

          cy.zoom({
            level: 1, // the zoom level
          });
          $("#load_data").hide();
          //cy.$('#'+id).emit('tap');
          //cy.center(cy.$('#'+id));
          //cy.collection("edge").addClass("edgeActive");

          // 加载完成后,加载该类,修复线有锯齿的问题
          setTimeout(function () {
            cy.collection("edge").addClass("lineFixed");
          }, 400);

          // 首页的插入图谱默认高亮第一层
          if (
            _rootData &&
            _rootData.nodes.length > 30 &&
            typeof _INSERT_URL != "undefined" &&
            _INSERT_URL
          ) {
            highLight([_rootNode.nodeId], cy);
          }
        });

        cy.nodes(function (node) {
          /*
                    // 当前查询节点关系文字显示
                    if(node._private.data.nodeId == _rootNode.nodeId){
                        node.neighborhood("edge").addClass("edgeLevel1");
                    }*/
        });
      }
      /**
       * 图谱缩放
       * type == 1  放大
       * type == 2  缩小
       */
      function maoScale(type) {
        console.log(type,"type")
        var rate = 0.2;
        var scale = cy.zoom();
        if (type == 1) {
          scale += rate;
        } else if (type == 2) {
          scale -= rate;
        }

        cy.zoom({
          level: scale, // the zoom level
        });
      }
      function highLight(nodeIds, cy) {
        console.log(nodeIds, cy,"nodeIds, cy")
        cy.collection("node").removeClass("nodeActive");
        cy.collection("edge").removeClass("edgeActive");
        cy.collection("node").addClass("dull");
        cy.collection("edge").addClass("dull");

        for (var i = 0; i < nodeIds.length; i++) {
          var nodeId = nodeIds[i];
          cy.nodes(function (node) {
            var nodeData = node._private.data;
            if (nodeData.id == nodeId) {
              node.removeClass("dull");
              //node.addClass('nodeActive');
              node.neighborhood("edge").removeClass("dull");
              node.neighborhood("edge").addClass("edgeActive");
              node.neighborhood("edge").connectedNodes().removeClass("dull");
              //node.neighborhood("edge").connectedNodes().addClass("nodeActive");
            }
          });
        }
      }
      function highLightFilter(nodeIds, cy) {
        function isInNodeIds(nodeId) {
          for (var i = 0; i < nodeIds.length; i++) {
            if (nodeId == nodeIds[i]) {
              return true;
            }
          }
          return false;
        }

        cy.collection("node").removeClass("nodeActive");
        cy.collection("edge").removeClass("edgeActive");
        cy.collection("node").addClass("dull");
        cy.collection("edge").addClass("dull");

        for (var i = 0; i < nodeIds.length; i++) {
          var nodeId = nodeIds[i];
          cy.nodes(function (node) {
            var nodeData = node._private.data;
            if (nodeData.id == nodeId) {
              node.removeClass("dull");
              //node.addClass('nodeActive');
              /*node.neighborhood("edge").removeClass("dull");
                            node.neighborhood("edge").addClass("edgeActive");
                            node.neighborhood("edge").connectedNodes().removeClass("dull");*/
              //node.neighborhood("edge").connectedNodes().addClass("nodeActive");
            }
          });
        }

        cy.edges(function (edge) {
          var data = edge._private.data;
          if (isInNodeIds(data.target) && isInNodeIds(data.source)) {
            edge.removeClass("dull");
            edge.addClass("edgeActive");
          }
        });
      }
      function cancelHighLight() {
        cy.collection("node").removeClass("nodeActive");
        cy.collection("edge").removeClass("edgeActive");
        cy.collection("node").removeClass("dull");
        cy.collection("edge").removeClass("dull");
      }
      //筛选面板:聚焦准备
      function focusReady(node) {
        filterReset();
        $("#FocusInput").val(node.data.obj.properties.name);
        $("#FocusInput").attr("node_id", node.nodeId);
        $("#FocusBt").text("聚焦");
        $("#FocusBt").removeClass("focusDisable");
        $("#ClearInput").show();
      }
      //筛选面板:取消聚焦
      function focusCancel() {
        $("#ClearInput").hide();
        $("#FocusBt").text("聚焦");
        $("#FocusBt").addClass("focusDisable");
        $("#FocusInput").val("");
        $("#FocusInput").attr("node_id", "");
        selPanelUpdateList(_rootData.nodes, _rootData.links, true);
        cancelHighLight();
      }
      function filterReset() {
        $("#SelPanel").attr("param-level", "2");
        $("#SelPanel").attr("param-status", "");
        $("#SelPanel").attr("param-num", "");
        $("#SelPanel").attr("param-invest", "");

        $("#ShowLevel a").removeClass("active");
        $("#ShowLevel a").eq(1).addClass("active");
        $("#ShowStatus a").removeClass("active");
        $("#ShowStatus a").eq(0).addClass("active");
        $("#ShowInvest a").removeClass("active");
        $("#ShowInvest a").eq(0).addClass("active");
        $("#inputRange").val(0);
        $("#inputRange").css({ backgroundSize: "0% 100%" });
      }

      //将rootData转换成cy图谱框架所需要的数据结构
      function transformData(graphData) {
        function getLinkColor(type) {
          if (type == "INVEST") {
            return _COLOR.line.invest;
          } else if (type == "EMPLOY") {
            return _COLOR.line.employ;
          } else if (type == "LEGAL") {
            return _COLOR.line.legal;
          }
        }
        function getLinkLabel(link) {
          var type = link.data.obj.type,
            role = link.data.obj.properties.role;
            console.log(role,"role")
          if (type == "INVEST") {
            return "投资";
          } else if (type == "EMPLOY") {
            return role ? role : "任职";
          } else if (type == "LEGAL") {
            return "法定代表人";
          }
        }
        //getLayoutNode(graphData);

        //
        id = graphData.nodes[0].nodeId;
        var els = {};
        els.nodes = [];
        els.edges = [];

        graphData.links.forEach(function (link, i) {
          var color = getLinkColor(link.data.obj.type);
          var label = getLinkLabel(link);

          els.edges.push({
            data: {
              data: link.data,
              color: color,
              id: link.linkId,
              label: label,
              source: link.sourceNode.nodeId,
              target: link.targetNode.nodeId,
            },
            classes: "autorotate",
          });
        });

        graphData.nodes.forEach(function (node) {
          els.nodes.push({
            data: {
              nodeId: node.nodeId,
              type: node.data.obj.labels[0],
              keyNo: node.data.obj.properties.keyNo,
              data: node.data,
              id: node.nodeId,
              name: node.data.obj.properties.name,
              category: node.data.category,
              color: node.data.color,
              borderColor: node.data.strokeColor,
              layout: node.layout,
              d3x: node.x,
              d3y: node.y,
              hasImage: node.data.obj.properties.hasImage,
              //labelLine:1 // 解决文字行距问题,第1行
            },
          });
        });

        return els;
      }
      //筛选面板:列表更新
      function selPanelUpdateList(nodes, links, isShowCheckbox) {
        $(".tp-list").html("");
        for (var i = 0; i < nodes.length; i++) {
          var node = nodes[i];
          var index = i + 1;
          var name = node.data.obj.properties.name;
          var keyNo = node.data.obj.properties.keyNo;
          var str = "";
          if (isShowCheckbox) {
            str =
              '<div class="checkbox" node_id="' +
              node.nodeId +
              '" keyno="' +
              keyNo +
              '"> <input checked type="checkbox"><label> ' +
              index +
              "." +
              name +
              "</label> </div>";
            //            var str = '<div class="checkbox" node_id="'+ node.nodeId +'" keyno="'+ keyNo +'"> <label> ' + index + '.' + name + '</label> </div>';
          } else {
            str =
              '<div class="checkbox" node_id="' +
              node.nodeId +
              '" keyno="' +
              keyNo +
              '"><label> ' +
              index +
              "." +
              name +
              "</label> </div>";
          }

          $(".tp-list").append(str);
        }

        $(".tp-list > div > label").click(function () {
          var _parent = $(this).parent();
          var nodeId = _parent.attr("node_id");

          focusReady(getGraphNode(nodeId, nodes));
        });

        $(".tp-list > div > input").click(function () {
          /*var _this = $(this);
                    var _parent = $(this).parent();
                    var nodeId = _parent.attr('node_id');
                    var checkedNodeIds = $('.tp-list').attr('node_ids');
                    if(checkedNodeIds){
                        checkedNodeIds = checkedNodeIds.split(',');
                    }*/

          var checkedNodeIds = [];
          $(".tp-list input:checked").each(function () {
            var _parent = $(this).parent();
            var nodeId = _parent.attr("node_id");
            checkedNodeIds.push(nodeId);
          });

          /*if(_this.is(':checked')){
                        checkedNodeIds.push(nodeId);
                        nodes.splice(1,1);
                        console.log('checked');
                    } else {
                        console.log('un checked');
                        var sub_nodes = []
                        sub_nodes = nodes.splice(0,1);
                        console.log(nodes);
                        console.log(sub_nodes);
                        graphInit(nodes, links);
                    }*/
          highLight(checkedNodeIds, cy);
          /*// 需要隐藏的节点及子节点
                    var choosedNode = getGraphNode(nodeId,nodes);
                    var subNodes = getSubNodes(choosedNode,links);
                    subNodes.push(choosedNode);
            
                    // 剩下的节点
                    var lastNodes = [];
                    for(var i = 0; i < nodes.length; i++){
                        var node = nodes[i];
                        if(!getGraphNode(node.nodeId,subNodes)){
                            lastNodes.push(node);
                        }
                    }
            
                    // 剩下的连线
                    var lastLinks = filterLinksByNodes(lastNodes,links);
            
                    graphInit(lastNodes, lastLinks);
                    if(_this.is(':checked')){
                        nodes.splice(1,1);
                        console.log('checked');
                    } else {
                        console.log('un checked');
                        var sub_nodes = []
                        sub_nodes = nodes.splice(0,1);
                        console.log(nodes);
                        console.log(sub_nodes);
                        graphInit(nodes, links);
                    }
                    console.log(nodeId);*/
        });
      }
      function showDetail2(keyNo, tupuUrl, type) {
        console.log(keyNo);
      }

      // ---------------------------- domUpdate 调用的方法 ---- End

      // 数据处理:将原始数据转换成graph数据
      function getRootData(list) {
        // console.log(list);
        var graph = {};
        graph.nodes = [];
        graph.links = [];

        //graph.nodes
        for (var i = 0; i < list.length; i++) {
          var nodes = list[i].graph.nodes;
          for (var j = 0; j < nodes.length; j++) {
            var node = nodes[j];
            var o = {};
            o.nodeId = node.id;
            o.data = {};
            o.data.obj = node;
            //o.data.showStatus = 'NORMAL'; // NORMAL HIGHLIGHT DULL
            o.data.showStatus = null; // NORMAL HIGHLIGHT DULL
            o.layout = {};
            o.layout.level = null; // 1 当前查询节点
            o.layout.singleLinkChildren = []; // 只连接自己的node
            graph.nodes.push(o);

            // 设置_rootNode
            if (_currentKeyNo == o.data.obj.properties.keyNo) {
              _rootNode = o;
            }
          }
        }

        graph.nodes = uniqeByKeys(graph.nodes, ["nodeId"]);
        //graph.links
        for (var i = 0; i < list.length; i++) {
          var relationships = list[i].graph.relationships;

          for (var k = 0; k < relationships.length; k++) {
            var relationship = relationships[k];
            var o = {};
            o.data = {};
            o.data.obj = relationship;
            //o.data.showStatus = 'NORMAL'; // NORMAL HIGHLIGHT DULL
            o.data.showStatus = null; // NORMAL HIGHLIGHT DULL
            o.sourceNode = getGraphNode(relationship.startNode, graph.nodes);
            o.targetNode = getGraphNode(relationship.endNode, graph.nodes);
            o.linkId = relationship.id;
            o.source = getNodesIndex(relationship.startNode, graph.nodes);
            o.target = getNodesIndex(relationship.endNode, graph.nodes);
            graph.links.push(o);
          }
        }
        graph.links = uniqeByKeys(graph.links, ["linkId"]);

        // console.log(graph);
        //emplyRevert(graph.links);
        //mergeLinks(graph.links);
        setLevel(graph.nodes, graph.links);
        setCategoryColor(graph.nodes, graph.links);

        return graph;
      }
      //  --------------------------- getRootData()引用的函数 ----- Begin
      //去重操作,元素为对象
      function uniqeByKeys(array, keys) {
        //将对象元素转换成字符串以作比较
        function obj2key(obj, keys) {
          var n = keys.length,
            key = [];
          while (n--) {
            key.push(obj[keys[n]]);
          }
          return key.join("|");
        }

        var arr = [];
        var hash = {};
        for (var i = 0, j = array.length; i < j; i++) {
          var k = obj2key(array[i], keys);
          if (!(k in hash)) {
            hash[k] = true;
            arr.push(array[i]);
          }
        }
        return arr;
      }
      // 数据处理:根据nodeId获取node
      function getGraphNode(nodeId, nodes) {
        var node = null;
        for (var i = 0; i < nodes.length; i++) {
          if (nodes[i].nodeId == nodeId) {
            node = nodes[i];
            break;
          }
        }
        return node;
      }
      // 数据处理:根据nodeId获取node 索引
      function getNodesIndex(nodeId, nodes) {
        var index = 0;
        for (var i = 0; i < nodes.length; i++) {
          var node = nodes[i];
          if (nodeId == node.nodeId) {
            index = i;
            break;
          }
        }
        return index;
      }
      // 数据处理:设置节点层级
      function setLevel(svg_nodes, svg_links) {
        function getNextNodes(nodeId, links, parentLevel) {
          var nextNodes = [];
          for (var i = 0; i < links.length; i++) {
            var link = links[i];
            if (
              nodeId == link.sourceNode.nodeId &&
              !link.targetNode.layout.level
            ) {
              link.targetNode.layout.level = parentLevel;
              nextNodes.push(link.targetNode);
            } else if (
              nodeId == link.targetNode.nodeId &&
              !link.sourceNode.layout.level
            ) {
              link.sourceNode.layout.level = parentLevel;
              nextNodes.push(link.sourceNode);
            }
          }
          nextNodes = uniqeByKeys(nextNodes, ["nodeId"]);

          return nextNodes;
        }

        var level = 1;
        var nodes = [];
        nodes.push(_rootNode);
        while (nodes.length) {
          var nextNodes = [];
          for (var i = 0; i < nodes.length; i++) {
            var node = nodes[i] || {};
            node.layout.level = level;
            nextNodes = nextNodes.concat(
              getNextNodes(node.nodeId, svg_links, level)
            );
          }
          level++;
          nodes = nextNodes;
        }
      }
      // 数据处理:设置节点角色
      function setCategoryColor(nodes, links) {
        for (var i = 0; i < links.length; i++) {
          var sameLink = {}; // 两点间连线信息
          sameLink.length = 0; // 两点间连线数量
          sameLink.currentIndex = 0; // 当前线索引
          sameLink.isSetedSameLink = false;
          links[i].sameLink = sameLink;
        }

        /*链接相同两点的线*/
        for (var i = 0; i < links.length; i++) {
          var baseLink = links[i];

          if (baseLink.sameLink.isSetedSameLink == false) {
            baseLink.sameLink.isSetedSameLink = true;
            var nodeId1 = baseLink.sourceNode.nodeId;
            var nodeId2 = baseLink.targetNode.nodeId;

            var sameLinks = [];
            sameLinks.push(baseLink);
            for (var j = 0; j < links.length; j++) {
              var otherLink = links[j];
              if (
                baseLink.linkId != otherLink.linkId &&
                !otherLink.sameLink.isSetedSameLink
              ) {
                if (
                  (otherLink.sourceNode.nodeId == nodeId1 &&
                    otherLink.targetNode.nodeId == nodeId2) ||
                  (otherLink.sourceNode.nodeId == nodeId2 &&
                    otherLink.targetNode.nodeId == nodeId1)
                ) {
                  sameLinks.push(otherLink);
                  otherLink.sameLink.isSetedSameLink = true;
                }
              }
            }

            for (var k = 0; k < sameLinks.length; k++) {
              var oneLink = sameLinks[k];
              oneLink.sameLink.length = sameLinks.length; // 两点间连线数量
              oneLink.sameLink.currentIndex = k; // 当前线索引
            }
          }
        }

        for (var i = 0; i < nodes.length; i++) {
          var node = nodes[i];
          if (_currentKeyNo == node.data.obj.properties.keyNo) {
            // 当前节点
            node.data.color = _COLOR.node.current;
            node.data.strokeColor = _COLOR.border.current;
          } else if (node.data.obj.labels[0] == "Company") {
            node.data.color = _COLOR.node.company;
            node.data.strokeColor = _COLOR.border.company;
          } else {
            node.data.color = _COLOR.node.person;
            node.data.strokeColor = _COLOR.border.person;
          }
        }
      }
      //  --------------------------- getRootData()引用的函数 ----- End
    }
  }
};
</script>

style

#relation_graph {
  background: #fff;
  position: relative;
  #MainD3{
    position: absolute;
    bottom: 0;
    width: 0;
    height: 0;
  }
}

总结

写这个文档主要是为了记录一下,方便日后查看。
持续更新。。。

参考网站

svg
d3中文网站

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
禁止转载,如需转载请通过简信或评论联系作者。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,470评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,393评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,577评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,176评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,189评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,155评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,041评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,903评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,319评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,539评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,703评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,417评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,013评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,664评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,818评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,711评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,601评论 2 353

推荐阅读更多精彩内容