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

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

图二为有问题的折叠,点击[设计师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 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" style="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.本来测试的时候随便找了个冒泡排序排了序测试了下,最后写博客才想用个快排,放个冒泡确实比较不提倡。

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

    

参考文献

0.Echarts官方文档,graph

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

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

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

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

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

时间: 2024-10-09 20:59:06

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

angular7中echarts关系图实战

Echarts关系图实战效果图 第一步: 下载echarts(4.1.0)和ngx-echarts(4.1.0)的依赖包,在angular.json中引入echarts.js,在公共模块中引入 第二步: html页面:<div style="height: 450px" echarts [options]="option" class="bar-chart" (chartInit)="onChartInit($event)&quo

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

参考博文:https://www.cnblogs.com/emrys5/p/echart-relationship-map.html 1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta name="viewport" content="width=device-width" /> 5 <title>ECharts 实现人民的名义关系图谱</title> 6 &

graph 关系图 设置

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"><html><head> <meta http-equiv="X-UA-Compatible" content="IE=8"> <!-- 使用IE8以上的渲染 -->

Echarts力导向图graph 关系图绘制Demo

近期因业务需求,给我司产品做了一个Echarts----力导向图&关系图 demo 其中分为两种关系图: 01.不同等级节点样式不同 02.不同公司的颜色样式不同 效果图献上: code如下: var dom = document.getElementById("container"); var myChart = echarts.init(dom); var app = {}; option = null; myChart.showLoading(); var webkitD

转:ECharts图表组件之简单关系图:如何轻松实现另类站点地图且扩展节点属性实现点击节点页面跳转

站点地图不外乎就是罗列一个网站的层次结构,提炼地讲就是一个关系结构图.那么我们如何巧用ECharts图表组件内的简单关系结构图来实现一个站点的地图结构呢?另外如何点击某个节点的时候实现页面跳转呢? 针对上述所提出的疑问,我琢磨了一下ECharts图表组件.找到官方这样的一个适合的简单关系图:http://echarts.baidu.com/doc/example/force1.html 通过观察ECharts图表组件的简单关系图的数据结构,得出我要实现一个站点地图需要做哪些工作: 1.引入ech

【 D3.js 高级系列 — 2.0 】 机械图 + 人物关系图

机械图(力路线图)结合老百姓的关系图中的生活,这是更有趣. 本文将以此为证据,所列的如何图插入外部的图像和文字的力学. 在[第 9.2 章]中制作了一个最简单的力学图.其后有非常多朋友有疑问,基本的问题包含: 怎样在小球旁插入文字 怎样将小球换为别的图形 怎样插入图片 怎样限制小球运动的边界 本文将对以上问题依次做出讲解.当中前三点是 SVG 元素的问题.和 D3 无多大关联. 1. SVG 图片 SVG 的图片元素的具体讲解可看[官方文档-图片].通常,我们仅仅须要使用到图片元素的五个属性就够

在MyEclipse中设计一个实体关系图

ER-Designer提供了大量的工具以及功能,来帮助您创建.重组和自定义ER图,同时它还能将ER图以JPEG格式导出.在本教程中,您将学习到: 从数据库中逆向获取的实体关系图(ERD) 在ER图中使用表数据 路由表关系 查找表 导出ER图保存将其为JPEG图像 持续时间:10分钟 没有MyEclipse?立即下载 1. 从数据库中创建实体关系(ER)图 本节我们为大家演示如何从现有的数据库中创建ERD,所给出的示例使用了Oracle 9i的人力资源(HR)示例数据库. (1)选择Window>

echars关系图

<!DOCTYPE html> <html> <head> <meta name="viewport" content="width=device-width" /> <meta charset="utf-8"> <title>echars关系图</title> <script src="https://cdn.bootcss.com/echart

关系图绘制详解

由于项目中需要使用关系图,一般官方网站都是用自己的json文件,而且关系图一般都包含很多的数据,修改起来很不方便,所以从网上找到一个详细关系图详细的博客,所以在此分享一下 代码: <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD