vue-grid-layout拖拽佈局實現空位添加新元素

場景

項目中遇到要作一個報表的儀表盤,每個卡片內是一個報表,報表有不一樣類型,每一種類型有其特定的尺寸。容許選擇報表並添加到儀表盤。容許經過拖拽調整每一個卡片位置和卡片的大小。最終能夠保存佈局好的儀表盤。javascript

遇到的問題

vue-grid-layout經過維護一個數組(layout)實現拖拽佈局,每個卡片爲一個item,每個item含有座標,寬高等信息。css

所以添加卡片時向數組中添加一個item便可,可是這樣新item的座標老是(0, 0),會將已經佈局好的卡片擠走,沒法實現選擇可容納卡片的空位添加新元素。html

解決思路

卡片對象有如下屬性,其中x, y, w, h是用於記錄卡片位置和大小的關鍵信息,i是卡片的id,須要保證在添加時不出現重複卡片。minWminH用於規定卡片的最小尺寸,type用於標記該卡片的類型。vue

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

要實現選擇可容納該卡片的空位添加卡片,實際上就是根據現有佈局(layout)和新卡片的大小(wh),算出新卡片的座標(xy)。java

可分爲如下步驟:npm

  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)
}

該方法的關鍵在於申請空間:json

在遍歷地圖上每個柵格時,首先須要肯定橫向和縱向所剩空間是否能容納下卡片。
若是不能,直接跳出,在可佈局邊界的最後一行添加元素便可。
若是能夠容納,考察新元素所需空間的每個柵格是否被佔用(地圖gridMap中標記"1"位佔用),這裏藉助一個數組itemSignArr記錄佔用狀況。若是這個數組中沒有出現"1",即表示所需空間是"空"的,能夠再次插入卡片。不然進入下一個柵格重複申請過程。數組

相關閱讀

vue-grid-layout文檔app

相關文章
相關標籤/搜索