1. 程式人生 > >基於cytoscape.js 、 d3.js實現的關係圖譜初級版本

基於cytoscape.js 、 d3.js實現的關係圖譜初級版本

前面的文章已經介紹了cytoscape.js 、 d3.js的安裝及簡單demo,現在展示從html頁面轉移到vue專案下的最初版的demo

效果圖:

 

程式碼如下:

<template>
    <div style="width: 100%;height: 100%;">
        <div id="MainCy" style="width: 100%;height: 100%;"></div>
        <div id="MainD3" scale="1" class="no-padding tp-container"
>111111 <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> <script> export default { mounted() { /** 網頁當前狀態判斷 (解決沒佈局完就切換頁面造成點聚集在一起)*/ 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"; getData(_currentKeyNo); } document.title = 'hidden-not-loaded' } else { if(!_isGraphLoaded){ _isNeedReload = true; } } }, false); /** end 解決瀏覽器標籤切換排列問題 */ $(document).ready(function () { // 獲取資料 _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){ // 模擬資料 graphDatas = [{ graph: { nodes:[ { id:"75351209", labels:["Company"], properties:{ econKind:"有限責任公司(法人獨資)1", hasImage:false, keyNo:"e6b98b6db9ac8bbee4906e92fc01e4c9", name:"北京蘭貝斯科技有限公司1", registCapi:"1000.000", status:"在業", } }, { id:"36341010", labels:["Person"], properties:{ keyNo:"ea5e4e0a1ddfaf97ffa6720af99e54ea", name:"張三", registCapi:"1000.000", role:"監事", } }, { id:"363410102", labels:["Company"], properties:{ keyNo:"ea5e4e0a1ddfaf97ffa6720af99e54ea1", name:"張三1", registCapi:"1000.000", role:"監事1", } }, { id:"363410103", labels:["Person"], properties:{ keyNo:"ea5e4e0a1ddfaf97ffa6720af99e54ea2", name:"張三2", registCapi:"1000.000", role:"監事2", } } ], relationships: [ { id:"62566458", startNode:"75351209", endNode:"36341010", type:"INVEST", properties: { role:"自然人股東", shouldCapi:50, stockPercent:50 } }, { id:"363403", startNode:"363410103", endNode:"36341010", type:"INVEST", properties: { role:"自然人股東", shouldCapi:50, stockPercent:50 } }, { id:"3634031", startNode:"363410103", endNode:"363410102", type:"INVEST", properties: { role:"自然人股東", shouldCapi:111, stockPercent:500 } } ], }, }]; _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"); }, } },