- 資訊首頁(yè) > 開(kāi)發(fā)技術(shù) > web開(kāi)發(fā) > JavaScript >
- React實(shí)現動(dòng)效彈窗組件
我們在寫(xiě)一些 UI 組件時(shí),若不考慮動(dòng)效,就很容易實(shí)現,主要就是有無(wú)的切換(類(lèi)似于 Vue 中的 v-if 屬性)或者可見(jiàn)性的切換(類(lèi)似于 Vue 中的 v-show 屬性)。
在 React 中,可以這樣來(lái)實(shí)現:
interface ModalProps { open: boolean; onClose?: () => void; children?: any; } const Modal = ({open. onClose, children}: ModalProps) => { if (!open) { return null; } return createPortal(<div> <div classname="modal-content">{children}</div> <div classname="modal-close-btn" onclick="{onClose}">x</div> </div>, document.body); };
使用方式:
const App = () => { const [open, setOpen] = useState(false); return ( <div classname="app"> <button onclick="{()" ==""> setOpen(true)}>show modal</button> <modal open="{open}" onclose="{()" ==""> setOpen(false)}> modal content </modal> </div> ); };
我們在這里就是使用open
屬性來(lái)控制展示還是不展示,但完全沒(méi)有漸變的效果。
若我們想實(shí)現 fade, zoom 等動(dòng)畫(huà)效果,還需要對此進(jìn)行改造。
很多同學(xué)在自己實(shí)現動(dòng)效時(shí),經(jīng)常是展示的時(shí)候有動(dòng)效,關(guān)閉的時(shí)候沒(méi)有動(dòng)效。都是動(dòng)效的時(shí)機沒(méi)有控制好。這里我們先自己來(lái)實(shí)現一下動(dòng)效的流轉。
剛開(kāi)始我實(shí)現的時(shí)候,動(dòng)效只有開(kāi)始狀態(tài)和結束狀態(tài),需要很多的變量和邏輯來(lái)控制這個(gè)動(dòng)效。
后來(lái)我參考了react-transition-group
組件的實(shí)現,他是將動(dòng)效拆分成了幾個(gè)部分,每個(gè)部分分別進(jìn)行控制。
動(dòng)效過(guò)程在enter-active
和exit-active
的過(guò)程中。
我們再通過(guò)一個(gè)變量 active 來(lái)控制是關(guān)閉動(dòng)效是否已執行關(guān)閉,參數 open 只控制是執行展開(kāi)動(dòng)效還是關(guān)閉動(dòng)效。
當 open 和 active 都為 false 時(shí),才銷(xiāo)毀彈窗。
const Modal = ({ open, children, onClose }) => { const [active, setActive] = useState(false); // 彈窗的存在周期 if (!open && !active) { return null; } return ReactDOM.createPortal( <div classname="modal"> <div classname="modal-content">{children}</div> <div classname="modal-close-btn" onclick="{onClose}"> x </div> </div>, document.body, ); };
這里我們接著(zhù)添加動(dòng)效過(guò)程的變化:
const [aniClassName, setAniClassName] = useState(''); // 動(dòng)效的class // transition執行完畢的監聽(tīng)函數 const onTransitionEnd = () => { // 當open為rue時(shí),則結束狀態(tài)為'enter-done' // 當open未false時(shí),則結束狀態(tài)為'exit-done' setAniClassName(open ? 'enter-done' : 'exit-done'); // 若open為false,則動(dòng)畫(huà)結束時(shí),彈窗的生命周期結束 if (!open) { setActive(false); } }; useEffect(() => { if (open) { setActive(true); setAniClassName('enter'); // setTimeout用來(lái)切換class,讓transition動(dòng)起來(lái) setTimeout(() => { setAniClassName('enter-active'); }); } else { setAniClassName('exit'); setTimeout(() => { setAniClassName('exit-active'); }); } }, [open]);
Modal 組件完整的代碼如下:
const Modal = ({ open, children, onClose }) => { const [active, setActive] = useState(false); // 彈窗的存在周期 const [aniClassName, setAniClassName] = useState(''); // 動(dòng)效的class const onTransitionEnd = () => { setAniClassName(open ? 'enter-done' : 'exit-done'); if (!open) { setActive(false); } }; useEffect(() => { if (open) { setActive(true); setAniClassName('enter'); setTimeout(() => { setAniClassName('enter-active'); }); } else { setAniClassName('exit'); setTimeout(() => { setAniClassName('exit-active'); }); } }, [open]); if (!open && !active) { return null; } return ReactDOM.createPortal( <div classname="{'modal" '="" +="" aniclassname}="" ontransitionend="{onTransitionEnd}"> <div classname="modal-content">{children}</div> <div classname="modal-close-btn" onclick="{onClose}"> x </div> </div>, document.body, ); };
動(dòng)效的流轉過(guò)程已經(jīng)實(shí)現了,樣式也要一起寫(xiě)上。比如我們要實(shí)現漸隱漸現的 fade 效果:
.enter { opacity: 0; } .enter-active { transition: opacity 200ms ease-in-out; opacity: 1; } .enter-done { opacity: 1; } .exit { opacity: 1; } .exit-active { opacity: 0; transition: opacity 200ms ease-in-out; } .exit-done { opacity: 0; }
如果是要實(shí)現放大縮小的 zoom 效果,修改這幾個(gè) class 就行。
一個(gè)帶有動(dòng)效的彈窗就已經(jīng)實(shí)現了。
使用方式:
const App = () => { const [open, setOpen] = useState(false); return ( <div classname="app"> <button onclick="{()" ==""> setOpen(true)}>show modal</button> <modal open="{open}" onclose="{()" ==""> setOpen(false)}> modal content </modal> </div> ); };
點(diǎn)擊鏈接查看效果。
類(lèi)似地,還有 Toast 之類(lèi)的,也可以這樣實(shí)現。
我們在實(shí)現動(dòng)效的思路上借鑒了 react-transition-group 中的組件。CSSTransition
已經(jīng)幫我封裝好了動(dòng)效展開(kāi)和關(guān)閉的過(guò)程,我們在實(shí)現彈窗時(shí),可以直接使用該組件。
這里有一個(gè)重要的屬性:,表示在動(dòng)效結束后,卸載該組件。
const Modal = ({ open, onClose }) => { // http://reactcommunity.org/react-transition-group/css-transition/ // in屬性為true/false,true為展開(kāi)動(dòng)效,false為關(guān)閉動(dòng)效 return createPortal( <csstransition in="{open}" timeout="{200}" unmountonexit=""> <div classname="modal"> <div classname="modal-content">{children}</div> <div classname="modal-close-btn" onclick="{onClose}"> x </div> </div> </csstransition>, document.body, ); };
在使用 CSSTransition 組件后,Modal 的動(dòng)效就方便多了。
至此已把待動(dòng)效的 React Modal 組件實(shí)現出來(lái)了。雖然 React 中沒(méi)有類(lèi)似 Vue 官方定義的<transition>
標簽,不過(guò)我們可以自己或者借助第三方組件來(lái)實(shí)現。
以上就是React實(shí)現動(dòng)效彈窗組件的詳細內容,更多關(guān)于React彈窗組件的資料請關(guān)注腳本之家其它相關(guān)文章!
免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng )、來(lái)自本網(wǎng)站內容采集于網(wǎng)絡(luò )互聯(lián)網(wǎng)轉載等其它媒體和分享為主,內容觀(guān)點(diǎn)不代表本網(wǎng)站立場(chǎng),如侵犯了原作者的版權,請告知一經(jīng)查實(shí),將立刻刪除涉嫌侵權內容,聯(lián)系我們QQ:712375056,同時(shí)歡迎投稿傳遞力量。
Copyright ? 2009-2022 56dr.com. All Rights Reserved. 特網(wǎng)科技 特網(wǎng)云 版權所有 特網(wǎng)科技 粵ICP備16109289號
域名注冊服務(wù)機構:阿里云計算有限公司(萬(wàn)網(wǎng)) 域名服務(wù)機構:煙臺帝思普網(wǎng)絡(luò )科技有限公司(DNSPod) CDN服務(wù):阿里云計算有限公司 百度云 中國互聯(lián)網(wǎng)舉報中心 增值電信業(yè)務(wù)經(jīng)營(yíng)許可證B2
建議您使用Chrome、Firefox、Edge、IE10及以上版本和360等主流瀏覽器瀏覽本網(wǎng)站