记录使用echarts的graph类型绘制流程图全过程(一)-x,y位置的计算
先说下本次案例业务需求,输入2个节点,获取数据后绘制出2个节点间的路径,之前使用的是网状图,但是网状图的效果不佳,需要转换成流程图的模式:

那么如何在不修改数据的情况下,实现类似效果尼?
看了下echarts的graph类型,可以实现类似的,下面是官方的实例

从实例中可以看出,难点在于节点的显示位置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;
}
}
}
}
}
}
记录使用echarts的graph类型绘制流程图全过程(一)-x,y位置的计算的更多相关文章
- 记录使用echarts的graph类型绘制流程图全过程(二)- 多层关系和圆形图片的设置
本文主要记录在使用echarts的graph类型绘制流程图时候遇到的2个问题:对于圆形图片的剪切和多层关系的设置 图片的设置 如果用echarts默认的symbol参数来显示图片,会显示图片的原始状态 ...
- Echarts中graph类型的运用求教
以下是百度Echarts官网上关系图的源码,但是这个关系图的node节点和edge都是静态文件里规定好的,我现在想动态实现,点击其中一个节点A然后新产生一个新节点B,并且有A和B之间的edge,就类似 ...
- 玩转控件:GDI+动态绘制流程图
前言 今天,要跟大家一起分享是"GDI+动态生成流程图"的功能.别看名字高大上(也就那样儿--!),其实就是动态生成控件,然后GDI+绘制直线连接控件罢了.实际项目效果图如下 ...
- 【Canvas】绘制几何级数Geometric series曲线 y=1+1/2+1/4+1/8+1/16+1/32+1/64+....
相关资料:https://baike.baidu.com/item/%E5%87%A0%E4%BD%95%E7%BA%A7%E6%95%B0/112584?fr=aladdin 图线: 代码: < ...
- graph easy绘制ascii简易流程图
graph-easy 日常我们经常需要画一些简易流程图,但是如果使用visio等工具来作图,一则略显大材小用,二则图片导出后再要粘贴.相比下,如果可以简单的用一些text的图来表达,则会简单的多.比如 ...
- Graphviz 绘制流程图
凝视说明非常具体.不再详述. digraph G{ //dot 是一种画图语言,它能够方便你採用图形的方式高速.直观地表达一些想法, //比方描写叙述某个问题的解决方式,构思一个程序的流程,澄清一堆貌 ...
- echarts拓扑图(graph,力导向布局图)
echarts连接:https://gallery.echartsjs.com/editor.html?c=xCLEj67T3H 讲解:https://www.cnblogs.com/koala201 ...
- 在echarts里在geojson绘制的地图上展示散点图(气泡)、线集。
先来要实现的效果图: 下方图1是官网的案例:http://www.echartsjs.com/gallery/editor.html?c=scatter-map 下图2是展示气泡类型为pin的效果: ...
- 使用SVG绘制流程图
本篇主要记录流程图的实现过程中的难点和核心技术点,先上效果图: 节点可以任意拖拽,曲线跟随变化 正在连接的线 1.节点实现 流程图是基于SVG绘制的,节点主要利用 g 和 foreignObject的 ...
随机推荐
- ThreadPoolExecutor执行任务,异常日志缺失问题
之前在使用自定义线程池异步执行耗时任务时,一直记着如果业务方法抛出异常没有捕获,那么是看不到日志框架输出的异常日志的,所以总是在业务方法中包裹一层try-catch捕获可能发生的异常.也未去深入为什么 ...
- Django+Nginx概念安装和使用–使用Django建立你的第一个网站
一 前记 最近在使用Django倒腾属于自己的网站,由于以前没有接触过多少这类信息,所以,很多东西都是从零开始学习的.在参考网上的资料时候,发现很多对这方面记录的,很多人都写的不是很清楚,也许我这个新 ...
- 封装axios来管控api的2种方式
前言:我们在开发项目的时候,往往要处理大量的接口.并且在测试环境 开发环境 生产环境使用的接口baseurl都不一样 这时候如果在开发环境完成之后切换每一个接口的baseurl会变的非常的麻烦,(要去 ...
- SpringCloud学习笔记(2):使用Ribbon负载均衡
简介 Spring Cloud Ribbon是基于Netflix Ribbon实现的一套客户端负载均衡工具,在注册中心对Ribbon客户端进行注册后,Ribbon可以基于某种负载均衡算法,如轮询(默认 ...
- JAVA之学生信息管理系统
StudentManager系统 系统的数据: 变量 stunumber 为字符串类型 String,用于存储学生的学号(有 8 位数字组成) 变量 name 为字符串类型 String,用于存储学生 ...
- 学习笔记-Unity3d代码实现Windows10加载圈圈的效果
最近在写一个Unity3d的模仿windows10的桌面的程序,由于Unity3d本身不支持Gif图片,所以突发奇想使用代码来实现接近的. 接下来是代码部分:不一一解析,很简单,看的懂原理就Okly了 ...
- NUMA导致的MySQL服务器SWAP问题分析
[作者] 王栋:携程技术保障中心数据库专家,对数据库疑难问题的排查和数据库自动化智能化运维工具的开发有强烈的兴趣. [问题描述] 我们知道当mysqld进程使用到SWAP时,就会严重影响到MySQL的 ...
- kubernetes部署jenkins(Docker in Docker)及认证
引言 Jenkins是一款开源 CI&CD 软件,用于自动化各种任务,包括构建.测试和部署软件. 本文将Jenkins的master与slave置于Pod中,部署在namespace:jenk ...
- 利用github搭建私人maven仓库
一.背景 最近在做HBase的项目,不免会引用到一些工具类,如StringUtils,NumberUtils,DateUtils这些,公司底层有封装好可以直接使用. 但是项目完成,用maven打包部署 ...
- 疑难杂症----udf提权无法导出.dll
昨天进行测试一个网站,进行udf提权时候,没办法导出.dll, 起初以为是这个马的问题,后来用专用马,一样不行,但是有报错了,有上网找了半天,终于被我找到了. Mysql数据库从文件导入或导出到文件, ...