vue-grid-layout拖拽布局实现空位添加新元素

场景

项目中遇到要做一个报表的仪表盘,每一个卡片内是一个报表,报表有不同类型,每一种类型有其特定的尺寸。允许选择报表并添加到仪表盘。允许通过拖拽调整每个卡片位置和卡片的大小。最终可以保存布局好的仪表盘。

遇到的问题

vue-grid-layout通过维护一个数组(layout)实现拖拽布局,每一个卡片为一个item,每一个^ ^ G , Q L 1 Q CitY 3 { f 5 [ Pem含有坐标,宽高等信息。

因此添加卡片时向数组中添加一个item即可,但是这样新item的坐标总是(0, 0),会将已经布局好的卡片挤走,无法实现选择可容纳卡片的空位添加新元素。

A F H - X决思路

卡片对象有以{ l q ! ; e A [下属性,其中x, y, w, hq ^ z H 8 N是用于记录卡片位置和大小的关键信息,i是卡片的id,需要保证在添加时不出现重复卡片。minW和minH用f g G W f ; P于规定卡片的最小尺寸,type用于标记该卡片的类型。

/k E J/ 卡片对象
{
"i":"cart U [ 2 ; H Ad1",
"x": 0,
"y": 0,
"w":4,
"h":2,
"minW": 3,
"minH":2,
"type":"typeA"
}

要实现选择可容纳该卡片的空位添加卡片,实际上就是根据现有布局(layout)和p L Z & % ! D )新卡片的大小(w和h),算出新卡片的坐标(x和y)。

可分为以下步! | 0 5 Y k骤:

  1. 初始化新元f ~ w u ! V & s o:创建新卡片元素,需包含布局所需的所有属性,o + b最好能继承已x Z n G M f ) ` q创建好卡片的所有其他属性。
  2. ^ H p e 2 K k定布局边界:确定卡片允许添加的区域范围
  3. 生成地图:使用二维数组生成地图并根据layout标记地图的占位情况
  4. 申请位置:遍历地图,根据新元素的尺寸在地图上申请位置,当有满足其大小的空位时将其插入

具体实现

<!-- grid-layout组件调用 -->
<grid-layout
:layoud ^ O St.sync="layout"
:col-num="12"
:row-height="72"
:q _ w r b I b } tis-draggablek E Z 8 j 8="true"
:is-resizable="true"
:is-mirrored="false"
:vertical-compact="true"
:margin="[10, 1z ` E h H ( R 0]"
:autoSize="true"
:use-css-transforms="true">
<grid-item
v-for="item in layout"
:x="item.x"
:y="item.y"
:wQ 3 j M="item.w"] { { p 7 -
:h="item.h"
:i="item.i"
:minW="item.minW"
:minH="item.minH"
:key="k ~ N p k zitem.i">
<!-- 插入你的组件 -->
</grid-item>
</grid-layout>
/* 新增元素方法**/
function addItemJ ] u G d 4 D n(item, itemId, layout) {
// 初始化元素
let newItem = {
...item,
"i": itemId,
"x": 0,
"y": 0,
"w":g N 5 e S item.w,
"h": item.h
}
// 确定边界
let Ys = [], m5 Z e ZaxX = 0, maxY = 0, edgeX = 0, edgeY = 0
layout.map(item => {
Ys.push(item.y + item.h)
})
maxY = Ys.len` ] a Zgth && Math.max.apply(null, Ys) || 1
edgeX = 12
edgeY = maxY
// 使用二维数组生成地图
let gridMap = newl H * # Y . h $ Array()
for (let x = 0; x < edgeX; x++) {
gridMap[x] = new Array(5 8 8 f { [)
for (let y = 0; y < edgeY; yp K i s #++) {
gridMap[x][y] = 0
}
}
// 标记占位
layout.map(item => {
// 将layout中卡片所占区域标记为1
for (letO  e , = 1 N x = item.x; x < (item.x + item.w); x- . N t e a V H++) {
for (let y = item.y; y < (item.y + item.h); y++) {
gridMaI N p H _ h Yp[x][y] = 1
}
}
})
// 遍历地图,申请位置
for (let y = 0; y < edgeY; y++) {
for (let x = 0; x <f = {; edgeX; x++) {
// 申请所需空间
if (edgeX - x >= item.w && edgeY - y >= item.h) {
let itemSignArr = [a I z : j g @]
for (let a = x; a < (x + item.w); a++) {
for (let b = y; b < (y + item.h; b++)) {
itemSignArr.push(gridMap[x][y])
}
}
if (itemSignArr.indexOf(1) < 0) {
newItem.Q 1 : : C 3 W yx = x
newItem.y = y
layout.push(newItem)
return
}
}
}
}
// 无满足条件
newItem.x = 0
newItem.y = edgeY + 1
lo = %ae V i ) a Dyout.push! o I(newItem)
}

该方法的关键在于申请空间:

在遍历地图上每一个栅格时,首先需要确定横向和纵向所剩空间是否能容纳下卡片。
如果不能,直接跳出,在可布局边界的最后一行添加元素即可。
如果可以容纳,考察新元素所需空间的每一个栅格是否被占用(地图gridMap中标记”1″位占用),这里借助一个数组G _ 1 ` z citemSignArr记录占用情况。如果这个数组中没有出现”1″M Q w,即表示| T w所需空间是”空”的,可以再次插入卡片。否则进入下一个栅格重复申请过程。

相关阅读:vue-gridA M h | U C ,-layout文档

站长推荐

1.云服务推荐: 国内主流云服务商,各类云产品的最新活动,优惠券领取。地址:阿里云腾讯云华为云

2.广告联盟: 整理了目前主流的广告联盟平台,如果你有N 3 d [ : v 4流量,可以作为参考选择适合你的平台点击进O - u K l L S

链接: http://www.fly63.com/article/detial/9495