相信有不同志已经使用过 vue-grid-layout 这个插件了,对于界面化拖拽布局操作起来还是非常友好的
前几天看到一个项目,要在此基础上优先添加到空白位置。
vue-grid-layout 插件本身可以通过对数组数据的 push,进行添加数据,但必要的 x , y , w , h , i 是必须的。
当然,你可以对 x 和 y 强行赋 0 , 他为将此区域内的基本项顺序往下挤。可我想我的是有空缺且能够放下时,优先使用空缺位置,没有空缺时,从上住下顺序添加。
<template>
<div id="gridPage">
<h1>grid</h1>
<div class="btnGroup">
<button @click="addItem">add</button>
</div>
<div class="view">
<grid-layout
:layout.sync="layout"
:col-num="layoutColNum"
:row-height="10"
:is-draggable="true"
:is-resizable="true"
:is-mirrored="false"
:vertical-compact="true"
:margin="[10, 10]"
:use-css-transforms="true"
@layout-updated="layoutUpdatedEvent"
@layout-ready="layoutReadyEvent"
>
<grid-item v-for="(item,idx) in layout"
class="item"
:x="item.x"
:y="item.y"
:w="item.w"
:h="item.h"
:i="item.i"
:key="idx">
{{item.i}}|{{idx}}
</grid-item>
</grid-layout>
</div>
</div>
</template>
<style lang="less">
#gridPage{
>h1{font-size: 16px;}
>.view{
border: 1px solid #000;
.item{background-color: #aaa;}
}
}
</style>
<script>
import VueGridLayout from 'vue-grid-layout';
export default {
name: 'girdPage',
data: function (){
return {
// 布局位置数据
layout: [],
// 布局二维数组地图
layoutMap: [],
// 布局列数
layoutColNum: 50
}
},
methods: {
// 添加项目
addItem: function () {
// 产生随机宽高
var itemW = this.rnd(5,10);
var itemH = this.rnd(5,10);
var addItem = {
"x": 0,
"y": this.layoutMap.length,
"w": itemW,
"h": itemH,
"i": this.layout[this.layout.length-1]?parseInt(this.layout[this.layout.length-1].i)+1:0
};
if(this.layoutMap.length){// console.log(this.layoutMap.length);
for(let r = 0 , rLen =this.layoutMap.length ; r < rLen; r++){for(let c = 0; c <= (this.layoutColNum-itemW); c++){
let res = this.regionalTest(
c,
r,
itemW,
rLen>(r+itemH)?itemH:rLen-r
);
if(res.result){
// 更新添加数据内容
addItem = {
"x": res.x,
"y": res.y,
"w": itemW,
"h": itemH,
"i": parseInt(this.layout[this.layout.length-1].i)+1
};
c = this.layoutColNum+1;
r = rLen+1;
}else{c = res.offsetX;}
}
}
}
// 更新二维数组地图
for(let itemR = 0 ; itemR < itemH ; itemR++){for(let itemC = 0 ; itemC < itemW ; itemC++){
// 如果没有该行,初始化
if(!this.layoutMap[addItem.y+itemR]){this.layoutMap[addItem.y+itemR] = new Array(this.layoutColNum);
for(let i = 0 ;i < this.layoutColNum ; i++){this.layoutMap[addItem.y+itemR][i] = 0;
}
}
// 标记点
this.layoutMap[addItem.y+itemR][addItem.x+itemC] = 1;
}
}
// console.log(this.layoutMap);
// 添加数据
this.layout.push(addItem);
},
// 生成二维数组地图
genereatePlaneArr: function (data) {var map = [];
if(Array.isArray(data)){for(var i = 0; i<data.length; i ++){var one = data[i];
// 循环行
for(var r = one.y ; r < ( one.y + one.h) ; r++){
// 循环列
for(var c = one.x ; c < ( one.x + one.w) ; c++){
// 检修当前行是否存在
if(!map[r]){map[r] = new Array(this.layoutColNum);
for(let i = 0 ; i < this.layoutColNum ; i++){map[r][i] = 0;
}
}
// 占据为 1
map[r] = 1;
}
}
}
}
return map;
},
// 区域检测 x,y 二维数据地图起始坐标点 w,h 检测区域宽高
regionalTest: function (x,y,w,h) {
// 定义返回 x,y 偏移 及 是否有空位置
let offsetX = 0,offsetY = 0,res = true;
// 按区域循环检测 二维数组地图
for(let r = 0; r < w ;r++){for(let c = 0; c <= h ;c++){let point = this.layoutMap[y+r]?this.layoutMap[y+r][x+c]:0;
// 如该点被占据 记录偏移值
if(point===1){
res = false;
offsetX = offsetX>(x+c)?offsetX:x+c;
offsetY = offsetY>(y+r)?offsetY:y+r;
}
}
}
return {
result: res,
offsetX: offsetX,
x: x,
y: y
};
},
// 组件更新完成生命周期
layoutReadyEvent: function(newLayout) {// console.log("4Ready");
// console.log(this.layout);
// 当插件加载完成后 获取现在的二维地图树
this.layoutMap = this.genereatePlaneArr(this.layout);
},
layoutUpdatedEvent: function(newLayout) {// console.log("Updated");
// console.log(this.layout);
// 当插件内容布局发生变化后 获取现在的二维地图树
this.layoutMap = this.genereatePlaneArr(this.layout);
},
rnd: function (m,n) {return (Math.random()*(m-n+1)+n)|0;
}
},
components: {
GridLayout: VueGridLayout.GridLayout,
GridItem: VueGridLayout.GridItem
}
}
简单写的一个 Demo, 不足之处还望指正