需要做一个树形图,可以查看各个人员的关系。

可伸缩的力引导图-失败

刚开始,打算做一个可展开和伸缩的,搜索时候发现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;
}

图一为简单的扩展折叠,完美

图二为有问题的折叠,点击[设计师B]时,[项目1]和[厂商D]应该存在,因为还有别的路径,逻辑错误,这里要弄折叠肯定得通过有向图比较好解决。不过这里暂时用不到,以后有机会填此坑。

那么,不考虑折叠了,考虑单击出相关一级子节点,双击只显示此节点的第一级。

可扩展,以及单独显示的力引导图

为了以后需要查数据库动态获取数据,所以暂时把关系都存到数据库里。主要有两组数据,一组是每个节点(数据),一组是他们的关系即连接线 。有人可能会想,这想一种数据结构,对,这就是有向图 :

扯回来,代码只是演示相关功能并不完善,这里将不详细介绍各代码,只是简单的取数据库而已,Echarts基础请看官方文档和技术文章。

数据库简单设计三个,categories各分类,nodes数据节点,links边(可以看到保存了关系)

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,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);
}

视图代码:

 <!DOCTYPE html>
<html>
<head>
<title>关系图试验</title>
<meta charset="utf-8">
<script type="text/javascript" src="__PUBLIC__/js/echarts.js"></script>
<script type="text/javascript" src="__PUBLIC__/js/jquery.js"></script> </head>
<body>
<div id="main" style="width: 1000px;height:600px;"></div>
<script type="text/javascript">
var linksNodeArrs=[];
var TimeFn=null;
$(function(){ option = {
title: {
text: '测试关系图'
},
tooltip: {},
//animationDurationUpdate: 1500,
animationEasingUpdate: 'quinticInOut',
label: {
normal: {
show: true,
textStyle: {
fontSize: 12
},
}
},
legend: {
x: "center",
show: true,
data: [{$legend_data}]
},
series: [
{
//name:'系列名',
type: 'graph',
layout: 'force',
symbolSize: 45,
focusNodeAdjacency: true, //突出相关
roam: true, //鼠标缩放、平移
force: { //斥力因子
repulsion: 500,
edgeLength:[100,200],
},
draggable:true,
tooltip:{
trigger:'item',
backgroundColor: 'rgba(245, 244, 237,0.7)' ,//提示框浮动背景色
borderColor:'black',
borderWidth:1,
textStyle:{
color:'black',
fontWeight:'bold', }
}, categories: {$json_cate},
label: {
normal: {
show: true,
textStyle: {
fontSize: 12
},
}
}, edgeSymbolSize: [0, 10],
edgeSymbol:'arrow', edgeLabel: {
normal: {
show: true,
textStyle: {
fontSize: 10
},
formatter: "{b}"
}
},
nodes: {$json_nodes},
links: {$json_links},
lineStyle: {
normal: {
opacity: 0.9,
width: 1,
curveness: 0,
shadowColor: 'rgba(0, 0, 0, 0.8)',
shadowBlur: 5,
shadowOffsetX:3,
shadowOffsetY:3,
},
emphasis:{
width:3 //hover时改变线宽
}
}, }
]
}; //console.log(option)
var myChart = echarts.init(document.getElementById('main'));
myChart.setOption(option); //↑上面为加载完后初次显示 myChart.on('click', cevent);
myChart.on('dblclick', dbcevent); function cevent(param) {
clearTimeout(TimeFn);
//执行延时
TimeFn = setTimeout(function(){
var option = myChart.getOption();
var data = param.data;
if (data != null && data != undefined) {
cid=data.id;
$.post("{:U('only_show')}",{id:cid},function(rs){
var json_nodes = eval('(' + rs.json_nodes + ')');
var json_links = eval('(' + rs.json_links + ')'); for(var v in json_nodes){
var exist=false;//是否存在
for(var i in option.series[0].nodes){ if(json_nodes[v]['id'] == option.series[0].nodes[i]['id'] || json_nodes[v]['name'] == option.series[0].nodes[i]['name']){
exist=true;
break;
}
}
if(!exist){
option.series[0].nodes.push(json_nodes[v]);
}
} for(var v in json_links){
var exist=false; //是否存在
for(var i in option.series[0].links){
if(json_links[v]['id_'] == option.series[0].links[i]['id_']){
exist=true;
break;
}
}
if(!exist){
option.series[0].links.push(json_links[v]);
}
} option.series[0].nodes=quicksort(option.series[0].nodes) //option.series[0].nodes=json_nodes;
//option.series[0].links=json_links;
myChart.setOption(option);
})
}
},300);
} function dbcevent(param) {
clearTimeout(TimeFn);
var option = myChart.getOption();
var data = param.data;
if (data != null && data != undefined) {
cid=data.id;
$.post("{:U('only_show')}",{id:cid},function(rs){
//console.log(rs);
var json_nodes = eval('(' + rs.json_nodes + ')');
var json_links = eval('(' + rs.json_links + ')');
option.series[0].nodes=json_nodes;
option.series[0].links=json_links;
myChart.setOption(option);
})
}
} //冒泡
function bubbleSort(array){
var i = 0,
len = array.length,
j,d;
for(;i<len;i++){
for(j=0;j<len;j++){
if(array[i]['id']<array[j]['id']){
d=array[j];
array[j]=array[i];
array[i]=d;
}
}
}
return array;
}
//快速
function quicksort(arr){
if (arr.length == 0)
return []; var left = new Array();
var right = new Array();
var pivot = arr[0]; for (var i = 1; i < arr.length; i++) {
if (arr[i]['id'] < pivot['id']) {
left.push(arr[i]);
} else {
right.push(arr[i]);
}
} return quicksort(left).concat(pivot, quicksort(right));
} });
</script> </body> </html>

稍微说明一下:

  • cevent()和dbcevent()函数单击双击效果加了延迟是因为,在我测试的时候,双击也会触发到两次单击然后才执行双击,重复执行。加延迟为了解决这个问题。
  • 154行在设置重置前用了个排序去排nodes,是因为,在乱序模式中不排序会造成错误,links的source和target用不到nodes里面的id值,就会去用索引的值,放两个图看效果。
  • p.s.本来测试的时候随便找了个冒泡排序排了序测试了下,最后写博客才想用个快排,放个冒泡确实比较不提倡。

最后放一个完结图:单击扩展一级节点,双击已节点扩展一级!

    

参考资料

0.Echarts官方文档,graph

1.Echarts Force力导向图实现节点可折叠

2.快速排序(Quicksort)的Javascript实现,阮一峰

3.Friday Algorithms: Quicksort – Difference Between PHP and JavaScript

4.ECharts3.x中的点击事件与行为

5.数据结构基础 C语言版,第二版,豆瓣

Echarts关系图-力引导布局的更多相关文章

  1. Echarts3 关系图-力导向布局图

    因为项目需要,要求实现类似力导图效果的图,我就瞄上了echarts. 注意事项1:由于我的项目要部署到内网,所以js文件要在本地,网上大多力导图都是echarts2的,而其又依赖zrender基础库, ...

  2. echarts 关系图graph force布局 拖动节点并固定不返回原点

    myChart.on('mouseup',function(params){var option=myChart.getOption();option.series[0].nodes[params.d ...

  3. echart3 力引导布局实现节点的提示和折叠

    最近在项目中需要开发一个图表来显示人员的各种属性,类似于一种树形的结构进行显示数据.如果多个人员有同一个属性,那么需要将相同的属性进行连线,即关联起来.即形成一个关系图,由于我自身对echarts稍微 ...

  4. hadoop下生成echarts关系图

    数据 O700 O2833 O700 O331 O700 O3425 O700 O350 O700 O3516 O700 O3826 读取文件类 public class FileReadFromHd ...

  5. echarts关系图圆心颜色渐变

    let option = { backgroundColor: '#1a4377', animationDurationUpdate: 1500, // 动画更新变化时间 animationEasin ...

  6. Echarts 关系图 添加点击事件

    /*实现的效果是:在关系图上加点击事件,点击某个点,得到改点代表的内容,并且实现一个跳转效果. 关键代码已用红色标出*/ <!DOCTYPE html> <html lang=&qu ...

  7. echarts3关系图:力引导布局, 固定某些节点

    在数组里设置 fixed: true,<a href='http://echarts.baidu.com/option.html#series-graph.data.fixed'>官方文档 ...

  8. ECharts之force力导向布局图——数据源说明及后端API约定

    Echarts ? 关于 Echarts 请移步这里 force 力导向图 实现方式,如: function require_EC () { require( [ 'echarts', //载入for ...

  9. Echarts——关系图(人民的名义为例,简化)源码

    参考博文:https://www.cnblogs.com/emrys5/p/echart-relationship-map.html <!DOCTYPE html> <html> ...

随机推荐

  1. bootstrap中的下拉菜单

    下拉菜单必要的代码: <div  class="container"> <div  class="dropdown"> <butt ...

  2. cms基本概念(dedecms,phpcms)

    1.什么是cms? cms是"Content Management System"的缩写,意为"内容管理系统". 内容管理系统是企业信息化建设和电子政务的新宠, ...

  3. [转] .NET领域驱动设计—看DDD是如何运用设计模式颠覆传统架构

    阅读目录: 1.开篇介绍 2.简单了解缘由(本文的前期事宜) 3.DomainModel扩展性(运用设计模式设计模型变化点) 3.1.模型扩展性 3.2.设计模式的使用(苦心专研的设计模式.设计思想可 ...

  4. [1] Report Fusioncharts

    图形报表之fusioncharts  

  5. javaScript操作DOM对象(看三遍,敲三遍,写三遍! 不会你找我)!!

    DOM是Document Object Model的缩写,即文档对象模型,是基于文档编程的一套API 使用javaScript操作DOM对象通常分为三类:1.DOM CORE        2.HTM ...

  6. Zab: A simple totally ordered broadcast protocol(译)

    摘要 这是一个关于ZooKeeper正在使用的全序广播协议(Zab)的简短概述.它在概念上很容易理解,也很容易实现,并且提供很高的性能.在这篇文章里,我们会呈现ZooKeeper在Zab上的需求,也会 ...

  7. Java内存分配及垃圾回收机制(未完待待续)

    Java内存区域 1.内存区域 jvm运行时数据区域 程序计数器 Java虚拟机栈 本地方法栈 方法区 Java堆 大图 2.概念解释 程序计数器   线程私有的一块很小的内存空间,它是当前线程所执行 ...

  8. Testlink安装步骤Checking if C:\inetpub\wwwroot\testlink-1.9.3\gui\templates_c directory is writable Failed !

    Testlink安装过程中问题现象: Checking if C:\inetpub\wwwroot\testlink-1.9.3\gui\templates_c directory is writab ...

  9. 【Android Developers Training】 58. 缓存位图

    注:本文翻译自Google官方的Android Developers Training文档,译者技术一般,由于喜爱安卓而产生了翻译的念头,纯属个人兴趣爱好. 原文链接:http://developer ...

  10. 使用Scribefire在博客中插入语法高亮

    效果如下, 文字1 int cool void main() { cout<<"hello world!"<<endl } 文字2 经过一番折腾,终于搞定了 ...