场景
我的项目中遇到要做一个报表的仪表盘,每一个卡片内是一个报表,报表有不同类型,每一种类型有其特定的尺寸。容许抉择报表并增加到仪表盘。容许通过拖拽调整每个卡片地位和卡片的大小。最终能够保留布局好的仪表盘。
遇到的问题
vue-grid-layout通过保护一个数组(layout
)实现拖拽布局,每一个卡片为一个item,每一个item含有坐标,宽低等信息。
因而增加卡片时向数组中增加一个item即可,然而这样新item的坐标总是(0, 0)
,会将曾经布局好的卡片挤走,无奈实现抉择可包容卡片的空位增加新元素。
解决思路
卡片对象有以下属性,其中x
, y
, w
, h
是用于记录卡片地位和大小的要害信息,i
是卡片的id,须要保障在增加时不呈现反复卡片。minW
和minH
用于规定卡片的最小尺寸,type
用于标记该卡片的类型。
// 卡片对象{ "i":"card1", "x": 0, "y": 0, "w":4, "h":2, "minW": 3, "minH":2, "type":"typeA"}
要实现抉择可包容该卡片的空位增加卡片,实际上就是依据现有布局(layout
)和新卡片的大小(w
和h
),算出新卡片的坐标(x
和y
)。
可分为以下步骤:
- 初始化新元素:创立新卡片元素,需蕴含布局所需的所有属性,最好能继承已创立好卡片的所有其余属性。
- 确定布局边界:确定卡片容许增加的区域范畴
- 生成地图:应用二维数组生成地图并依据
layout
标记地图的占位状况 - 申请地位:遍历地图,依据新元素的尺寸在地图上申请地位,当有满足其大小的空位时将其插入
具体实现
<!-- 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文档