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

这是我参与「掘金日新计划 · 6 月更文挑战」的第5天,点击查看活动详情

前言

图表的需求为:

  • 展现不同节点之间的联系,比如,公司为节点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: "雇佣"
		}
	}
]

注意点:在echarts的关系图里,id必须为字符串,否则节点不会渲染。此外,nodes里面每一个节点,必须有xy,因为在后面的option配置里,我们不会选取任何的layout布局,故节点坐标信息是必须的

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

function generateRandomNum() {
    const startNumber = -600
    const endNumber = 800
    var choice = endNumber - startNumber + 10;
    return Math.floor(Math.random() * choice + startNumber)
}

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

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

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

将参数配置到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);

节点可拖拽

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

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

npm install [email protected]

节点可点击高亮

由于节点本身具备鼠标移入高亮事件,需在节点点击高亮后,特意屏蔽节点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'
    });
})

highlight函数代码如下所示:

highlight(index, myChart) {
    myChart.dispatchAction({
        type: "highlight",
        dataIndex: index
    })
}

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

猜你喜欢

转载自juejin.im/post/7112328010560208910