本文主要內(nèi)容 拖拽的原理常見拖拽組件庫比較React-DnD快速上手Re-resizable快速上手如何實(shí)現(xiàn)一個(gè)最簡單的拖拽大盤系統(tǒng)最近給我們的后臺系統(tǒng)" />

国产成人精品无码青草_亚洲国产美女精品久久久久∴_欧美人与鲁交大毛片免费_国产果冻豆传媒麻婆精东

15158846557 在線咨詢 在線咨詢
15158846557 在線咨詢
所在位置: 首頁 > 營銷資訊 > 網(wǎng)站運(yùn)營 > React組件可視化拖拽頁面搭建,源碼生成,你有什么想法?

React組件可視化拖拽頁面搭建,源碼生成,你有什么想法?

時(shí)間:2024-01-26 06:45:01 | 來源:網(wǎng)站運(yùn)營

時(shí)間:2024-01-26 06:45:01 來源:網(wǎng)站運(yùn)營

React組件可視化拖拽頁面搭建,源碼生成,你有什么想法?:正好有篇文章可以參考:

本文主要內(nèi)容
  1. 拖拽的原理
  2. 常見拖拽組件庫比較
  3. React-DnD快速上手
  4. Re-resizable快速上手
  5. 如何實(shí)現(xiàn)一個(gè)最簡單的拖拽大盤系統(tǒng)
最近給我們的后臺系統(tǒng)做了一個(gè)所見即所得的大盤編輯器,頗有收獲,寫篇文章做個(gè)全面的回顧

一、基本原理

對一個(gè)DOM元素而言,完整的拖拽流程分為兩部分,即 拖動 + 放置

讓一個(gè)元素支持拖動是一件非常容易做到的事情,我們只需要在對應(yīng)的HTML結(jié)點(diǎn)新增一個(gè)draggable="true"的屬性即可,另外,超鏈接和圖像都是默認(rèn)可拖動的。

真正麻煩的是放置部分,我們需要監(jiān)聽ondragstart、ondragenter、ondragoverondragleave等等各階段發(fā)生在元素上的拖動事件,最后還需要處理ondrop事件完成最終的放置,我們需要做好數(shù)據(jù)的傳遞,可放置區(qū)域的識別、最終位置的處理,頁面的更新等等一系列細(xì)小繁瑣的工作。

所幸的是,已經(jīng)有成熟的庫來幫助我們完善這些細(xì)節(jié)了,讓我們只需要關(guān)注于渲染邏輯即可。

下面列出了常見的React拖拽相關(guān)的庫:

React DnD 是由Redux作者Dan Abramov主導(dǎo)開發(fā),也是非常老牌的React拖拽工具庫,提供了對底層的拖拽的一層封裝。

React-Beautiful-DnD 是由Alassian團(tuán)隊(duì)(沒錯(cuò),就是開發(fā)Jira的團(tuán)隊(duì))貢獻(xiàn)的React拖拽工具庫。相比于React-DnD,提供了更高層級功能的封裝,如動畫、虛擬列表、移動端等功能。也是Github上Star最多的React 拖拽庫

React-Grid-Layout是由一家比特幣交易公司BitMex開源的,可謂柵格布局模式下集成最好的框架庫,支持放大縮小,自動布局,在AWS控制臺與Grafana中已經(jīng)使用了此框架,對初學(xué)者非常友好。

由于這里我并不想把自己的命運(yùn)交給比特幣公司,更想從偏底層來實(shí)現(xiàn)自己的一整套拖拽邏輯,故此選用了React-DnD庫來完成頁面拖動功能的開發(fā)。

二、React-DnD 快速入門

React-dnd中,包含四個(gè)核心概念:backendmonitor,drag,drop。

下面是一個(gè)最簡單最基本的例子:

import { HTML5Backend } from 'react-dnd-html5-backend'import { DndProvider, useDrag, useDrop } from 'react-dnd'function Drag() { const [collectedProps, drag] = useDrag({ item: { values, type: 'KEY' } }) return ( <div ref={drag}>Drag</div> )}function Drop() { const [collectedProps, drop] = useDrop({ accept: 'KEY' }) return ( <div ref={drop}>Drop Area</div> )}export default function Demo() { return ( <DndProvider backend={HTML5Backend}> <Drag /> <Drop /> </DndProvider> )}

1. Backend

此處的backend,可以理解為拖拽背后的實(shí)現(xiàn)的邏輯,此處主要是用來區(qū)分PC端和移動端不同的事件監(jiān)聽和處理方式,如果是運(yùn)行在PC端的,使用react-dnd-html5-backend,否則就使用react-dnd-touch-backend,注意DndProvider一定是在Drag和Drop的最外層使用的。

2. Monitor

monitor一眼看上去其實(shí)并不好理解,但是確實(shí)沒有更貼切的單詞了。monitor是監(jiān)控整個(gè)拖動事件的總狀態(tài)數(shù)據(jù),主要分為sourceMonitor和targetMonitor,分別代表Drag和Drop元素當(dāng)前的狀態(tài)數(shù)據(jù),如偏移距離、是否浮于上層等等。我們在使用useDrag和useDrop的時(shí)候,可以通過對應(yīng)的monitor數(shù)據(jù)進(jìn)行狀態(tài)判定或者預(yù)置切換等等豐富功能。

3. Drag

drag即允許拖動的元素(source),我們通過useDrag生成的ref指向給了某一個(gè)DIV,此DIV便會被設(shè)置draggable=true的屬性,同時(shí)拖動的所有事件都會被我們監(jiān)聽到。使用方法可以參考上面例子。

const [collectedProps, drag] = useDrag({item, canDrag, collect})useDrag返回的數(shù)組一共有三個(gè)元素,我們只說前兩個(gè):

collectedProps: 這其實(shí)是React-DnD一個(gè)很精妙的設(shè)計(jì),組件在拖動的時(shí)候,此變量便代表著需要監(jiān)聽的數(shù)據(jù)drag: 即拖動元素的Ref引用,賦給對應(yīng)的DOM元素即可useDrag的函數(shù)參數(shù)也很多,這里只挑重要的說一下:

item: 必填,即包含的數(shù)據(jù)對象,必須字段type,與drop對象對應(yīng),只有同一個(gè)type值的才能被放置進(jìn)去canDrag: 選填,(monitor) => boolean,表示是否可拖拽,這在區(qū)分編輯與只讀模式非常有用collect: 選填,(monitor) => object,通過此方法返回的值可以從上述的collectedProps中取到, 通過使用monitor判斷狀態(tài),我們可以返回如opacity、hightlighted等屬性用來給拖動元素添加樣式

4. Drop

drop即可以拖動到的元素(target),它的返回?cái)?shù)組有兩個(gè)元素,而且與useDrag返回值作用幾乎一致

const [collectedProps, drop] = useDrop({ accept, hover, drop, collect })其中參數(shù)和返回值如下:

collectedProps: 同上,也是collect函數(shù)返回的objectdrop: 即放置元素的Ref引用,賦給對應(yīng)的DOM元素即可useDrop的參數(shù)也很多,我們也挑重點(diǎn)的說明一下:

accept: 必填,支持字符串或者字符串?dāng)?shù)組,對應(yīng)于drag的type值,同樣的值才可被拖入此元素中hover: 選填,(item, monitor) => void,item即拖動到此drop上drag對象的值,通過用于展示滑上后的預(yù)覽效果drop: 選填,(item, monitor) => void,同上,此事件在鼠標(biāo)放開后觸發(fā)collect: 選填,(monitor) => object,作用同上,也可以用來表達(dá)drag進(jìn)來和離開事件至此所有的react-dnd基本概念已經(jīng)介紹完了,正所謂“九層之臺起于累土,千里之行始于足下”,頁面上的所有交互都是基于這些最基本的功能實(shí)現(xiàn)的,也許你仍然覺得很抽象,不妨參考下官網(wǎng)的Demo其中Sandbox的代碼例子來學(xué)習(xí)一下,擠需體驗(yàn)十番鐘,里造會干我一樣,愛象節(jié)款工具!

三、Re-Resizable與寬高吸附

上面說完了拖拽,下面該說一下拉伸了。

拉伸是可通過在CSS屬性中指定resize來支持拉伸,比如常見的textarea就是默認(rèn)內(nèi)置了此屬性,但是瀏覽器并未像drag一樣提供resize專門的API,故大部分庫都是通過監(jiān)聽mousedown,mousemove,mouseup這種有些hack的方式完成的。

re-resizable 也是React體系下支持拉伸的庫,這個(gè)庫入門非常簡單,只看官方文檔就能很快理解。

我們可以像表單組件一樣給它設(shè)置value(也就是size)和onChange(也就是onRisizeStop)即可完成拉伸的功能,比較麻煩的是enable如果指定了則八個(gè)方向都需指定一遍。

值得一提的是如何去做寬高的輔助吸附,簡單點(diǎn)可以使用grid來設(shè)置步長,如果要做定制化的對齊就麻煩了,這里分享一個(gè)思路,我們可以在onResize或onResizeStop的時(shí)候,通過參數(shù)我們可以獲取偏移位置,此時(shí)可以對偏移位置進(jìn)行計(jì)算后四舍五入,便可保證按比例變化。

如果想做類似Photoshop(不是PS)或者CAD那種橫軸縱軸吸附的,可以參考document.elementFromPoint(x,y)方法,通過不斷加步長迭代的方式應(yīng)該可以找到最近的子元素并獲取對應(yīng)的寬高。

四、如何實(shí)現(xiàn)一個(gè)拖拽系統(tǒng)的最小集的?

我把整個(gè)拖拽系統(tǒng)分成四部分:

  1. 拖拽源容器區(qū)域,2. 拖拽源組件區(qū)域,3. 畫布上的容器區(qū)域,4. 畫布上的組件。
下面的TYPE即表示useDrag中的type值,ACCEPT即表示useDrop中的accept的值。

1. 拖拽源容器區(qū)域

TYPE="Container"
拖拽源容器即所有可供用戶拖拽到畫布上的容器布局,所有的組件應(yīng)當(dāng)被放置到容器內(nèi)進(jìn)行布局上的管理,如果組件能實(shí)現(xiàn)良好的布局管理其實(shí)也可以不需要此容器。

2. 拖拽源組件區(qū)域

TYPE="Widget"
即實(shí)際業(yè)務(wù)上需要的展示組件,這部分是支持二次開發(fā)的,且用了Form-Render 支持以配置項(xiàng)的方式生成組件配置表單,組件只需要關(guān)注業(yè)務(wù)邏輯,配置項(xiàng)會自動注入進(jìn)來。

3. 畫布容器區(qū)域

TYPE="PaintContainer" ACCEPT=["Container", "PaintContainer"]
當(dāng)把拖拽源拖入畫布后,即生成一個(gè)畫布容器區(qū)域,也可以不用一個(gè)新的TYPE,這樣做主要是便于快速區(qū)分是從拖拽源過來的或是畫布上模塊的移動,如果想讓一個(gè)DOM同時(shí)支持Drag & Drop,可以這樣做:

const ref = useRef();const [,drop] = useDrop({});const [,drag] = useDrag({});drop(drag(ref));return <div ref={ref}> Both Can Drag & Drop </div>

4. 畫布組件區(qū)域

TYPE="PaintWidget" ACCEPT=["Widget", "PaintWidget"]
這里也可以用兩個(gè)不同的TYPE來區(qū)分,區(qū)分從拖拽源進(jìn)來的還是從畫布上別的地方拖進(jìn)來的,一個(gè)是把數(shù)據(jù)填充進(jìn)去,一個(gè)是交換兩個(gè)位置的下標(biāo)。

最后說一下數(shù)據(jù)結(jié)構(gòu)

畫布區(qū)域使用一個(gè)JS數(shù)組來維護(hù),數(shù)組元素大致結(jié)構(gòu)如下:

{ uuid: string; // 唯一標(biāo)識區(qū)塊的id width,height... // 定位與尺寸屬性 children: { // 里面的子展示組件 uuid: string; // 唯一標(biāo)識展示組件的id span: number; // 展示組件占寬度 widgetId: string; // 具體是哪一個(gè)展示組件,渲染時(shí)會取組件列表中獲取并渲染 config: object; // 個(gè)性化配置項(xiàng)值 }[] }這里不得不贊美一下React的 Render(data) => View 模式做這種畫布實(shí)在太合適了,每次只要修改了數(shù)據(jù)結(jié)構(gòu),React就會自動根據(jù)數(shù)據(jù)結(jié)構(gòu)渲染出畫布里具體的內(nèi)容,少操了很多心。

五、其他問題

1. 如何做日期數(shù)據(jù)補(bǔ)0?

這普遍發(fā)生在做折線圖的時(shí)候,DB的數(shù)據(jù)并不是每天都有,特別是在畫多條折線圖的時(shí)候:

[ { data: '2020-09-01', type: 'A', count: 5 }, { data: '2020-09-03', type: 'A', count: 15 }, { data: '2020-09-03', type: 'B', count: 10 }, { data: '2020-09-06', type: 'C', count: 20 },]上面的數(shù)據(jù),缺少了9月2日和9月4日,9月5日的數(shù)據(jù),如果不把空缺的時(shí)間填上去,那橫軸間隔就會很奇怪。

并且因?yàn)槭嵌鄺l折線,每個(gè)日期都需要每種type對應(yīng)的數(shù)據(jù),不然會出現(xiàn)折線斷掉的情況。

補(bǔ)0的方法無非三種思路:

  1. 數(shù)據(jù)庫每天定時(shí)更新,插入冗余數(shù)據(jù),這得看業(yè)務(wù)場景和表的作用來定
  2. 創(chuàng)建日期表,每次查詢的時(shí)候做LEFT JOIN,雖然用起來簡單了,但是性能可能會略差
  3. 后端或者前端補(bǔ)0,這里因?yàn)橛胓o寫太麻煩了考慮到減小后端計(jì)算壓力和網(wǎng)絡(luò)傳輸壓力,就放到前端來了
設(shè)查詢的時(shí)間范圍長度為N,返回的記錄為responseData數(shù)組,總種類數(shù)為M,分享一個(gè)O(NM)時(shí)間復(fù)雜度的方法(因?yàn)樽罱K數(shù)組長度就是N*M,所以應(yīng)該還是蠻高效的)

第一步:用dayjs工具生成從查詢起始時(shí)間到終止時(shí)間的時(shí)間序列數(shù)組dateList,元素為日期string

第二步:生成空的結(jié)果數(shù)組resultList,參考Echarts規(guī)范,這個(gè)數(shù)組的格式為{ type: value[] },type就是狀態(tài)值,value的下標(biāo)是日期的下標(biāo),值是count數(shù)據(jù)

第三步:下標(biāo)指針i指向dateList0個(gè)元素,下標(biāo)指針j指向responseData0個(gè)元素

第四步:先不比較,遍歷M所有狀態(tài),給resultList[Enum(M)][i]賦值resultList[Enum(M)][i] || 0

第五步:比較dateList[i]responseData[j]對應(yīng)的日期是否一樣,如果一樣,則跳轉(zhuǎn)到第六步,否則到第七步

第六步:賦值resultList[type][i]responseData[j].count,這里的type是responseData[j].type,然后j++,因?yàn)檫€要在結(jié)果中找尋同一個(gè)日期下其他數(shù)據(jù),接著返回第四步

第七步:說明結(jié)果中不存在此日期下數(shù)據(jù),因?yàn)榈谒牟街幸呀?jīng)做了默認(rèn)值賦值,所以直接i++,然后返回第四步

第八步:當(dāng)i超過dateList的長度后,終止循環(huán)即可

六、還缺點(diǎn)啥

這畢竟是兩個(gè)星期做出來的東西,還有很多實(shí)現(xiàn)并不完善的地方:

1. 拖拽交互

拖拽交互如果想要增加動效,預(yù)覽等等效果,需要增加很多細(xì)節(jié)上的判斷

2.布局

目前強(qiáng)制行優(yōu)先布局,強(qiáng)制四平八整,可能需要支持列方向上的布局

3.組件庫建設(shè)

CMS系統(tǒng)中核心的就是模板+組件庫。目前組件沒有版本的概念,硬編碼到代碼中,需要拆分出來異步引用,另外也需要做好對所在容器寬高做自適應(yīng)。




更多內(nèi)容:



關(guān)鍵詞:

74
73
25
news

版權(quán)所有? 億企邦 1997-2025 保留一切法律許可權(quán)利。

為了最佳展示效果,本站不支持IE9及以下版本的瀏覽器,建議您使用谷歌Chrome瀏覽器。 點(diǎn)擊下載Chrome瀏覽器
關(guān)閉