當前位置:
首頁 > 知識 > Echarts關係圖-力引導布局

Echarts關係圖-力引導布局

需要做一個樹形圖,可以查看各個人員的關係。

可伸縮的力引導圖-失敗

剛開始,打算做一個可展開和伸縮的,搜索時候發現CSDN有一篇美美噠程序媛寫的Echarts Force力導向圖實現節點可摺疊。

這裡放上前輩的代碼

/**
這段代碼來自 http://blog.csdn.net/r4NqiAn/article/details/48320487
Echarts-Force
力導向布局圖樹狀結構實現節點可摺疊效果
作者:Reese
日期:2015-09-09
版本:V0.1
功能:點擊一次節點,展開一級子節點;再次點擊節點,摺疊所有子孫節點;
彈出最終子節點的標籤
備註:在使用該方法的時候,在nodes的屬性里要自定義flag屬性,並設置ignore
*/
var ecConfig = require("echarts/config");
function openOrFold(param){
var linksNodes=;//中間變數
var data=param.data;//表示當前選擇的某一節點

var option = myChart.getOption;//獲取已生成圖形的Option
var nodesOption=option.series[0].nodes;//獲得所有節點的數組
var linksOption=option.series[0].links;//獲得所有連接的數組
var categoryLength=option.series[0].categories.length;//獲得類別數組的大小

/**
該段代碼判斷當前節點的category是否為最終子節點,
如果是,則彈出該節點的label
*/
if(data.category==(categoryLength-1)){
alert(data.label);
}

/**判斷是否選擇到了連接線上*/
if(data != null && data != undefined){
/**
判斷所選節點的flag
如果為真,則表示要展開數據,
如果為假,則表示要摺疊數據
*/
if (data.flag) {
/**
遍歷連接關係數組
最終獲得所選擇節點的一層子節點
*/
for(var m in linksOption){
//引用的連接關係的目標,既父節點是當前節點
if(linksOption[m].target==data.id){
linksNodes.push(linksOption[m].source);//獲得子節點數組
}
}//for(var m in linksOption){...}
/**
遍歷子節點數組
設置對應的option屬性
*/
if(linksNodes != null && linksNodes != undefined){
for(var p in linksNodes){
nodesOption[linksNodes[p]].ignore = false;//設置展示該節點
nodesOption[linksNodes[p]].flag = true;
}
}
//設置該節點的flag為false,下次點擊摺疊子孫節點
nodesOption[data.id].flag = false;
//重繪
myChart.setOption(option);
}else{
/**
遍歷連接關係數組
最終獲得所選擇節點的所有子孫子節點
*/
for(var m in linksOption){
//引用的連接關係的目標,既父節點是當前節點
if(linksOption[m].target==data.id){
linksNodes.push(linksOption[m].source);//找到當前節點的第一層子節點
}
if(linksNodes != null && linksNodes != undefined){
for(var n in linksNodes){
//第一層子節點作為父節點,找到所有子孫節點
if(linksOption[m].target==linksNodes[n]){
linksNodes.push(linksOption[m].source);
}
}
}
}//for(var m in linksOption){...}
/**
遍歷最終生成的連接關係數組
*/
if(linksNodes != null && linksNodes != undefined){
for(var p in linksNodes){
nodesOption[linksNodes[p]].ignore = true;//設置摺疊該節點
nodesOption[linksNodes[p]].flag = true;
}
}
//設置該節點的flag為true,下次點擊展開子節點
nodesOption[data.id].flag = true;
//重繪
myChart.setOption(option);
}//if (data.flag) {...}
}//if(data != null && data != undefined){...}
}//function openOrFold(param){...}
myChart.on(ecConfig.EVENT.CLICK, openOrFold);

看了一下,思路很清晰。然後開始做,發現她的這代碼有個問題就是摺疊如果多層會有摺疊不上的情況,也可能是我自己代碼的原因。

需注意

1.在Echarts3中沒有ignore屬性,我發現data.category如果對應值不存在的話,就會不顯示節點,所以,再點擊的時候設置子節點x.category=x.category*-1;就可隱藏,顯示時候同樣反轉就行。有需要特殊隱藏稍加一點判斷就行。

nodesOption[linksNodes[p]].category = nodesOption[linksNodes[p]].category*-1;

2.不用多加自定義屬性去摺疊了,隱藏了就摺疊了,但是得獲取到遞歸獲取到所有子id。這裡還有些殘留的代碼片段

//先判斷是要展開還是閉合:如果有一個category為正,則閉合;否則擴展一層 。
expend=true;

for ( var p in linksNodes) {
if(nodesOption[linksNodes[p]].category>0 ){ //&& !is_exist(linksOption,nodesOption,nodesOption[linksNodes[p]].id)
expend=false;
nodesOption[linksNodes[p]].category=Math.abs(nodesOption[linksNodes[p]].category)*-1;
}
nodesOption[linksNodes[p]].category=Math.abs(nodesOption[linksNodes[p]].category)*-1; //順便使所有的值一致,全置為負值。因為關閉全關閉,展開只展開一層(特例)下面做處理就好、
//console.log(p+":"+nodesOption[linksNodes[p]].category)
}

if(expend){ //展開 一層
linksNodeArrs=;
linksNodes_in=get_id(linksOption,data.id,0);
//console.log(linksNodes_in)
for ( var p in linksNodes_in) {
nodesOption[linksNodes_in[p]].category = nodesOption[linksNodes_in[p]].category*-1;
}
}else{ //閉合不需要做處理

}

//遞歸取所有 id
function get_id(arr,cId,f=1){
for ( var m in arr) {
if (arr[m].source == cId) {
linksNodeArrs.push(arr[m].target);
//linksNodeArrs.push(m);
//console.log(arr[m].target);
if(f)
get_id(arr,arr[m].target);
}
}
return linksNodeArrs;
}

Echarts關係圖-力引導布局

圖一為簡單的擴展摺疊,完美

Echarts關係圖-力引導布局

圖二為有問題的摺疊,點擊[設計師B]時,[項目1]和[廠商D]應該存在,因為還有別的路徑,邏輯錯誤,這裡要弄摺疊肯定得通過有向圖比較好解決。不過這裡暫時用不到,以後有機會填此坑。

那麼,不考慮摺疊了,考慮單擊出相關一級子節點,雙擊只顯示此節點的第一級。

可擴展,以及單獨顯示的力引導圖

為了以後需要查資料庫動態獲取數據,所以暫時把關係都存到資料庫里。主要有兩組數據,一組是每個節點(數據),一組是他們的關係即連接線 。有人可能會想,這想一種數據結構,對,這就是有向圖 :

Echarts關係圖-力引導布局

扯回來,代碼只是演示相關功能並不完善,這裡將不詳細介紹各代碼,只是簡單的取資料庫而已,Echarts基礎請看官方文檔和技術文章。

資料庫簡單設計三個,categories各分類,nodes數據節點,links邊(可以看到保存了關係)

Echarts關係圖-力引導布局

TP代碼:

#查詢最基本的顯示
public function index{
####分類查詢####
$category=M("categories")->field("name")->where("name is not null")->order("id asc")->select;//註:需要從0排起
//legend,頁頂部的標籤 -可空
$legend_data=" ";
foreach($category as $v){
$legend_data.=""{$v["name"]}",";
}
$legend_data=substr($legend_data,0,-1);
$this->assign("legend_data",$legend_data);

//分類,與legend同數據
$json_cate=json_encode($category,JSON_NUMERIC_CHECK);
$this->assign("json_cate",$json_cate);

####連接查詢#### 查詢第一層
$links_data=M("links")->field("source,target,value,id as id_")->order("id asc")->where("source=0")->select;//

$id_str="0,";
foreach($links_data as $v){
$id_str.="{$v["target"]},";
}

$id_str=substr($id_str,0,-1);
$links_data2=M("links")->field("source,target,value,id as id_")->order("id asc")->where(" `source` in (%s) ",$id_str)->select;

foreach($links_data2 as $k=>$v){
$links_data=$v;
}

$json_links=json_encode($links_data);
$this->assign("json_links",$json_links);
####數據查詢####
$where=array;
$where["id"]=array("in",$id_str);
$nodes_data=M("nodes")->field("id as id,name,category")->order("id asc")->where($where)->select;

$json_nodes=json_encode($nodes_data,JSON_NUMERIC_CHECK);
//echo $json_nodes;
$this->assign("json_nodes",$json_nodes);

$this->display;
}
  
public function only_show{ //only_show函數等下點擊節點時候ajax用。
$selfid=I("post.id");
####連接查詢####
$map=array;
$map["_query"] = "source=$selfid&target=$selfid&_logic=or";
$links_data=M("links")->field("source,target,value")->order("id asc")->where($map)->select;

$id_str=" ";

foreach($links_data as $v){
$id_str.="{$v["target"]},";
$id_str.="{$v["source"]},";
}
$id_str=substr($id_str,0,-1);

$links_data=M("links")->field("source,target,value,id as id_")->order("id asc")->where(" `source` in (%s) or `target` in (%s) ",$id_str,$id_str)->select;

$json_links=json_encode($links_data);
$result["json_links"]=$json_links;

####數據查詢####
$where["id"]=array("in",$id_str);
$nodes_data=M("nodes")->field("id as id,name,category")->order("id asc")->where($where)->select;

$json_nodes=json_encode($nodes_data,JSON_NUMERIC_CHECK);
$result["json_nodes"]=$json_nodes;
$this->ajaxReturn($result);
}

視圖代碼:

1 <!DOCTYPE html>
2 <html>
3 <head>
4 <title>關係圖試驗</title>
5 <meta charset="utf-8">
6 <script type="text/javascript" src="__PUBLIC__/js/echarts.js"></script>
7 <script type="text/javascript" src="__PUBLIC__/js/jquery.js"></script>
8
9 </head>
10 <body>
11 <div id="main" stylex="width: 1000px;height:600px;"></div>
12 <script type="text/javascript">
13 var linksNodeArrs=;
14 var TimeFn=null;
15 $(function{
16
17 option = {
18 title: {
19 text: "測試關係圖"
20 },
21 tooltip: {},
22 //animationDurationUpdate: 1500,
23 animationEasingUpdate: "quinticInOut",
24 label: {
25 normal: {
26 show: true,
27 textStyle: {
28 fontSize: 12
29 },
30 }
31 },
32 legend: {
33 x: "center",
34 show: true,
35 data: [{$legend_data}]
36 },
37 series: [
38 {
39 //name:"系列名",
40 type: "graph",
41 layout: "force",
42 symbolSize: 45,
43 focusNodeAdjacency: true, //突出相關
44 roam: true, //滑鼠縮放、平移
45 force: { //斥力因子
46 repulsion: 500,
47 edgeLength:[100,200],
48 },
49 draggable:true,
50 tooltip:{
51 trigger:"item",
52 backgroundColor: "rgba(245, 244, 237,0.7)" ,//提示框浮動背景色
53 borderColor:"black",
54 borderWidth:1,
55 textStyle:{
56 color:"black",
57 fontWeight:"bold",
58
59 }
60 },
61
62 categories: {$json_cate},
63 label: {
64 normal: {
65 show: true,
66 textStyle: {
67 fontSize: 12
68 },
69 }
70 },
71
72 edgeSymbolSize: [0, 10],
73 edgeSymbol:"arrow",
74
75 edgeLabel: {
76 normal: {
77 show: true,
78 textStyle: {
79 fontSize: 10
80 },
81 formatter: "{b}"
82 }
83 },
84 nodes: {$json_nodes},
85 links: {$json_links},
86 lineStyle: {
87 normal: {
88 opacity: 0.9,
89 width: 1,
90 curveness: 0,
91 shadowColor: "rgba(0, 0, 0, 0.8)",
92 shadowBlur: 5,
93 shadowOffsetX:3,
94 shadowOffsetY:3,
95 },
96 emphasis:{
97 width:3 //hover時改變線寬
98 }
99 },
100
101
102 }
103 ]
104 };
105
106 //console.log(option)
107 var myChart = echarts.init(document.getElementById("main"));
108 myChart.setOption(option);
109
110 //↑上面為載入完後初次顯示
111
112 myChart.on("click", cevent);
113 myChart.on("dblclick", dbcevent);
114
115 function cevent(param) {
116 clearTimeout(TimeFn);
117 //執行延時
118 TimeFn = setTimeout(function{
119 var option = myChart.getOption;
120 var data = param.data;
121 if (data != null && data != undefined) {
122 cid=data.id;
123 $.post("{:U("only_show")}",{id:cid},function(rs){
124 var json_nodes = eval("(" + rs.json_nodes + ")");
125 var json_links = eval("(" + rs.json_links + ")");
126
127 for(var v in json_nodes){
128 var exist=false;//是否存在
129 for(var i in option.series[0].nodes){
130
131 if(json_nodes[v]["id"] == option.series[0].nodes[i]["id"] || json_nodes[v]["name"] == option.series[0].nodes[i]["name"]){
132 exist=true;
133 break;
134 }
135 }
136 if(!exist){
137 option.series[0].nodes.push(json_nodes[v]);
138 }
139 }
140
141 for(var v in json_links){
142 var exist=false; //是否存在
143 for(var i in option.series[0].links){
144 if(json_links[v]["id_"] == option.series[0].links[i]["id_"]){
145 exist=true;
146 break;
147 }
148 }
149 if(!exist){
150 option.series[0].links.push(json_links[v]);
151 }
152 }
153
154 option.series[0].nodes=quicksort(option.series[0].nodes)
155
156 //option.series[0].nodes=json_nodes;
157 //option.series[0].links=json_links;
158 myChart.setOption(option);
159 })
160 }
161 },300);
162 }
163
164
165
166 function dbcevent(param) {
167 clearTimeout(TimeFn);
168 var option = myChart.getOption;
169 var data = param.data;
170 if (data != null && data != undefined) {
171 cid=data.id;
172 $.post("{:U("only_show")}",{id:cid},function(rs){
173 //console.log(rs);
174 var json_nodes = eval("(" + rs.json_nodes + ")");
175 var json_links = eval("(" + rs.json_links + ")");
176 option.series[0].nodes=json_nodes;
177 option.series[0].links=json_links;
178 myChart.setOption(option);
179 })
180 }
181 }
182
183 //冒泡
184 function bubbleSort(array){
185 var i = 0,
186 len = array.length,
187 j,d;
188 for(;i<len;i++){
189 for(j=0;j<len;j++){
190 if(array[i]["id"]<array[j]["id"]){
191 d=array[j];
192 array[j]=array[i];
193 array[i]=d;
194 }
195 }
196 }
197 return array;
198 }
199 //快速
200 function quicksort(arr){
201 if (arr.length == 0)
202 return ;
203
204 var left = new Array;
205 var right = new Array;
206 var pivot = arr[0];
207
208 for (var i = 1; i < arr.length; i++) {
209 if (arr[i]["id"] < pivot["id"]) {
210 left.push(arr[i]);
211 } else {
212 right.push(arr[i]);
213 }
214 }
215
216 return quicksort(left).concat(pivot, quicksort(right));
217 }
218
219 });
220 </script>
221
222 </body>
223
224 </html>

稍微說明一下:

  • cevent函dbcevent函數單擊雙擊效果加了延遲是因為,在我測試的時候,雙擊也會觸發到兩次單擊然後才執行雙擊,重複執行。加延遲為了解決這個問題。
  • 154行在設置重置前用了個排序去排nodes,是因為,在亂序模式中不排序會造成錯誤,links的source和target用不到nodes裡面的id值,就會去用索引的值,放兩個圖看效果。
  • p.s.本來測試的時候隨便找了個冒泡排序排了序測試了下,最後寫博客才想用個快排,放個冒泡確實比較不提倡。

Echarts關係圖-力引導布局

Echarts關係圖-力引導布局

最後放一個完結圖:單擊擴展一級節點,雙擊已節點擴展一級!

Echarts關係圖-力引導布局

參考文獻

4.

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 科技優家 的精彩文章:

自動生成proto Js語句
原型原型鏈學習記錄
iOS用域名或主機名獲取IP地址
默默前行的livego——基於go語言的rtmp直播伺服器

TAG:科技優家 |

您可能感興趣

NetBooster Asia與Artefact完成併購,致力於加強區域布局
讀 SnapKit和Masonry 自動布局框架源碼
iOS版Microsoft Edge Beta繼續優化 iPad布局更和諧
Arm宣布收購Stream Technologies 意在加快物聯網布局
布局大殺器—ConstraintLayout
Facebook將對Oculus部門進行布局AR/VR
Microsoft Store布局調整:新增「Departments」下拉菜單
類索尼PS Vita布局風格,GPD Win Max Windows 10微型掌機曝光
Imec聯手ASML布局post-3nm微影技術
Apple Watch Series 4拆解:內部布局更周到
布局 Micro LED?歐司朗光電半導體與 X-Celeprint 簽技術和專利授權合約
Label顯示圖片,Pack布局控制項
B站與索尼旗下Funimation宣布合作 布局動畫領域
網易牽手Survios布局VR《Raw Data》線下版首發
UIUC & Zillow提出LayoutNet:從單個RGB圖像中重建3D房間布局
博世全球布局Ultimaker 3D印表機
銳成芯微布局RISC-V,加入SiFIVE DesignShare項目
沖Echo Show而來?Facebook智能音箱的布局不限於此
TalkingData發布全新戰略布局,大數據助力多行業發展
布局完成!Astell&Kern 新子品牌誕生