基于VueGridLayout的低代码平台:解决自定义位置插入卡片和横轴覆盖问题
前言
在传统的低代码平台中,卡片的布局通常是基于网格系统或者预定义的模板。但这种布局方式可能会限制用户的创意和灵活性。 基于VueGridLayout的低代码平台克服了这一限制,允许用户自由拖拽、调整卡片的位置和大小,以适应不同的需求,所以目前很多低代码平台在处理拖拽组织逻辑的时候,都会借鉴 VueGridLayout 或者直接使用。
最近在开发低代码平台的时候就遇到了关于 VueGridLayout 的一个坑——自定义位置插入卡片和横轴覆盖问题
问题原因
前置知识
在谈论问题原因之前,我们不妨看看 VueGridLayout 提供了什么功能。

VueGridLayout 支持响应布局,就是当我们拖动的时候它可以支持自动填充,也可以支持自动吸顶
,这种功能可以很方便的用于低代码平台的开发。
问题重现
我需要实现的功能是在这个布局中某个指定的卡片下新增一个自定义卡片,高宽不定.
举个例子:
在 卡片4
下面新增一个 卡片a
,卡片a
可能会很长也可能很短,当我插入这个卡片之后,后面的卡片都要往下移或者说位置要发生对应的改动。
如下图所示:

这里插入一个卡片a后,下面的卡片都会下移,效果如下:

因为使用的是VueGridLayout
的拖拽能力,所以目前的样式是很正常的。
但是如果我们希望通过js的方式,直接往里面插入一个卡片要怎么操作呢?
实际上VueGridLayout
上面的所有模块 对应的都是layout数组的一项,每一项都有宽,高,横,竖坐标

所以当我们希望往里面加入一个自定义卡片的时候,可以通过往layout数组 插入一项的方式实现,并设置当前项的大小和位置即可。
所以对于刚刚的操作,如果我们希望往 卡片4 下面插入一个 新的卡片
,那么我们的操作顺序应该是:
- 获得
卡片4
的横轴坐标,纵轴坐标,以及卡片4的高度
- 基于卡片4的信息,设置
目标卡片的横轴坐标为 卡片4的横轴坐标
,目标卡片的纵轴坐标为 卡片4的纵轴坐标加上卡片4的高度
- 将
目标卡片插入到 layout数组
即可,然后我们目标卡片就会出现在指定位置,并且VueGridLayout会帮我们重新布局

我直接说结论:
如果我们插入的卡片宽度
未超过目标卡片的宽度
时,那么我们的插入效果会非常完美。
但是如果 我们插入的卡片宽度
超过目标卡片的宽度
时,就可能会和旁边的卡片发生重叠问题。

可以看到这里 19 直接覆盖了 2和3 这显然是我们不想看到的。
问题的原因是,当我们自定义卡片并设置位置时,VueGridLayout
对于目标插入位置纵轴下方的卡片会重新布局
,但是横轴方向上的卡片却不会重新布局
就导致了这种覆盖问题。
问题解决
通过上面的分析,我们知道了为什么会导致这种覆盖情况。

当 19
插入到 4 的下方
时, 13 以及后面的所有卡片会被重新布局
所以纵轴方向上没有问题。
但是因为此时的卡片定位在了 4 的下方
,那么当宽度超过 4
的时候, 19
就会把 4
右边的 其宽度可覆盖的其他卡片盖住。
因此就出现了这个 自定义位置插入卡片和横轴覆盖问题
解决思路
那么我们怎么解决这个问题呢?
实际上这个问题是我们插入位置不对导致的,虽然我们要插入到 4 的下方
,但我们卡片的纵轴坐标应该是基于最长的 2 来定。

当我们的卡片插入到我们覆盖的其他卡片的 中
纵轴坐标加上高度 最大的卡片时
就不会出现覆盖的问题
了。
思路如下:
- 先基于目标卡片 初始化我们需要插入的卡片,但是我们此时的卡片还不能直接插入,因为会导致覆盖问题。
- 基于layout数组,计算所有和我们初始化卡片在二维坐标系上发生重叠的卡片。
- 找到所有在目标卡片右侧的卡片
- 在上面的卡片中找
纵轴坐标加上高度
最大的卡片,并将初始化的卡片的纵轴设置为最大卡片的纵轴坐标加上高度
(这一步就相当于把 19 放到 2 的下面)
代码实现
数据初始化
// 卡片数组
let layout = [
{"x":0,"y":0,"w":2,"h":2,"i":"0"},
{"x":2,"y":0,"w":2,"h":4,"i":"1"},
{"x":4,"y":0,"w":2,"h":5,"i":"2"},
{"x":6,"y":0,"w":2,"h":3,"i":"3"},
{"x":8,"y":0,"w":2,"h":3,"i":"4"},
{"x":10,"y":0,"w":2,"h":3,"i":"5"},
{"x":0,"y":5,"w":2,"h":5,"i":"6"},
{"x":2,"y":5,"w":2,"h":5,"i":"7"},
{"x":4,"y":5,"w":2,"h":5,"i":"8"},
{"x":6,"y":3,"w":2,"h":4,"i":"9"},
{"x":8,"y":4,"w":2,"h":4,"i":"10"},
{"x":10,"y":4,"w":2,"h":4,"i":"11"},
{"x":0,"y":10,"w":2,"h":5,"i":"12"},
{"x":2,"y":10,"w":2,"h":5,"i":"13"},
{"x":4,"y":8,"w":2,"h":4,"i":"14"},
{"x":6,"y":8,"w":2,"h":4,"i":"15"},
{"x":8,"y":10,"w":2,"h":5,"i":"16"},
{"x":10,"y":4,"w":2,"h":2,"i":"17"},
{"x":0,"y":9,"w":2,"h":3,"i":"18"},
{"x":2,"y":6,"w":2,"h":2,"i":"19"}
],
// 目标卡片
let targetC = {"x":0,"y":0,"w":2,"h":2,"i":"0"},
// 初始化卡片
let currentC = {"x":targetC.x,"y":targetC.y,"w":8,"h":2,"i":"20"},
设置初始化卡片的真实宽高
// 存储与初始化卡片重叠的卡片
const overlappingCards = []
// 遍历对象数组,检查每个对象是否与初始化卡片重叠
for (const obj of layout) {
if (
obj.x < currentC.x + currentC.w
&& obj.x + obj.w > currentC.x
&& obj.y < currentC.y + currentC.h
&& obj.y + obj.h > currentC.y
) {
overlappingCards.push(obj)
}
}
// 获取重叠卡片中,在目标卡片右侧的卡片集合
const targetOverlappingCards = overlappingCards.filter(item => item.x > targetC.x + targetC.w)
// targetOverlappingCards中找 纵轴坐标加上高度 最大的卡片。
// 并返回 纵轴坐标加上高度 的最大值 作为初始化卡片的真实纵坐标
const maxY = targetOverlappingCards.reduce((max, item) => {
return Math.max(max, item.y + item.h)
}, 0)
// 设置初始化卡片的真实坐标
currentC.x = targetC.x
currentC.y = maxY
// 插入layout数组
layout.push(currentC)
问题总结
到此问题就基本解决了,但是实际应用时还需处理其他边界情况,比如目标卡片的横轴下标
加上初始化卡片的宽度
超过边界时,就相当于这个卡片加进去就会溢出卡片图床,对于这种情况需要单独处理下。
总体来说是一个定位错误导致的覆盖问题
思路还是很简单的:
先正常基于目标卡片的位置去定位我们初始化卡片 然后判断整个卡片集合中哪些卡片和我们初始化卡片发生了重叠 将目标卡片右侧的重叠卡片筛选出来,并找到其中 纵轴坐标加上高度 的最大值,作为目标卡片的纵轴坐标,就可以定位到正确的卡片位置。
转载自:https://juejin.cn/post/7268221660527935540