`

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

阅读更多

     最近在项目中需要开发一个图表来显示人员的各种属性,类似于一种树形的结构进行显示数据。如果多个人员有同一个属性,那么需要将相同的属性进行连线,即关联起来。即形成一个关系图,由于我自身对echarts稍微熟悉一下,因此采用echarts3来完成此图表的开发。
注意:echarts的不同版本api有些稍微的不同。

完成效果:

  需求:
    1、点击父节点
        |- 该父节点的子节点是没有显示的,那么显示它的子节点
        |- 该父节点的子节点是显示的,那么隐藏它的子节点和子孙节点
    2、对于父节点显示出它的分类名称,比如 用户信息(父节点)下有用户名、性别、生日、身份证(子节点)等
        |- 父节点显示 用户信息
        |- 子节点显示 用户名 : 名称
                     性别 : 名称
    3、鼠标移动到父节点上,显示出它下方子节点的具体信息

 

预备知识:
    1、节点的隐藏和实现:在 series[index].data[index]中存在category 值,如果它的值和series[index].categories中的角标没有对应起来,那么此节点是不显示的(即隐藏,将category的值改成负数,显示 改成整数,值要和categories的角标对应起来)
    2、2个节点要连接起来,那么 links 中的 source,target 的值只需要和 data 中的 name 属性的值对应起来即可
    3、需要了解一下echarts的富文本样式,用于格式化节点上显示的值
    4、了解一点es5,es6的语法

 

图片解释:(下方的 lengedName 实际是data中的 legendName ,图片上写错了

具体实现:
    1、点击显示和隐藏节点



 
        |- 找到点击节点的 open 的值(第一次点击是不存在的,点击完增加这个属性)
            > true(存在,即点过一回)
                * 从links中找到所有的子节点和子孙节点的 name(links中的target属性)的值,需要递归获取
                * 从data中获取获取关联的节点
                * 将节点的category 的值改成 负数
                * 如果节点的nodeType === 1(即上面图片解释中的父节点), 那么需要将 open的值设置成false
                * 将当前点击的节点的 open 属性改成 false
                * 重新渲染echarts图表

                * 此时图表的节点就折叠起来了
            > false(即不存在或后续赋值为false)
                * 从links中找到所有的子节点的 name(links中的target属性)的值
                * 从data中获取获取关联的节点
                * 将节点的category 的值改成 整数
                * 将当前点击的节点的 open 属性改成 true
                * 重新渲染echarts图表

                * 此时图表的节点就展开了

/**
     * 绑定图表的点击事件
     * @param chart
     */
    function bindChartClickEvent(chart) {
        chart.on('click', function (params) {
            var category = params.data.category,
                nodeType = params.data.nodeType;
            if (category === 0 || nodeType === 1) {
                toggleShowNodes(chart, params);
            }
        });
    }

    /**
     * 展开或关闭节点
     * @param chart
     * @param params
     */
    function toggleShowNodes(chart, params) {
        var open = !!params.data.open,
            options = chart.getOption(),
            seriesIndex = params.seriesIndex,
            srcLinkName = params.name,
            serieLinks = options.series[seriesIndex].links,
            serieData = options.series[seriesIndex].data,
            serieDataMap = new Map(),
            serieLinkArr = [];
        // 当前根节点是展开的,那么就需要关闭所有的根节点
        if (open) {
            // 递归找到所有的link节点的target的值
            findLinks(serieLinkArr, srcLinkName, serieLinks, true);
            if (serieLinkArr.length) {
                serieData.forEach(sd => serieDataMap.set(sd.name, sd));
                for (var i = 0; i < serieLinkArr.length; i++) {
                    if (serieDataMap.has(serieLinkArr[i])) {
                        var currentData = serieDataMap.get(serieLinkArr[i]);
                        currentData.category = -Math.abs(currentData.category);
                        if (currentData.nodeType === 1) {
                            currentData.open = false;
                        }
                    }
                }
                serieDataMap.get(srcLinkName).open = false;
                chart.setOption(options);
            }
        } else {
            // 当前根节点是关闭的,那么就需要展开第一层根节点
            findLinks(serieLinkArr, srcLinkName, serieLinks, false);
            if (serieLinkArr.length) {
                serieData.forEach(sd => serieDataMap.set(sd.name, sd));
                for (var j = 0; j < serieLinkArr.length; j++) {
                    if (serieDataMap.has(serieLinkArr[j])) {
                        var currentData = serieDataMap.get(serieLinkArr[j]);
                        currentData.category = Math.abs(currentData.category);
                    }
                }
                serieDataMap.get(srcLinkName).open = true;
                chart.setOption(options);
            }
        }
    }

    /**
     * 查找连接关系
     * @param links 返回的节点放入此集合
     * @param srcLinkName 源线的名称
     * @param serieLinks 需要查找的集合
     * @param deep 是否需要递归进行查找
     */
    function findLinks(links, srcLinkName, serieLinks, deep) {
        var targetLinks = [];
        serieLinks.filter(link => link.source === srcLinkName).forEach(link => {
            targetLinks.push(link.target);
            links.push(link.target)
        });
        if (deep) {
            for (var i = 0; i < targetLinks.length; i++) {
                findLinks(links, targetLinks[i], serieLinks, deep);
            }
        }
    }

 2、节点名称显示的格式化



        富文本样式的使用 (series中label的设置

"label": {
                    "normal": {
                        "show": true,
                        "position": "top",
                        "formatter": function (args) {
                            if (args.data.nodeType === 1) {
                                return "{prefixClassName|" + args.data.legendName + "}";
                            } else {
                                return "{prefixClassName|" + args.data.legendName + " :}\r\n    " + args.name;
                            }
                        },
                        "rich": {
                            "prefixClassName": {
                                color: "#FF9301",
                                fontWeight: "bold"
                            }
                        }
                    }
                }

 3、鼠标移动到父节点上显示子节点的信息

 
        找到当前节点关联的所有的子节点,通过links来进行查找,当前节点的name属性的值等于links中source中的值,那么target就是关联的子节点的name的值,遍历data数据,如果name属性的值等于target的值,就找到了关联节点的数据。
        注意: 在显示的时候需要注意一下获取前面颜色的获取(当categories中的值过多时需要注意一下)

tooltip: {
            "formatter": function (arg) {
                var nodeType = arg.data.nodeType,
                    srcName = arg.name,
                    seriesIndex = arg.seriesIndex,
                    options = echart.getOption(),
                    serieData = options.series[seriesIndex].data,
                    serieLinks = options.series[seriesIndex].links,
                    colors = options.color,
                    serieDataMap = new Map(),
                    serieLinkArr = [],
                    tips = '';
                // 父节点,排除根节点
                if (nodeType === 1) {
                    serieLinks.filter(link => link.source === srcName).forEach(link => serieLinkArr.push(link.target));
                    if (serieLinkArr.length) {
                        serieData.forEach(sd => serieDataMap.set(sd.name, sd));
                        for (var i = 0; i < serieLinkArr.length; i++) {
                            if (serieDataMap.has(serieLinkArr[i])) {
                                var currentData = serieDataMap.get(serieLinkArr[i]),
                                    color = getColor(colors, currentData.category);
                                tips += '<span style="background-color: ' + color + ';width: 10px;height: 10px;border-radius: 50%;display: inline-block"></span> ' + currentData.legendName + " : " + currentData.name + ' <br />';
                            }
                        }
                    }
                    return tips;
                } else {
                    return '';
                }
            }
        }

  /**
     * 获取颜色
     * @param colors
     * @param index
     * @returns {*}
     */
    function getColor(colors, index) {
        var length = colors.length,
            colorIndex = index;
        if (index >= length) {
            colorIndex = length - index;
        }
        return colors[colorIndex];
    }

       

完成代码如下:(如需运行,下载附件中的即可,或自定导入echarts3的js文件)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>echart3 力引导布局实现节点的提示和折叠</title>
    <script src="echarts.common.min.js"></script>
</head>
<body>
<div id="chart" style="width: 100%;height: 600px"></div>
<script type="text/javascript">
    var echart = echarts.init(document.getElementById('chart'));
    var options = {
        tooltip: {
            "formatter": function (arg) {
                var nodeType = arg.data.nodeType,
                    srcName = arg.name,
                    seriesIndex = arg.seriesIndex,
                    options = echart.getOption(),
                    serieData = options.series[seriesIndex].data,
                    serieLinks = options.series[seriesIndex].links,
                    colors = options.color,
                    serieDataMap = new Map(),
                    serieLinkArr = [],
                    tips = '';
                // 父节点,排除根节点
                if (nodeType === 1) {
                    serieLinks.filter(link => link.source === srcName).forEach(link => serieLinkArr.push(link.target));
                    if (serieLinkArr.length) {
                        serieData.forEach(sd => serieDataMap.set(sd.name, sd));
                        for (var i = 0; i < serieLinkArr.length; i++) {
                            if (serieDataMap.has(serieLinkArr[i])) {
                                var currentData = serieDataMap.get(serieLinkArr[i]),
                                    color = getColor(colors, currentData.category);
                                tips += '<span style="background-color: ' + color + ';width: 10px;height: 10px;border-radius: 50%;display: inline-block"></span> ' + currentData.legendName + " : " + currentData.name + ' <br />';
                            }
                        }
                    }
                    return tips;
                } else {
                    return '';
                }
            }
        },
        "series": [
            {
                "itemStyle": {
                    "normal": {
                        "label": {
                            "show": true
                        },
                        "borderType": "solid",
                        "borderColor": "rgba(182,215,0,0.5)",
                        "borderWidth": 2,
                        "opacity": 1
                    },
                    "emphasis": {
                        "borderWidth": 5,
                        "borderType": "solid",
                        "borderColor": "#40f492"
                    }
                },
                "lineStyle": {
                    "normal": {
                        "color": "rgba(182,0,255,0.5)",
                        "width": "3",
                        "type": "dotted",
                        "curveness": 0.1,
                        "opacity": 1
                    }
                },
                "label": {
                    "normal": {
                        "show": true,
                        "position": "top",
                        "formatter": function (args) {
                            if (args.data.nodeType === 1) {
                                return "{prefixClassName|" + args.data.legendName + "}";
                            } else {
                                return "{prefixClassName|" + args.data.legendName + " :}\r\n    " + args.name;
                            }
                        },
                        "rich": {
                            "prefixClassName": {
                                color: "#FF9301",
                                fontWeight: "bold"
                            }
                        }
                    }
                },
                "layout": "force",
                "roam": true,
                "edgeSymbolSize": [
                    8,
                    10
                ],
                "edgeSymbol": [
                    "circle",
                    "arrow"
                ],
                "focusNodeAdjacency": false,
                "force": {
                    "repulsion": 300,
                    "edgeLength": 50
                },
                "links": [
                    {
                        "source": "3****************3",
                        "target": "3****************3-bank-card"
                    },
                    {
                        "source": "3****************3-bank-card",
                        "target": "工行卡:4077"
                    },
                    {
                        "source": "3****************3-bank-card",
                        "target": "建行卡:4078"
                    },
                    {
                        "source": "3****************3",
                        "target": "3****************3-basic-info"
                    },
                    {
                        "source": "3****************3-basic-info",
                        "target": "张三"
                    },
                    {
                        "source": "3****************3",
                        "target": "3****************3-contact"
                    },
                    {
                        "source": "3****************3-contact",
                        "target": "145157****@qq.com"
                    },
                    {
                        "source": "3****************3-contact",
                        "target": "14515783**"
                    }
                ],
                "categories": [
                    {
                        "name": "用户"
                    },
                    {
                        "name": "身份证"
                    },
                    {
                        "name": "姓名"
                    },
                    {
                        "name": "性别"
                    },
                    {
                        "name": "生日"
                    },
                    {
                        "name": "手机"
                    },
                    {
                        "name": "固定电话"
                    },
                    {
                        "name": "邮箱"
                    },
                    {
                        "name": "qq"
                    },
                    {
                        "name": "地址"
                    },
                    {
                        "name": "银行卡"
                    },
                    {
                        "name": "基本信息"
                    },
                    {
                        "name": "地址分类"
                    },
                    {
                        "name": "联系方式"
                    },
                    {
                        "name": "银行卡分类"
                    }
                ],
                "name": "人员关系图",
                "type": "graph",
                "showSymbol": true,
                "yAxisIndex": 0,
                "z": 2,
                "data": [
                    {
                        "name": "3****************3",
                        "symbolSize": 40,
                        "value": "3****************3",
                        "category": 0,
                        "draggable": true,
                        "label": {
                            "normal": {
                                "show": true,
                                "position": "inside"
                            }
                        },
                        "legendName": "用户",
                        "nodeType": 0,
                        "idCardNum": "3****************3"
                    },
                    {
                        "name": "3****************3-bank-card",
                        "symbolSize": 40,
                        "value": "银行卡分类",
                        "category": -14,
                        "draggable": true,
                        "label": {
                            "normal": {
                                "show": true,
                                "position": "inside"
                            }
                        },
                        "legendName": "银行卡分类",
                        "nodeType": 1
                    },
                    {
                        "name": "工行卡:4077",
                        "symbolSize": 20,
                        "value": "工行卡:4077",
                        "category": -10,
                        "draggable": true,
                        "legendName": "银行卡",
                        "nodeType": 0
                    },
                    {
                        "name": "建行卡:4078",
                        "symbolSize": 20,
                        "value": "建行卡:4078",
                        "category": -10,
                        "draggable": true,
                        "legendName": "银行卡",
                        "nodeType": 0
                    },
                    {
                        "name": "3****************3-basic-info",
                        "symbolSize": 40,
                        "value": "基本信息",
                        "category": -11,
                        "draggable": true,
                        "label": {
                            "normal": {
                                "show": true,
                                "position": "inside"
                            }
                        },
                        "legendName": "基本信息",
                        "nodeType": 1
                    },
                    {
                        "name": "张三",
                        "symbolSize": 20,
                        "value": "张三",
                        "category": -2,
                        "draggable": true,
                        "legendName": "姓名",
                        "nodeType": 0
                    },
                    {
                        "name": "3****************3-contact",
                        "symbolSize": 40,
                        "value": "联系方式",
                        "category": -13,
                        "draggable": true,
                        "label": {
                            "normal": {
                                "show": true,
                                "position": "inside"
                            }
                        },
                        "legendName": "联系方式",
                        "nodeType": 1
                    },
                    {
                        "name": "145157****@qq.com",
                        "symbolSize": 20,
                        "value": "145157****@qq.com",
                        "category": -7,
                        "draggable": true,
                        "legendName": "邮箱",
                        "nodeType": 0
                    },
                    {
                        "name": "14515783**",
                        "symbolSize": 20,
                        "value": "14515783**",
                        "category": -8,
                        "draggable": true,
                        "legendName": "qq",
                        "nodeType": 0
                    }
                ]
            }
        ],
        "legend": {
            "data": [
                "用户",
                "身份证",
                "姓名",
                "性别",
                "生日",
                "手机",
                "固定电话",
                "邮箱",
                "qq",
                "地址",
                "银行卡",
                "基本信息",
                "地址分类",
                "联系方式",
                "银行卡分类"
            ]
        },
        "title": [
            {
                "left": "left",
                "text": "人员关系图"
            }
        ]
    };
    echart.setOption(options);
    bindChartClickEvent(echart);

    /**
     * 获取颜色
     * @param colors
     * @param index
     * @returns {*}
     */
    function getColor(colors, index) {
        var length = colors.length,
            colorIndex = index;
        if (index >= length) {
            colorIndex = length - index;
        }
        return colors[colorIndex];
    }

    /**
     * 绑定图表的点击事件
     * @param chart
     */
    function bindChartClickEvent(chart) {
        chart.on('click', function (params) {
            var category = params.data.category,
                nodeType = params.data.nodeType;
            if (category === 0 || nodeType === 1) {
                toggleShowNodes(chart, params);
            }
        });
    }

    /**
     * 展开或关闭节点
     * @param chart
     * @param params
     */
    function toggleShowNodes(chart, params) {
        var open = !!params.data.open,
            options = chart.getOption(),
            seriesIndex = params.seriesIndex,
            srcLinkName = params.name,
            serieLinks = options.series[seriesIndex].links,
            serieData = options.series[seriesIndex].data,
            serieDataMap = new Map(),
            serieLinkArr = [];
        // 当前根节点是展开的,那么就需要关闭所有的根节点
        if (open) {
            // 递归找到所有的link节点的target的值
            findLinks(serieLinkArr, srcLinkName, serieLinks, true);
            if (serieLinkArr.length) {
                serieData.forEach(sd => serieDataMap.set(sd.name, sd));
                for (var i = 0; i < serieLinkArr.length; i++) {
                    if (serieDataMap.has(serieLinkArr[i])) {
                        var currentData = serieDataMap.get(serieLinkArr[i]);
                        currentData.category = -Math.abs(currentData.category);
                        if (currentData.nodeType === 1) {
                            currentData.open = false;
                        }
                    }
                }
                serieDataMap.get(srcLinkName).open = false;
                chart.setOption(options);
            }
        } else {
            // 当前根节点是关闭的,那么就需要展开第一层根节点
            findLinks(serieLinkArr, srcLinkName, serieLinks, false);
            if (serieLinkArr.length) {
                serieData.forEach(sd => serieDataMap.set(sd.name, sd));
                for (var j = 0; j < serieLinkArr.length; j++) {
                    if (serieDataMap.has(serieLinkArr[j])) {
                        var currentData = serieDataMap.get(serieLinkArr[j]);
                        currentData.category = Math.abs(currentData.category);
                    }
                }
                serieDataMap.get(srcLinkName).open = true;
                chart.setOption(options);
            }
        }
    }

    /**
     * 查找连接关系
     * @param links 返回的节点放入此集合
     * @param srcLinkName 源线的名称
     * @param serieLinks 需要查找的集合
     * @param deep 是否需要递归进行查找
     */
    function findLinks(links, srcLinkName, serieLinks, deep) {
        var targetLinks = [];
        serieLinks.filter(link => link.source === srcLinkName).forEach(link => {
            targetLinks.push(link.target);
            links.push(link.target)
        });
        if (deep) {
            for (var i = 0; i < targetLinks.length; i++) {
                findLinks(links, targetLinks[i], serieLinks, deep);
            }
        }
    }
</script>
</body>
</html>
  • 大小: 1.1 MB
  • 大小: 87.7 KB
  • 大小: 703.4 KB
  • 大小: 32.2 KB
  • 大小: 40.6 KB
0
0
分享到:
评论

相关推荐

    echarts关系图(力引导)拖动节点不还原位置

    echarts关系图(力引导)拖动节点不还原位置,在init创建时第三个参数对象添加myOpts_:{draggableFixed_:true}即可,如: var mychart = echarts.init(dom,null,{myOpts_:{draggableFixed_:true}});创建一个拖动不还原的...

    echart拓扑图单节点拖拽.zip

    2. `layout`: 指定节点的布局方式,可以是`'force'`(力引导布局)或其他布局算法。 3. `Roam`: 设置是否开启缩放和平移,`roam: true`允许用户自由拖动和缩放图表。 4. `topology.draggable`: 设置节点是否可拖动,...

    echart拓扑图单节点拖拽互不影响demo

    **ECharts 拓扑图拖拽互不影响详解** ECharts 是一个由百度开发的开源JavaScript图表库,它提供了一套...通过学习和理解这个示例,开发者可以进一步扩展功能,比如添加节点连接线的拖拽,或者实现更复杂的布局算法。

    echarts tree 点击节点收缩(关闭子节点)功能实现

    ECharts Tree提供了`symbol`和`symbolSize`属性来控制节点的显示样式,我们可以利用这些属性来实现节点的收缩效果。例如,当节点收缩时,我们可以将`symbol`设置为"none",隐藏节点;当节点展开时,可以将其设置为...

    echart 3D环形图 动画效果

    在这个“echart 3D环形图 动画效果”的主题中,我们将探讨如何使用ECharts来创建具有3D视觉冲击力和动态效果的环形图。 3D环形图是一种特殊的饼图变体,通过三维视角展示数据的比例关系,使数据更具有立体感和层次...

    eCharts Tree双向布局

    3. **节点样式与连接线**:双向布局中,节点的位置和连接线的方向需要特别考虑。可以使用`symbolSize`设置节点大小,`lineStyle`调整连接线的样式。对于连接线,可能需要自定义`edgeShape`,例如使用`arc`或`...

    力导向图force-direct算法(MATLAB实现)含数据集

    力导向图(Force-directed Graph Drawing)是一种用于图形可视化的算法,它通过模拟物理系统中粒子间的相互作用力来布局图的节点和边,使得图形在视觉上清晰且美观。在这个项目中,MATLAB被用来实现力导向图算法,...

    d3.layout.force()---入门篇之节点替换为图片

    通过将节点替换为图片,我们可以创建更具吸引力和表现力的可视化,帮助用户更好地理解和探索复杂的数据网络。在实践中,不断调整和优化布局参数,以及添加交互性,可以使你的可视化作品更加引人入胜。

    echart_关系图

    3. **布局算法**:ECharts提供了自动布局功能,如力导向布局,它模拟物理系统的引力和斥力来自动排列节点,使得图形更加美观且易于理解。也可以自定义布局算法,适应特殊的需求。 4. **交互功能**:关系图支持多种...

    Echarts 实现树状图的展示与编辑示例

    当鼠标移动到节点上时,会有节点描叙信息提示框。 当在节点处右键时,会弹出右键编辑菜单。 当鼠标单击节点以外区域时,会隐藏右键编辑菜单。 节点动作包含: 增加子节点,删除子节点,删除当前节点,编辑节点 非常...

    基于echart.js实现的人员架构图,企业内部组织结构图展示图.rar

    本项目“基于echart.js实现的人员架构图,企业内部组织结构图展示图”就是利用ECharts这一强大的工具来呈现企业的组织结构。 ECharts提供了多种图表类型,包括柱状图、折线图、饼图、散点图等,同时也支持自定义...

    QTableView 切换折叠展开图标

    6. **布局调整**:为了正确地显示折叠后的子项,可能还需要重写`QAbstractItemView`的`updateGeometries()`和`setFirstVisibleRow()`方法,以便在折叠节点时调整可见行的布局。 在实际项目中,你可能会遇到一些额外...

    echart树图:横向组织结构 曲线.zip

    这个配置项允许我们定义数据、布局方式、节点样式、以及连接线的样式等。 要实现横向组织结构图,我们需要设置`layout`属性为`'horizontal'`。这将使树图的节点沿着水平方向排列。同时,为了使连接线呈现曲线,我们...

    GridView父子节点异步折叠

    实现GridView父子节点异步折叠,通常需要以下几个关键步骤: 1. **数据模型**:首先,你需要有一个支持层级关系的数据模型,比如自引用的实体类或者包含ParentId字段的对象集合。 2. **数据绑定**:在后台代码中,...

    echarts力导向图节点折叠-附件资源

    echarts力导向图节点折叠-附件资源

    ECharts--基于力导向布局图功能更完善的人物关系图插件扩展-增加横纵滚动条

    力导向布局是一种常见的网络图绘制方法,它模拟了物理系统的力场,其中节点间的引力和斥力相互作用,使得整个图形达到稳定状态。在ECharts中,力导向布局常用于展现人物关系图,帮助用户理解复杂的关联结构。节点与...

    原生JS实现径向树布局算法

    3. **计算位置**:根据节点在当前层的位置和前一层节点的半径,计算出每个节点的x和y坐标。半径可以通过一个递增函数(如指数增长)来确保远离根节点的节点在视觉上不会太密集。 4. **连接节点**:通过线条连接父...

    折叠树的展开和折叠折叠

    在数据结构中,折叠可能通过维护一个表示当前可视节点的栈或队列来实现。 折叠和展开操作对于树遍历也至关重要。在深度优先搜索(DFS)和广度优先搜索(BFS)等算法中,它们可以帮助控制搜索的范围。例如,在DFS中...

    原生JS实现树状图布局算法.zip

    树状图布局算法是构建树状图的关键,它的目标是在二维平面上合理地安排节点的位置,使各个节点之间的连接线尽可能短,同时保持整体的美观性和可读性。原生JavaScript实现的这种算法可以避免对特定库的依赖,降低引入...

Global site tag (gtag.js) - Google Analytics