echarts3 graph java_记录使用echarts的graph类型绘制流程图全过程(一)-x,y位置的计算...

本文介绍了如何在不修改数据的情况下,利用echarts的graph类型实现从两个节点开始的流程图绘制。通过分析数据,获取节点关系,设置节点的x轴和y轴顺序,实现了动态布局。同时,文章详细解释了节点位置计算、行数位置设定和具体位置信息的设置过程。
摘要由CSDN通过智能技术生成

先说下本次案例业务需求,输入2个节点,获取数据后绘制出2个节点间的路径,之前使用的是网状图,但是网状图的效果不佳,需要转换成流程图的模式:

7c544f30109835e259f83d036de20cc5.png

那么如何在不修改数据的情况下,实现类似效果尼?

看了下echarts的graph类型,可以实现类似的,下面是官方的实例

7efcf24b69981a6e17609691e7560a6f.png

从实例中可以看出,难点在于节点的显示位置x,y和曲线的设置。业务数据中:

1、节点的数量不定,关系的数量不定,

2、后台返回的数据只有单独的节点信息和关系信息

实现思路:

1、分析数据,获取前后节点关系,获得行数位置(节点的xIndex信息)

在节点数组中找到开始节点并设置xIndex 为1,然后从它开始找第二层的节点

// 获取节点的x轴顺序

setNodeOrder () {

this.entityList.forEach(item => {

if (item.id === this.startNode) {

item.xIndex = 1;

}

// 设置一个对象,记录节点信息,用于后面查询信息,比数组查询来的快

this.nodesObj[item.id] = item;

});

this.findNodeOrder(2, this.startNode);

}

// 广度遍历--按顺序找到第index层的数据,并设置对应的参数,再层层递进

findNodeOrder (xIndex, sId){

}

想一下,如果是第二层的节点,那么应该是关系中,源是startNode或者目的节点是startNode,如此类推,层层递进,

这里需要使用广度遍历而不是深度遍历,设定2个数组,currentQueue记录当前层的节点,afterQueue记录下一层的节点,当currentQueue层遍历完后,将afterQueue变成currentQueue,继续遍历,直至结束,核心代码如下:

......

let nextIds = [];

this.relationList.forEach(item => {

// 源

if (item.source === sId) {

if (!this.nodesObj[item.target].xIndex) {

this.nodesObj[item.target].xIndex = xIndex;

nextIds.push(item.target);

}

}

// 目的

if (item.target === sId) {

if (!this.nodesObj[item.source].xIndex) {

this.nodesObj[item.source].xIndex = xIndex;

nextIds.push(item.source);

}

}

});

let nextIdsLen = nextIds.length;

// 1、没有当前的队列,没有后续的队列,有nextIds

if (

!this.currentQueue.length &&

!this.afterQueue.length &&

nextIdsLen

) {

this.currentQueue = nextIds;

this.currentXindex = this.currentXindex + 1;

this.setNextOrder();

} else if (this.currentQueue.length && nextIdsLen) {

// 2、有当前的队列在遍历,排队

this.afterQueue = this.afterQueue.concat(nextIds);

} else if (!this.currentQueue.length && this.afterQueue.length) {

// 3、没有当前的队列了,有排队的队列,则排队的进去

if (nextIdsLen) {

this.afterQueue = this.afterQueue.concat(nextIds);

}

this.currentQueue = JSON.parse(JSON.stringify(this.afterQueue));

this.afterQueue = [];

this.currentXindex = this.currentXindex + 1;

}

setNextOrder函数:

setNextOrder () {

while (this.currentQueue.length) {

let id = this.currentQueue.shift();

this.findNodeOrder(this.currentXindex, id);

}

}

2、根据行数信息设置列数的位置(节点的yIndex层的信息)

先排序第一层和第二层的顺序,因为第一层就一个节点,第二层和第一层的关系都是一样的,所以顺序无所谓,可以直接遇到就叠加

// 排完了x,再排列y

// 先排第一和二行的元素的y,按顺序排

let maxXindex = 1;

let secondYIndexObj = {};

for (let i in this.nodesObj) {

let item = this.nodesObj[i];

if (!item.yIndex) {

maxXindex = item.xIndex > maxXindex ? item.xIndex : maxXindex;

if (item.xIndex === 1) {

item.yIndex = 1;

this.yIndexObj[item.id] = {

xIndex: 1,

yIndex: 1

};

}

if (item.xIndex === 2) {

if (!secondYIndexObj[item.xIndex]) {

item.yIndex = 1;

// 记录当前的y轴上的节点个数

secondYIndexObj[item.xIndex] = 1;

this.yIndexObj[item.id] = {

xIndex: 2,

yIndex: 1

};

} else {

item.yIndex = secondYIndexObj[item.xIndex] + 1;

secondYIndexObj[item.xIndex] = item.yIndex;

this.yIndexObj[item.id] = {

xIndex: 2,

yIndex: item.yIndex

};

}

}

}

}

但是从第三层开始就要注意了,不能再按照第二层的递增顺序的方法来控制。因为第二层与第三层的关系和数量都不一定,如果随机排列会导致线很乱,最好的方法是根据第二层的顺序,获取第三层的顺序。即将第二层第N个节点有关的的节点设置在第N层。这样如果第二层的数量与第三层的数量相等,那么就完全在一条直线上了。如果数量不等,也不至于那么混乱但是如果第三层有多个节点与第二层的同一个节点有关系,这个时候我选择的是随机选择层级了

// 从第三层开始

if (maxXindex > 2) {

// 后面列的排序根据前一列为准(尽量保证在一条直线上)

for (let j = 3; j <= maxXindex; j++) {

for (let i in this.nodesObj) {

let item = this.nodesObj[i];

while (item.xIndex === j && !item.yIndex) {

// 先看跟它一层的节点有多少个

if (!this.nodeOrders[j]) {

let totals = this.findYtotal(j);

this.nodeOrders[j] = [];

for (let i = 1; i <= totals; i++) {

this.nodeOrders[j].push(i);

}

}

// 找第二层中的对应的层级

let findX = j - 1;

// 找到所有的层级

let findYs = this.findLinkNode(item.id, findX);

// 找到还有的层级

let sameArr = findYs.filter(x =>

this.nodeOrders[j].includes(x)

);

let findY;

// 找不到按顺序抽取了

if (!sameArr.length) {

// 只能随机选择一个了

let ran = Math.floor(

Math.random() * this.nodeOrders[j].length

);

findY = this.nodeOrders[j][ran];

this.nodeOrders[j].splice(ran, 1);

} else {

findY = sameArr[0];

// 去除该顺序

let order;

this.nodeOrders[j].find((num, k) => {

if (num === findY) {

order = k;

return true;

}

});

this.nodeOrders[j].splice(order, 1);

}

this.yIndexObj[item.id] = {

xIndex: j,

yIndex: findY

};

item.yIndex = findY;

}

}

}

}

3、设置具体的位置信息

获取图表的中心位置centerY,将起点和终点的y位置放在中间,其他的根据上面获取的xIndex和yIndex的还有根据情况设置的间距(gapX,gapY)及节点的大小(size)计算出每个节点的x和y信息

// 设置节点的位置x,y

setNodesPositon () {

for (let i in this.nodesObj) {

let item = this.nodesObj[i];

if (item.id === this.startNode) {

item.y = this.centerY;

item.x = 1;

}

if (!item.x) {

item.x = this.gapX * (item.xIndex - 1) + this.size / 2;

}

if (!item.y && !item.total) {

item.total = this.findYtotal(item.xIndex);

if (item.total === 1) {

item.y = this.centerY;

} else {

let middleNum = item.total / 2;

let ceilNum = Math.ceil(middleNum);

let topGap;

let bottomGap;

let ty = (ceilNum - item.yIndex) * (this.gapY + this.size);

let by = (item.yIndex - ceilNum) * (this.gapY + this.size);

if (item.total % 2 === 1) {

topGap = this.centerY - ty;

bottomGap = this.centerY + by;

} else {

topGap = this.centerY - (ty + this.gapY + 0.5 * this.size);

bottomGap = this.centerY + (by - 0.5 * this.size);

}

if (item.yIndex <= middleNum) {

item.y = topGap;

} else {

item.y = bottomGap;

}

// 奇数

if (item.total % 2 === 1) {

if (item.yIndex === ceilNum) {

item.y = this.centerY;

}

}

}

}

}

}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值