场景

我的项目中遇到要做一个报表的仪表盘,每一个卡片内是一个报表,报表有不同类型,每一种类型有其特定的尺寸。容许抉择报表并增加到仪表盘。容许通过拖拽调整每个卡片地位和卡片的大小。最终能够保留布局好的仪表盘。

遇到的问题

vue-grid-layout通过保护一个数组(layout)实现拖拽布局,每一个卡片为一个item,每一个item含有坐标,宽低等信息。

因而增加卡片时向数组中增加一个item即可,然而这样新item的坐标总是(0, 0),会将曾经布局好的卡片挤走,无奈实现抉择可包容卡片的空位增加新元素。

解决思路

卡片对象有以下属性,其中x, y, w, h是用于记录卡片地位和大小的要害信息,i是卡片的id,须要保障在增加时不呈现反复卡片。minWminH用于规定卡片的最小尺寸,type用于标记该卡片的类型。

// 卡片对象{  "i":"card1",  "x": 0,  "y": 0,  "w":4,  "h":2,  "minW": 3,  "minH":2,  "type":"typeA"}

要实现抉择可包容该卡片的空位增加卡片,实际上就是依据现有布局(layout)和新卡片的大小(wh),算出新卡片的坐标(xy)。

可分为以下步骤:

  1. 初始化新元素:创立新卡片元素,需蕴含布局所需的所有属性,最好能继承已创立好卡片的所有其余属性。
  2. 确定布局边界:确定卡片容许增加的区域范畴
  3. 生成地图:应用二维数组生成地图并依据layout标记地图的占位状况
  4. 申请地位:遍历地图,依据新元素的尺寸在地图上申请地位,当有满足其大小的空位时将其插入

具体实现

<!-- grid-layout组件调用 --><grid-layout    :layout.sync="layout"    :col-num="12"    :row-height="72"    :is-draggable="true"    :is-resizable="true"    :is-mirrored="false"    :vertical-compact="true"    :margin="[10, 10]"    :autoSize="true"    :use-css-transforms="true">        <grid-item            v-for="item in layout"            :x="item.x"            :y="item.y"            :w="item.w"            :h="item.h"            :i="item.i"            :minW="item.minW"            :minH="item.minH"            :key="item.i">            <!-- 插入你的组件 -->        </grid-item></grid-layout>
/* 新增元素办法**/function addItem(item, itemId, layout) {  // 初始化元素  let newItem = {    ...item,    "i": itemId,    "x": 0,    "y": 0,    "w": item.w,    "h": item.h  }  // 确定边界  let Ys = [], maxX = 0, maxY = 0, edgeX = 0, edgeY = 0  layout.map(item => {    Ys.push(item.y + item.h)  })  maxY = Ys.length && Math.max.apply(null, Ys) || 1  edgeX = 12  edgeY = maxY  // 应用二维数组生成地图  let gridMap = new Array()  for (let x = 0; x < edgeX; x++) {    gridMap[x] = new Array()    for (let y = 0; y < edgeY; y++) {      gridMap[x][y] = 0    }  }  // 标记占位  layout.map(item => {    // 将layout中卡片所占区域标记为1    for (let x = item.x; x < (item.x + item.w); x++) {      for (let y = item.y; y < (item.y + item.h); y++) {        gridMap[x][y] = 1      }    }   })  // 遍历地图,申请地位  for (let y = 0; y < edgeY; y++) {    for (let x = 0; x < edgeX; x++) {      // 申请所需空间      if (edgeX - x >= item.w && edgeY - y >= item.h) {        let itemSignArr = []        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.x = x          newItem.y = y          layout.push(newItem)          return        }      }    }  }  // 无满足条件  newItem.x = 0  newItem.y = edgeY + 1  layout.push(newItem)}

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

在遍历地图上每一个栅格时,首先须要确定横向和纵向所剩空间是否能包容下卡片。
如果不能,间接跳出,在可布局边界的最初一行增加元素即可。
如果能够包容,考查新元素所需空间的每一个栅格是否被占用(地图gridMap中标记"1"位占用),这里借助一个数组itemSignArr记录占用状况。如果这个数组中没有呈现"1",即示意所需空间是"空"的,能够再次插入卡片。否则进入下一个栅格反复申请过程。

相干浏览

vue-grid-layout文档