所見即所得, 經過拖拽實現一個自定義佈局

前言

該項目基於react-grid-layoutantd的柵格佈局實現。可能不適用全部項目, 可是思路也能夠參考。react

demo體驗地址: 自定義佈局git

源碼地址: 源碼github

實現功能

經過可視化拖拽區塊, 組合區塊生成自定義佈局代碼, 可直接引入有antd的項目中使用。數組

上面可視化操做後生成的佈局代碼:antd

<Row>
    <Col span={10} className="drag-item">
        <Col className="drag-subitem" style={{ height: '10vh' }} span={24}></Col>
        <Col className="drag-subitem" style={{ height: '95vh' }} span={24}></Col>

    </Col>
    <Col span={14} className="drag-item">
        <Col className="drag-subitem" style={{ height: '25vh' }} span={24}></Col>
        <Col className="drag-subitem" style={{ height: '80vh' }} span={24}></Col>

    </Col>
</Row>
複製代碼

代碼生成的頁面:數據結構

實現思路

要想生成任意佈局, 柵格佈局是最合適的。dom

這裏不考慮特殊狀況下的不規則佈局。工具

咱們先將佈局按列劃分, 每一列裏面再分紅幾塊, 這樣基本就能知足咱們大部分的業務場景了。佈局

提及來簡單,可是要和可視化拖拽結合起來就沒那麼簡單了。ui

咱們經過拖拽獲得每一個區塊的寬高和位置信息, 藉此來肯定咱們的區塊應該放在哪一個位置。

而後將其與antd的柵格佈局的API結合, 生成咱們想要的佈局代碼。

用過antd的柵格佈局的應該知道, Row控制行佈局, Col控制列布局。個人思路是:

整個頁面都是一個Row, 而後根據拖拽信息劃分列, 每一個列裏都是佔滿整個列寬的列, 這樣就能夠往下堆疊。

Col經過span屬性控制寬度, 高度咱們直接經過style屬性加上去。

實現

實現可視化拖拽

這裏不得不介紹一下強大的react-grid-layout庫了。

git地址: react-grid-layout

咱們只須要提供簡單的基礎信息就能生成可拖拽可伸縮的區塊。 這裏對API不作過多介紹, 主要將實現。

const dragItem = { x: 14, y: 14, w: 1, h: 1, maxH: 20 }

<GridLayout className="layout"
    onResizeStop={onResizeStop}
    onDragStop={onDragStop}
    preventCollision={true}
    margin={[0, 0]}
    cols={12}
    rowHeight={30}
    width={800}
    style={{ height: 600 }}
>
    {
        Object.keys(dragObj).map(item => <div className="drag-item" style={{ backgroundColor: '#' + ('00000' + (Math.random() * 0x1000000 << 0).toString(16)).slice(-6) }} key={item} data-grid={dragObj[item]}></div>)
    }
</GridLayout>
                
複製代碼

數據驅動視圖, 咱們經過添加區塊事件改變代碼中的dragObj, 便可實現往視圖中添加區塊。

當咱們改變了視圖中的區塊位置或大小時, 咱們須要保存當前的區塊信息, 方便咱們接下來的生成佈局代碼。

上面代碼中的onResizeStoponDragStop事件能夠幫咱們監聽咱們的拖拽結束和改變區塊大小的動做。

這兩個事件作的事情是相同的, 記錄當前區塊狀態, 這裏看下事件代碼:

const onResizeStop = (arr) => {
        let obj = {}, layoutObj = {}
        arr.forEach(item => {
            obj[item['i']] = { x: item['x'], y: item['y'], w: item['w'], h: item['h'], maxH: item['maxH'] }
            if (layoutObj[item['x']]) {
                layoutObj[item['x']].push(item)
            } else {
                layoutObj[item['x']] = [item]
            }
        })
        setLayoutObj(layoutObj)
        setDragObj(obj)
    }
複製代碼

這兩個事件都會給咱們提供一個包含全部區塊信息的數組參數, 咱們經過對這個參數作解析, 將其改形成咱們可使用的數據結構, 並保存在state中。

解析對象生成代碼

當咱們經過拖拽生成咱們想要的數據結構後, 要生成咱們想要的頁面, 此時咱們須要知道, 區塊的位置和寬高如何映射到咱們的佈局中去

我這裏經過約定: 拖拽區域大小爲寬800, 高600。 每一個區塊的基礎劃分:

將高度20等分, 寬度12等分。

這樣:

一個單位的h對應0.5vh的高度;

一個單位的w對應2個單位的span(antd的Col的span是24等分)

有了映射關係, 咱們只要把存儲區塊信息的對象改形成代碼串就好了:

const onCodeGenerator = () => {
        setCodeStr(
            '<Row>\n' 
            + 
            Object.keys(layoutObj).map(item => ` <Col span={${layoutObj[item][0].w * 2}} className="drag-item"> ${layoutObj[item].map(subItem => ` <Col className="drag-subitem" style={{height:'${subItem.h * 5}vh'}} span={24}></Col>\n`).join('')} </Col>\n`
            ).join('')
            +
            '</Row>'
        )

    }
複製代碼

經過上面的代碼進行改造, 就能獲得咱們想要的代碼串, 直接複製, 粘貼到咱們的項目中就獲得了一個合適的佈局。

後話

這算是一個工具性的東西, 我是根據當前公司業務的基礎上進行的開發, 因此可能對你們的適用性不是很高, 這裏記錄下思路, 方便之後有相似的需求能夠參考。

相關文章
相關標籤/搜索