基于自然流布局的可視化拖拽搭建平臺(tái)設(shè)計(jì)方案
LowCode 是高效、高性能的拖拽式低代碼開(kāi)發(fā)平臺(tái). 也是筆者最近一直在研究的方向, 對(duì)于可視化搭建平臺(tái)的實(shí)現(xiàn)方案筆者之前寫過(guò)很多文章, 這里帶大家探索一個(gè)新方向——基于自然流布局的可視化搭建平臺(tái).
在我們之前實(shí)現(xiàn)的 h5-dooring 搭建平臺(tái)中, 我們采用了網(wǎng)格布局的方式來(lái)實(shí)現(xiàn)拖拽生成H5頁(yè)面或者Web app, 其好處就是靈活簡(jiǎn)單, 用戶基本沒(méi)有任何使用成本, 在前端層也能做一定的橫向擴(kuò)展, 但是存在幾個(gè)缺陷:
- 實(shí)現(xiàn)嵌套組件比較復(fù)雜
- 沒(méi)有層的概念
雖然通過(guò)改造可以實(shí)現(xiàn)層和嵌套的問(wèn)題, 最近也在努力往這個(gè)方向?qū)崿F(xiàn)(雖然和設(shè)計(jì)初衷相駁, dooring的初衷是抹去層和嵌套的概念, 讓搭建扁平化和智能化, 所以沒(méi)有采用自由布局的方案)
但是如果一定要實(shí)現(xiàn)嵌套和層的功能, 有沒(méi)有另一種更簡(jiǎn)單的方案呢? 筆者目前想到了兩種解決方案:
將智能布局改為自由布局, 即可以采用類似 react-resizable 的這種方案
基于自然流來(lái)實(shí)現(xiàn), 也就是抹去定位的概念, 完全基于元素在文檔的順序, 層級(jí)和定位的選擇權(quán)交給用戶
因?yàn)榈谝环N方案筆者在dooring的早期已經(jīng)實(shí)現(xiàn)過(guò)一版, 最后棄用采用了網(wǎng)格布局, 所以說(shuō)我們來(lái)探討一下第二種方案的實(shí)現(xiàn).
基于自然流布局實(shí)現(xiàn)拖拽生成頁(yè)面
自然流布局的好處就是我們不用通過(guò)定位的方式來(lái)限定元素的位置等信息, 而是以html文檔流的方式來(lái)布局元素, 并且用戶可以靈活的設(shè)置元素的層級(jí)(layer)和偏移(transform), 接下來(lái)我們來(lái)看看簡(jiǎn)單的實(shí)現(xiàn)效果.
1. demo效果
由上圖的demo我們可以發(fā)現(xiàn)組件在畫布中的布局完全是默認(rèn)的文檔流的方式, 所以我們有更靈活的布局實(shí)現(xiàn).
2. 實(shí)現(xiàn)思路
具體實(shí)現(xiàn)思路主要分以下幾個(gè)部分:
- 組件區(qū)拖拽至畫布
- 畫布區(qū)拖拽
- 組件編輯器和更新機(jī)制
第一點(diǎn)和第三點(diǎn)我們?cè)?H5-dooring中已經(jīng)實(shí)現(xiàn)了, 感興趣的可以看我之前的文章, 我們這里重點(diǎn)來(lái)實(shí)現(xiàn)畫布區(qū)拖拽, 也是比較核心的環(huán)節(jié).
2.1 H5拖放api基本介紹
拖放(Drag 和 drop)是 HTML5 標(biāo)準(zhǔn)的組成部分, 早已被大多數(shù)瀏覽器支持. 我們目前使用的拖放插件基本上基于 H5 拖放 API 來(lái)實(shí)現(xiàn)的, 其實(shí)實(shí)現(xiàn)第一點(diǎn)組件區(qū)拖拽至畫布我們完全可以用原生來(lái)實(shí)現(xiàn), 這里筆者簡(jiǎn)單來(lái)介紹以下.
首先我們來(lái)看看一個(gè)完整的拖放過(guò)程:
- 首先要設(shè)置一個(gè)元素可拖放(比如)
- 設(shè)計(jì)拖動(dòng)的時(shí)候會(huì)發(fā)生什么(需要用到ondragstart事件 和 setData(你要傳遞的數(shù)據(jù)))
- 放到何處,也就是目標(biāo)容器(通常在目標(biāo)容器上綁定ondragover和ondrop事件)
有了以上3個(gè)步驟, 我們就能實(shí)現(xiàn)第一點(diǎn)的需求, 筆者寫個(gè)簡(jiǎn)單demo來(lái)給大家參考一下:
- <script type="text/javascript">
- function allowDrop(ev) {
- ev.preventDefault();
- }
- function drag(ev){
- ev.dataTransfer.setData("Text",ev.target.id);
- }
- function drop(ev){
- ev.preventDefault();
- let data=ev.dataTransfer.getData("Text");
- ev.target.appendChild(document.getElementById(data));
- }
- </script>
- <div id="box" ondrop="drop(event)" ondragover="allowDrop(event)"></div>
- <img id="drag" src="dooring.png" draggable="true" ondragstart="drag(event)" width="336" height="69" />
也就是對(duì)應(yīng)的我們的組件拖放區(qū)域, 如下圖所示:
2.2 畫布區(qū)拖拽布局實(shí)現(xiàn)
因?yàn)橹暗陌姹疚覀儾捎昧司W(wǎng)格布局來(lái)實(shí)現(xiàn)智能拖拽, 由于內(nèi)部定位機(jī)制采用的是絕對(duì)定位(absolute), 所以是實(shí)現(xiàn)層級(jí)和固定組件比較困難, 如果組件的呈現(xiàn)完全脫離了定位的束縛, 我們就可以實(shí)現(xiàn)以上的困境了. 所以這里我們調(diào)研了一種方案——拖拽排序機(jī)制.
自然流布局的規(guī)律就是默認(rèn)情況下html頁(yè)面是基于dom出現(xiàn)的順序來(lái)排列的, 也就是我們說(shuō)的堆疊.
我們可以遵循這樣的設(shè)計(jì), 通過(guò)排序的方式改變組件的位置從而實(shí)現(xiàn)自然流布局的頁(yè)面搭建.
那么我們?cè)倩氐缴厦嬲f(shuō)的布局問(wèn)題, 比如說(shuō)要想實(shí)現(xiàn)柵格化布局, 我們只需要定義一個(gè)flex容器, 將組件拖拽到容器里就好了, 這樣也就解決了嵌套的問(wèn)題. 同時(shí)我們還可以設(shè)計(jì)嵌套容器的柵格數(shù), 這樣就可以實(shí)現(xiàn)類似如下的效果:
拖拽排序的庫(kù)我們可以使用:
- sortable
- Vue.Draggable
- react-dnd
還有很多優(yōu)秀的庫(kù), 這里就不一一舉例了.
3. 如何實(shí)現(xiàn)層級(jí)和嵌套
其實(shí)在上面的實(shí)現(xiàn)思路中我們已經(jīng)解決了嵌套的問(wèn)題了, 即提供拖放的容器組件, 利用筆者在上文中介紹的拖放api即可實(shí)現(xiàn). 對(duì)于組件層級(jí)來(lái)說(shuō), 因?yàn)槲覀儾捎玫氖亲匀涣鞑季? 所以我們可以輕松的設(shè)置元素的定位屬性, 比如我們提供一個(gè)定位的設(shè)置:
關(guān)于如何設(shè)計(jì)一個(gè)動(dòng)態(tài)的屬性編輯器, 筆者之前文章中也就詳細(xì)的介紹, 大家可以參考:
- 表單編輯器實(shí)現(xiàn)(FormEditor)
以上就是自然流布局的基本實(shí)現(xiàn)方式, 后續(xù)筆者也會(huì)在github上同步最新的成果.