• Echarts5.* 关系图谱(relation graph)添加节点拖拽、点击节点高亮效果


    前言

    图表的需求为:

    • 展现不同节点之间的联系,比如,公司为节点A,张三为节点B,它们之间的联线为雇佣,这些信息皆需展示出来
    • 点击节点A,只会高亮与它相关的节点
    • 单个节点可拖拽

    echartsg6权衡了一段时间,最终选定echarts,原因无非以下两种

    • echarts文档齐全,上手简单
    • echarts使用人更多一些,遇到问题可在网上找到对应方案

    绘制节点,显示信息

    部分节点与连线的原始数据信息如下所示:

    var nodes = [
    	{
    		id: 0,
    		labels: ["Building"],
    		properties: {
    			name: "辉隆大厦",
    			building_id: "4a9387792719s"
    		}
    	}
    	...
    ]
    
    var links = [
    	{
    		id: 0,
    		source: 0,
    		target: 10,
    		type: "HIRE",
    		properties: {
    			relation: "雇佣"
    		}
    	}
    ]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    注意点:在echarts的关系图里,id必须为字符串,否则节点不会渲染。

    随机生成-600~800之间的坐标点,代码如下所示:

    function generateRandomNum() {
        const startNumber = -600
        const endNumber = 800
        var choice = endNumber - startNumber + 10;
        return Math.floor(Math.random() * choice + startNumber)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    处理原始数据,显示节点信息,及连线上的文字信息,代码如下所示:

    nodes = nodes.map(a => {
    	const {labels, id, properties} = a
    	const name = labels[0]
    	const { category, symbolSize, value } = this.initSingleNodeParam(name, properties)
        const showName = properties.name === 'None' ? properties.label : (properties.name || properties.component_name || properties.label)
    	return {
    		id: String(id),
            name: showName,
            symbolSize,
            x: generateRandomNum(),
            y: generateRandomNum(),
            label: {
            	show: true
            },
            type: name,
            value,
            category,
            properties
    	}
    })
    
    links = links.map(a => {
        return {
            source: String(a.source),
            target: String(a.target),
            label: {
                normal: {
                    show: true,
                    formatter: a.properties.relation
                }
            }
        }
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    initSingleNodeParam这个函数根据不同的节点类型,返回不同的categorysymbolSizevalue信息,部分代码如下所示:

    initSingleNodeParam(name, properties) {
        let category = 0
        let symbolSize = 40
        let value = `类别:楼栋`
        if (name === 'Room') {
            category = 1
            symbolSize = 32
            value = `类别:${properties.label}`
        } else if (name === 'Company') {
            category = 2
            symbolSize = 32
            value = `类别:${properties.label}`
        } else if (name === 'Person') {
            ...
        }
        return {
            category,
            symbolSize,
            value
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    将参数配置到option内,初始化图表

    const categories = [
        {
            name: 'A'
        }, {
            name: 'B'
        }, {
            name: 'C'
        }, {
            name: 'D'
        }, {
            name: 'E'
        }, {
            name: 'F'
        }
    ]
    
    
    var option = {
        tooltip: {},
        animationDuration: 1500,
        animationEasingUpdate: 'quinticInOut',
        hoverAnimation:false,
        series: [
            {
                // name: '孪生',
                type: 'graph',
                layout: 'none',
                circular:{rotateLabel:true},
                animation: false,
                data: nodes,
                links: links,
                categories: categories,
                roam: true,
                draggable: false,
                label: {
                    position: 'right',
                    formatter: '{b}'
                },
                lineStyle: {
                    color: 'source',
                    curveness: 0.3
                },
                emphasis: {
                    focus: 'adjacency',
                    lineStyle: {
                        width: 5,
                        color: "#ffff00"
                    }
                },
                draggable: true,
                hoverAnimation:false
            }
        ]
    };
    myChart.setOption(option);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55

    节点可拖拽

    echarts官网推荐的办法是,在每个节点上创建一个circle,拖动circle时,实时更新节点的坐标位置即可,切记:下面的方法需在myChart.setOption(option)之后执行

    myChart.setOption({
        graphic: echarts.util.map(option.series[0].data, function (item, dataIndex) {
            //使用图形元素组件在节点上划出一个隐形的图形覆盖住节点
            var tmpPos=myChart.convertToPixel({'seriesIndex': 0},[item.x,item.y]);
            return {
                type: 'circle',
                id:String(item.id),
                position: tmpPos,
                shape: {
                    cx: 0,
                    cy: 0,
                    r: 20
                },
                // silent:true,
                invisible: true,
                draggable: true,
                properties: item.properties,
                nodeType: item.type,
                dataIndex,
                ondrag: echarts.util.curry(onPointDragging, dataIndex),
                z: 100              //使图层在最高层
            };
        })
    });
    window.addEventListener('resize', updatePosition);
    myChart.on('dataZoom', updatePosition);
    myChart.on('graphRoam', updatePosition);
    
    function updatePosition() {    //更新节点定位的函数
        myChart.setOption({
            graphic: echarts.util.map(option.series[0].data, function (item, dataIndex) {
                var tmpPos=myChart.convertToPixel({'seriesIndex': 0},[item.x,item.y]);
                return {
                    position: tmpPos
                };
            })
        });
    
    }
    function onPointDragging(dataIndex) {      //节点上图层拖拽执行的函数
        var tmpPos=myChart.convertFromPixel({'seriesIndex': 0},this.position);
        option.series[0].data[dataIndex].x = tmpPos[0];
        option.series[0].data[dataIndex].y = tmpPos[1];
        myChart.setOption(option);
        updatePosition();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46

    添加完上面的方法后,发现一个问题,只能在界面初始化拖动节点,一旦鼠标释放,便再也不能拖动任意节点了。最开始,将echarts版本降至4.0.0,发现可任意拖动节点(但不能高亮节点),后面,将echarts版本固定在5.0.0,便无该问题了,需注意的是,安装echarts时,需指定5.0.0版本

    npm install echarts@5.0.0
    
    • 1

    节点可点击高亮

    由于节点本身具备鼠标移入高亮事件,需在节点点击高亮后,特意屏蔽节点mouseover功能,代码如下所示:

    myChart.on("click", params => {
        const { properties, dataIndex, nodeType } = params.event.target
        this.prevIndex = dataIndex
        this.highlight(dataIndex, myChart)
        const entityProperties = {
            ...properties,
            type: nodeType
        }
        this.$store.commit("jobInstance/SET_HIGHLIGHTENTITY", entityProperties);
    })
    myChart.on("mouseout", _ => {
        if (this.prevIndex === null) return
        this.highlight(this.prevIndex, myChart)
    })
    myChart.on("mouseover", _ => {
        myChart.dispatchAction({
            type: 'downplay'
        });
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    highlight函数代码如下所示:

    highlight(index, myChart) {
        myChart.dispatchAction({
            type: "highlight",
            dataIndex: index
        })
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    有任何问题,欢迎大家留言讨论

  • 相关阅读:
    js 创建DOM,并添加父DOM上,移除某个DOM的所有子节点
    Re 爬取数据
    向npm发包
    基于ASP.NET Core 5.0使用RabbitMQ消息队列实现事件总线(EventBus)
    nginx学习(1)
    94. Promise 的基本用法?
    Crossplane-云基础架构管理平台
    vue深拷贝的几种实现方式
    韩顺平--多人在线通信系统
    flutter系列之:在flutter中使用导航Navigator
  • 原文地址:https://blog.csdn.net/zwf193071/article/details/125426558