popup定位引擎popper.js介绍
https://medium.com/@FezVrasta/popper-js-v1-5e8b3acd888c
https://survivejs.com/blog/popper-interview/
本文譯自popper.js作者的一篇博客
在過去,我為了在web app中更好地定位我的tooltips和popover,我會花幾個小時寫同樣的一段代碼,不斷進(jìn)行微調(diào)。每次我開始一個新的項(xiàng)目,總會根據(jù)不同的環(huán)境對定位有不同的需求。這種繁瑣直到我用emberjs開發(fā)一個大型應(yīng)用時達(dá)到極致,這個項(xiàng)目中由于比較爛的UX設(shè)計決定,幾乎她想在每個元素上都支持hover出現(xiàn)一個popover!
在這個大項(xiàng)目中,我們開始使用bootstrap3的tooltip,并不斷地通過hack代碼來實(shí)現(xiàn)新的功能。
在這個大項(xiàng)目中,一個重要的需求是:不允許將tooltip元素移動到body元素的直接兒子,因?yàn)槿绻@樣的話,我們的代碼就會broken.
幾乎經(jīng)過一年的項(xiàng)目開發(fā)并且維護(hù)我們的定制化實(shí)現(xiàn),我決定使用Tetcher,因?yàn)樗菜品浅?qiáng)大和穩(wěn)定。。不幸的是,玩了幾個小時后,發(fā)現(xiàn)他有一個重要的缺陷或者說限制:他會自動地修改dom節(jié)點(diǎn),將popper移動到body的兒子位置。他會增加一些classes并且增加一些inline style,而這你幾乎無法控制!
于是我又到github上花了很多時間去查找有沒有備選的方案,但是我最終一無所獲。難道是沒有人需要一個類似的library?或者還沒有人能夠比較好的抽象出來并實(shí)現(xiàn)一個可以單獨(dú)發(fā)布的lib?
我決定花一個周末的事件重寫我們自己項(xiàng)目中使用的定位engine,這就是popper.js的又來。
在v0.x時代,popper僅僅是一個js文件,謝了幾個函數(shù),聚焦在保持lib小巧輕量級。我希望popperjs能夠有很好的擴(kuò)展性,所以我決定使用middleware system.主要的想法是:計算元素的位置并且允許middleware來根據(jù)特定的需求來修改這個位置。
比如:一旦我們有了popper的position,并給了一些邊界約束,那么modifier就可以檢查popper是否會overflow并且自動反轉(zhuǎn)位置以確保popper不會被視窗cut off.
在工作了一段時間 后,我發(fā)現(xiàn)我們需要更好的代碼結(jié)構(gòu)以便更好地能夠維護(hù)使用他,這就是v1.0版本發(fā)布的初衷。
為了管理好代碼,我決定切換到es6模塊上來。我引入了rollup作為code bundler并使用babel作為transpiler,我也將自動化測試引擎從一個無頭的chrome setup切換到saucelabs cross-browser test suite上來。
在開發(fā)過程中,我決定各個功能模塊一級modifier/middleware都放到他們自己的文件中,這樣能在Libray中重用。
update流程:
我也重構(gòu)了整個update流程,也就是每次popper需要更新元素的位置時需要調(diào)用的代碼。
這個update process會在每一frame都被調(diào)用,也就是說大概60fps,這樣能夠保證位置修改的流暢連貫。為了實(shí)現(xiàn)這個連貫流暢的目標(biāo),整個update process的代碼必須簡練,并且盡可能地避免直接訪問dom.下面是其工作流程
React, Vuejs等第三方view library的集成
我必須考慮為了支持react或者vuejs需要做些什么,由于這些view library都會直接做dom操作,那么popperjs應(yīng)該做什么設(shè)計的思考呢?v1.0將所有的dom操作都集中到一個modifier中,applyStyle
這將允許使用vuejs,react的用戶簡單地disable掉applyStyle并且替換為vuejs,或者react兼容的函數(shù)。
Popper.js是如何工作的?
popper.js使用一個reference element(通常是一個button或者一個link)和一個popper element(任何你需要position的元素),popper.js找到這兩個元素的common offset parent,計算reference element相對于這個parent的位置,然后產(chǎn)生出一個坐標(biāo)集用于設(shè)置popper元素的position.就這么簡單。
最困難的地方在于必須考慮到一些邊界條件,比如跨瀏覽器的兼容性,box model的能力,必須考慮scrollable element等。簡單的用法:
new Popper(referenceElement, popperElement)
這段代碼將會把popperElement放置到referenceElement的下發(fā)。而且,通過這段代碼,你就可以訪問所有的內(nèi)置功能。
1. 如果referenceElement非常靠近了viewport的底部,那么popperElement將會被定位放置到referenceElement的上面,否則會出現(xiàn)popper部分不可見的問題;
2. 如果這兩個元素位于兩個不同的parents,那么popper.js也將會對這種情況考慮周全,也能很好的定位放置popper元素。
3. 它能夠有效地處理scrollable elements和頁面的resize場景.
Popper.js和其他類似的解決方案有什么不同呢?
主要的不同點(diǎn)在于該庫不需要直接操作dom。這有兩個好處:1.他不需要將popper節(jié)點(diǎn)移動到另外的context中,比如body的兒子,2.這樣很容易將popper.js集成到react,angular,vuejs等view library中。你可以像下面的代碼一樣輕松地將dom manipulation delegate給vuejs:
new Popper(referenceElement, popperElement, {
modifiers: {
applyStyle: { enabled: false },
updateReactData: {
order: 900,
fn(data) {
this.setState({ data });
return data;
}
},
},
});
上面的代碼中,我們關(guān)閉了內(nèi)置的applyStyle modifier,并且定義了我們客制化的modifier,該函數(shù)代理獲取計算出來的popper坐標(biāo)而將坐標(biāo)輸出到react組件中。
既然你具有了關(guān)于popper.js的所有knowledge,那么你就可以對popper element施加任何你想要的樣式。
你可能注意到我的定制modifier返回data對象。這個對象是必須的,因?yàn)槠渌膍odifier可能會在這個custom modifier之后執(zhí)行,它們也需要使用這個data對象。這種鏈條方式的調(diào)用使得popper.js非常易于擴(kuò)展。你可以注入任何定制化的函數(shù)在存續(xù)modifer之前或者之后運(yùn)行,或者關(guān)閉部分modifer,或者修正其他的modifer的行為,而這只需要通過修改這個data對象數(shù)據(jù)就可以了。
總結(jié)
以上是生活随笔為你收集整理的popup定位引擎popper.js介绍的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 茶叶排行榜(中国十大名茶加盟品牌)
- 下一篇: 2020年格力中央空调价格表