通过css + html 实现节点流程布局
需求背景:
在开发可视化流程节点编排项目时需要做一个节点的执行流程结果的展示用于呈现某条流程的执行过程,大致展示结果如下图:
说实话,刚拿到设计图时很闷,这咋做(低声细语好恶心的东西),但是没办法呀!有困难就要去克服,本着一个专业程序员的修养(默默打开了百度),一番搜寻无果——属实搞心态。索性放下去搞别的了,不能把其他工作卡住呀。当其他工作OK后又拿起了这个需求左看又看,突然脑子中浮现了新的想法。
实现思路:
- 首先将拿到的节点数据进行格式化操作,将原本的一维数组,改为二维数组,再将偶数行数据逆排列。
- html 部分将一维数组作为行排列,将二维数组进行列排列,在通过flex布局控制奇偶行样式,实现,至于节点间连线,让UI切了张图放入节点连接处。
既然有了思路那就撸起袖子加油干——开始撸代码了!!
代码实现:
- 先将节点数据进行格式化操作
formatNodes(list){
let newList = deepClone(list)
let data = [];
/*
index: 当前data数组需要添加的索引
start: 开始截取的位置
end: 截取结束的位置
*/
const deep = (index, start, end) => {
// 当截取内容为空时结束递归循环
if(!newList.slice(start,end).length){
return
}else {
// 当index索引为奇数时(也就是偶数行),将截取到的数据逆排列添加
if(index % 2 === 1){
data[index] = [...newList.slice(start,end)].reverse()
}else {
// 当为偶数时(也就是奇数行),将截取数据正向排列添加
data[index] = [...newList.slice(start,end)]
}
deep(++index, end, end+5)
}
}
deep(0, 0, 5)
return data;
}
- 定义html结构:通过索引判断当前行为奇数行还是偶数行,偶数行时添加
.right
类名使连线向右,奇数行相反,并需要在奇偶行对应的开头结尾处添加.left-bottom
和.right-bottom
使联想向下旋转。
<div class="node-order">
<div class="node-row"
:style="{
justifyContent: index % 2 === 1 &&
item.length < 5 ?
'flex-end' :
'space-between'
}"
v-for="(item,index) in keyPoints"
:key="index">
<div
class="node-col"
v-for="(val,key) in item"
:key="key">
<div>
<span class="node-name">{{val}}</span>
<div
:class="['node-arrow', item[0] > item[item.length-1] ? 'right-bottom' : 'left-bottom']"
v-if="(index % 2 === 1 && item.length == 5 && key === 0) || (index % 2 === 0 && item.length == 5 && key === 4)">
<img src="../../../../public/img/icon/arrow.png" alt="连线">
</div>
</div>
<div
:class="['node-arrow', index % 2 === 1 ? 'left' : 'right']"
:style="{marginTop: index % 2 === 1 ? '12px' : '0px'}"
v-if="key < item.length -1">
<img src="../../../../public/img/icon/arrow.png" alt="连线">
</div>
</div>
</div>
</div>
- 设置css样式
.node-order {
padding: 32px 28px;
box-sizing: border-box;
.node-row {
width: 100%;
display: flex;
justify-content: space-between;
margin-top: 12px;
&:first-child {
margin-top: 0px;
}
.node-col {
display: flex;
justify-content: flex-end;
& div:first-child {
white-space: nowrap;
}
}
}
.node-arrow {
width: 34px;
height: 10px;
}
.left,
.right {
margin: 0px 12px;
}
.left {
transform: rotate(180deg);
}
.right {
transform: rotate(0deg);
}
.right-bottom,
.left-bottom {
margin-top: 12px;
height: 22px;
transform: rotate(90deg);
}
.right-bottom {
justify-content: flex-end;
}
.node-name {
line-height: 22px;
}
}
- 到这里就实现了原型图的样式了,其实实现后觉得并不难,代码都很简单,但是作为一个新思路吧 我还是决定记录下来,毕竟在做的过程中还是遇到了一些好的东西。
其实在,最开始的时候有想着去找一些现成的东西去做,比如:
- antv/x6:
demo:https://x6.antv.vision/zh/examples/layout/general#grid
官网:https://x6.antv.vision/zh
github:https://github.com/antvis/x6
在x6中有一个网格布局很类似,这个我也实现出来了,但是效果不太好,而且不仅要安装@antv/x6还要安装@antv/layout两个组件库才可以实现,为之所以舍弃这个是因为需要安装两个组件库(虽说项目中已经有x6组件库了),但还有一点是因为它生成的结构不能合理的适配,所有我果断放弃了。但并不意味这它不好,蚂蚁金服的可视化组件库还是很不错的,在这个项目中我就用x6实现了一个编排数据分析图,还是要为x6点赞。有兴趣的可以看看这个库。
2. go.js
demo:https://gojs.net.cn/extensions/Serpentine.html
官网:https://gojs.net.cn/index.html
这个js库也是同事之前有过开发经验推荐我的,这个可视化js库内部有很多示例,很强大,但是自己没有时间学习。我在这个js库中找到了我想要的效果图
当我打开go.js源码一看后我果断抛弃了他,理由同样是因为需要安装两个库SerpentineLayout.js.
和go.js
,因为go.js内部的代码实在太多了,用于实现这样的小功能属实有点“杀鸡用牛刀”了,之所以经过这两个组件库尝试的不理想,才有了我后来的实现思路。
在写的过程中还发现了一个不错的scss-mixin库
,他可以混入你的scss代码,当你需要对节点的数量进行判断时他就派上用场了
github:https://github.com/danielguillan/quantity-queries
// 查询等于N
@include exactly($count){...}
// 查询大于或等于N
@include at-least($count){...}
// 查询小于或等于N
@include at-most($count){...}
// 查询大于或等于M与小于或等于N
@include between($first, $last){...}
是不是感觉很强大。