flouting?ui定位組件完美替代ant?deisgn使用詳解
前言
因?yàn)橐獙憆eact定位組件(這不是標(biāo)題黨,就是完爆ant deisgn的定位組件,你應(yīng)該看到一半就會(huì)同意我的觀點(diǎn)),如下圖:
紅框部分是用絕對定位放在按鈕上面的,你們B端用的主流組件庫都是這樣實(shí)現(xiàn)的,它是很多組件的基礎(chǔ)組件,比如下圖:
下拉框組件
select組件
還有什么DataPicker,TreeSelect,Dropdown組件等等的下拉框都是以定位組件為基礎(chǔ)的。
這個(gè)組件實(shí)現(xiàn)的復(fù)雜度在哪
上面提到,這不過就是一個(gè)絕對定位嘛(我們假設(shè)紅框部分的的dom絕對定位是相較于body元素),我們拿最簡單的情況來看,如下圖,如何把紅框部分渲染到按鈕”更多“的下方呢?
我們可以計(jì)算更多按鈕的getBoundingRect(),返回
const reference = { top: xx, // 按鈕距離瀏覽器頂部的距離 left: xx, // 按鈕距離瀏覽器左邊的距離 width: xx, // 按鈕的寬:沒有padding height:xx,// 按鈕的高:沒有padding ...等等其他屬性 }
所以紅框部分左上角的坐標(biāo)就輕易的計(jì)算出來了,上面的數(shù)據(jù)在reference對象上,所以借助reference的定位,我們計(jì)算紅框部分的下拉框的定位是在哪
{ position: 'absolute', top: reference.top + window.pageYOffset // 豎直方向滾動(dòng)距離 + reference.height left: reference.left + window.pageXOffset // 橫向滾動(dòng)距離 }
為啥是上面這么計(jì)算呢,假如沒有滾動(dòng)條滾動(dòng),那么紅框部分的絕對定位的top,是不是等于按鈕的距離瀏覽器頂部的高度 + 本身的高度,這個(gè)沒問題吧?
然后,如果滾動(dòng)條滾動(dòng)了的話,是不是要在上面top的基礎(chǔ)上加上這段距離,就是紅框部分在文檔流絕對定位的top。
好了,到此為止,就是最基本的定位組件的邏輯了,我們接下來看復(fù)雜點(diǎn)!
復(fù)雜度1
還是拿上面的圖的紅色部分下拉框來說,下拉框一般是在下面,但是我可以定位到上邊吧?左邊,右邊也沒啥吧,再過分點(diǎn),右上,左下?定位組件要處理對吧
復(fù)雜度2
假設(shè),我們定位在下面,我想向左偏移8px,向下偏移3px咋辦,你是不是應(yīng)該有暴露一個(gè)口子
復(fù)雜度3
假設(shè)我定位在下面,那么我一直滾,馬上就要滾動(dòng)到看不見下拉框了,如下圖
此時(shí)我想讓定位在上面,能不能自動(dòng)幫我處理?如下圖:
復(fù)雜度4
是不是還有可能超出瀏覽器視口了,如下圖:
我們想自動(dòng)處理,遇到超出就自動(dòng)變?yōu)橄路綐幼樱?/p>
復(fù)雜度5
此時(shí)我定位了一次,但是有可能滾動(dòng)容器不是window,是另一個(gè)div,這個(gè)計(jì)算咋辦?還有,是不是我滾動(dòng)的時(shí)候,我要監(jiān)聽滾動(dòng)事件,還要監(jiān)聽瀏覽器resize事件,因?yàn)槲叶ㄎ坏闹悼赡軙?huì)變?為啥呢,我們上面復(fù)雜度3是不是自動(dòng)幫我們在滾動(dòng)的時(shí)候調(diào)整位置
所以你不監(jiān)聽滾動(dòng)事件你咋知道要調(diào)整位置了?
還有很多細(xì)枝末節(jié),比如瀏覽器兼容性等等。。。。
國內(nèi)組件庫怎么實(shí)現(xiàn)這個(gè)功能
目前阿里的ant design和字節(jié)的arco design都是自己實(shí)現(xiàn)的,我們拿arco來看(ant內(nèi)部叫rc-trriger組件,arco叫trriger組件),面向過程的代碼,看的我頭皮發(fā)麻。。。我截個(gè)圖:
上面的代碼屬于把我們提到的復(fù)雜度全部揉在了一起。
flouting-ui為啥代碼質(zhì)量比ant高
它是以中間件的形式去處理的,思路是什么呢?它假設(shè)最開始有一個(gè) computePosition函數(shù),我們假設(shè)上面提到的復(fù)雜度都沒有,也就是不考慮的前提下,我們怎么計(jì)算定位組件的坐標(biāo),也就是我們最前面的圖里說的,紅色框部分絕對定位的的的top值和left值:
API如下:
computePosition(要掛載的dom節(jié)點(diǎn),下拉框組件,參數(shù)...)
然后我們剛才提到的復(fù)雜度,它分別用中間件的形式去處理,比如復(fù)雜度2,是想定位之后還有點(diǎn)偏移量,flouting-ui咋做的呢
import {computePosition, offset} from '@floating-ui/dom'; // referenceEl: 要掛載的dom節(jié)點(diǎn) // floatingEl:下拉框組件(或者說想要掛載到上面referenceEl的dom元素) computePosition(referenceEl, floatingEl, { middleware: [offset(10)], });
如上,offset就是一個(gè)中間件,offset(10),就是向左偏移10px
好了,如果想處理復(fù)雜度3呢,我們用另一個(gè)中間件
import {computePosition, flip} from '@floating-ui/dom'; computePosition(referenceEl, floatingEl, { middleware: [flip()], });
這樣就自動(dòng)處理了,是不是很簡單啊
其實(shí)所有這些復(fù)雜度的解決方案,在flouting-ui里都是以中間件的形式去處理的,還可以傳多個(gè)中間件解決多個(gè)問題。
中間件的形式好在哪
那么我們就可以自定義很多中間件了,也就是你的組件不僅僅提供了很多功能,解決了很多常用的問題,你還允許用戶寫代碼去拓展,試問,現(xiàn)在哪個(gè)組件庫的代碼是這么寫的?沒有吧?
代碼中間件原理
我們先看看flouting-ui的computePosition API是怎么實(shí)現(xiàn)的,它是flouting-ui的核心方法,是串聯(lián)所有中間件的基礎(chǔ)。
下一篇寫完整的源碼(很晦澀,估計(jì)也沒幾個(gè)人看,所以這期就不寫了),理解起來說實(shí)話,你不熟悉原生dom的話有點(diǎn)困難,比如說為啥這個(gè)庫要用window.pageYoffset而不是document.body.scrollTop去獲取瀏覽器html元素的滾動(dòng)距離,因?yàn)閐ocument.body.scrollTop固定為0,取不到。。。
核心思路講解:我們還是拿下圖做類比
let {x, y} = 求出紅色框里的下拉框絕對定位的x坐標(biāo)和y坐標(biāo) // 記錄原始placement let statefulPlacement = placement; // 所有中間件導(dǎo)出的值都掛載到下面的對象上 let middlewareData: MiddlewareData = {}; // 數(shù)據(jù)經(jīng)過middleware的處理 // middleware是一個(gè)數(shù)組,存放所有中間件,就是我們上面說的處理每一個(gè)復(fù)雜度的對象 for (let i = 0; i < middleware.length; i++) { // name是中間件的名字,fn是處理復(fù)雜度的邏輯 const {name, fn} = middleware[i]; // 通過把最前面計(jì)算的x,y經(jīng)過fn的處理,得到了新的x,y的值 // data是指返回的數(shù)據(jù),想讓后面的中間件也能訪問到的數(shù)據(jù) /** * 每個(gè)middleware需要返回 * x 新的x坐標(biāo) * y 新的y坐標(biāo) * data * reset */ const { x: nextX, y: nextY, data, reset, } = await fn({ /** * 每個(gè)middleware收到的參數(shù) * x 目前的x坐標(biāo) * y 目前的y坐標(biāo) * initialPlacement 最初傳入的placement * placement * middlewareData middleware返回的額外數(shù)據(jù) */ x, y, initialPlacement: placement, placement: statefulPlacement, strategy, middlewareData, rects, platform, elements: {reference, floating}, }); x = nextX ?? x; y = nextY ?? y; // 每次處理后的數(shù)據(jù)想要讓后面的中間件訪問,就需要掛載到middlewareData對象 // 這個(gè)對象非常好啊,用name隔離了作用域 middlewareData = { ...middlewareData, [name]: { ...middlewareData[name], ...data, }, }; rest的處理邏輯。。。省略,不是很重要
最后return出被處理完的x,y坐標(biāo),或者自動(dòng)幫我們監(jiān)聽滾動(dòng)事件和resize事件,然后拿著x,y就可以賦在css的絕對定位的top和left上,實(shí)現(xiàn)了定位。
每次處理后的數(shù)據(jù)想要讓后面的中間件訪問,就需要掛載到middlewareData對象,這個(gè)對象非常好啊,用name隔離了作用域,這就是比koa這個(gè)框架處理的高明之處,koa里的ctx對象就像一個(gè)垃圾桶,什么屬性都往上面掛載,掛載太多了,你也不知道是哪個(gè)中間件掛載的
所以flouting-ui的處理思路給我打開了新的思路!nice?。?!
中間件如何寫
源碼再開一篇文章寫,這里看看就好,不用過多去關(guān)系代碼
export const offset = (value: Options = 0): Middleware => ({ name: 'offset', // 中間件名字 options: value, // 傳給中間件的值 async fn(middlewareArguments) { // 中間件處理函數(shù) const {x, y} = middlewareArguments; const diffCoords = await convertValueToCoords(middlewareArguments, value); return { x: x + diffCoords.x, y: y + diffCoords.y, data: diffCoords, }; }, });
本文結(jié)束,所以如果市面上的組件庫的每個(gè)組件都是這個(gè)形式暴露給用戶,就是提供插件式的自定義的中間件,那么整個(gè)組件庫的拓展性可以說碾壓市面上國內(nèi)所有的react的組件庫了
以上就是flouting ui定位組件完美替代ant deisgn使用詳解的詳細(xì)內(nèi)容,更多關(guān)于flouting ui定位組件的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
ReactNative錯(cuò)誤采集原理在Android中實(shí)現(xiàn)詳解
這篇文章主要為大家介紹了ReactNative錯(cuò)誤采集原理在Android中實(shí)現(xiàn)詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-02-02React通過redux-persist持久化數(shù)據(jù)存儲(chǔ)的方法示例
這篇文章主要介紹了React通過redux-persist持久化數(shù)據(jù)存儲(chǔ)的方法示例,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2019-02-02react render的原理及觸發(fā)時(shí)機(jī)說明
這篇文章主要介紹了react render的原理及觸發(fā)時(shí)機(jī)說明,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-02-02React?實(shí)現(xiàn)具備吸頂和吸底功能組件實(shí)例
這篇文章主要為大家介紹了React?實(shí)現(xiàn)具備吸頂和吸底功能組件實(shí)例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-02-02React組件設(shè)計(jì)模式之組合組件應(yīng)用實(shí)例分析
這篇文章主要介紹了React組件設(shè)計(jì)模式之組合組件,結(jié)合實(shí)例形式分析了React組件設(shè)計(jì)模式中組合組件相關(guān)概念、原理、應(yīng)用場景與操作注意事項(xiàng),需要的朋友可以參考下2020-04-04解決React報(bào)錯(cuò)Expected an assignment or funct
這篇文章主要為大家介紹了React報(bào)錯(cuò)Expected an assignment or function call and instead saw an expression解決方案詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-12-12