基於Svelte3.x自定義多功能svPopup彈出框元件(元件式+函式式)
前幾天有分享一個svelte自定義tabbar+navbar元件,今天繼續帶來svelte自定義彈窗元件。
svPopup 一款基於 Svelte.js 開發的手機端彈框元件。彙集了msg、info、toast、alert、dialog、actionsheet等多種型別彈窗。支援 25+ 引數自定義搭配組合、元件式+函式式兩種呼叫方式。
由於svelte框架比較新,一些相關的專案案例及自定義元件例子比較少,只能看官方語法文件,並結合之前開發的一些vue3彈窗外掛,最後實現瞭如上圖所示的svelte自定義彈框。
◆ 引入元件
在需要使用彈窗功能的頁面引入Popup元件。
import Popup, {svPopup} from '$lib/Popup'
其中 Popup 是元件式呼叫, svPopup 是函式式呼叫。
- 元件式寫法
<Popup bind:open={isVisibleDialog} xclose xposition="top" title="標題資訊" content="這裡是內容資訊" btns={[ {text: '確認', style: 'color:#f60;', click: () => isVisibleDialog=false}, ]} on:open={handleOpen} on:close={handleClose} > <svelte:fragment slot="content"><h3>自定義插槽顯示插槽內容!!!</h3></svelte:fragment> </Popup>
- 函式式寫法
let el = svPopup({ title: '標題資訊', content: '<p style='color:#df6a16;'>這裡是內容資訊</p>', xclose: true, xposition: 'top', shadeClose: false, btns: [ {text: '取消', click: () => { el.$set({open: false}) }}, {text: '確認', style: 'color:#f90;', click: () => handleOK}, ], onOpen: () => {}, onClose: () => {} })
一些簡單的彈窗效果可以使用函式式呼叫,一些複雜的互動功能可以使用元件式自定義slot來實現功能。
<!-- msg提示 --> <Popup bind:open={showMsg} anim="fadeIn" content="msg提示框測試(3s後視窗關閉)" shadeClose="false" time="3" /> <!-- 自定義多按鈕 --> <Popup bind:open={showMulityBtns} anim="fadeIn" title="<b style='color:red;'>溫馨提示</b>" zIndex="6666" content="<div style='padding:10px 35px;'>是否檢查軟體更新並下載最新的更新?通過行動網路下載可能產生額外的費用。如果可能,通過WLAN網路下載。</div>" btns={[ {text: '稍後提示', style: 'color:#2196f3;', click: () => null}, {text: '取消', style: 'color:#a9a9a9;', click: () => showMulityBtns=false}, {text: '立即更新', style: 'color:#00e0a1;', click: handleInfo}, ]} />
<!-- 底部對話方塊 --> <Popup bind:open={showFooter} anim="footer" type="footer" shadeClose="false" zIndex="1001" content="確定刪除該條資料嗎?刪除後可在7天之內恢復資料,超過7天后資料就無法恢復啦!" btns={[ {text: '恢復', style: 'color:#00e0a1;', click: handleInfo}, {text: '刪除', style: 'color:#ee0a24;', click: () => null}, {text: '取消', style: 'color:#a9a9a9;', click: () => showFooter=false}, ]} /> <!-- ActionSheet底部彈出式選單 --> <Popup bind:open={showActionSheet} anim="footer" type="actionsheet" zIndex="2020" content="彈窗內容,告知當前狀態、資訊和解決方法,描述文字儘量控制在三行內" btns={[ {text: '拍照', style: 'color:#09f;', disabled: true, click: handleInfo}, {text: '從手機相簿選擇', style: 'color:#00e0a1;', click: handleInfo}, {text: '儲存圖片', style: 'color:#e63d23;', click: () => null}, {text: '取消', click: () => showActionSheet=false}, ]} />
<!-- Ios樣式 --> <Popup bind:open={showIos1} type="ios" shadeClose="false" title="標題內容" zIndex="1990" content="彈窗內容,告知當前狀態、資訊和解決方法,描述文字儘量控制在三行內" btns={[ {text: '知道了', click: () => showIos1=false}, {text: '確定', style: 'color:#00e0a1;', click: handleInfo}, ]} > </Popup> <!-- Android樣式 --> <Popup bind:open={showAndroid1} type="android" shadeClose="false" xclose title="標題內容" zIndex="2000" content="彈窗內容,告知當前狀態、資訊和解決方法,描述文字儘量控制在三行內" btns={[ {text: '知道了', click: () => showAndroid1=false}, {text: '確定', style: 'color:#00e0a1;', click: handleInfo}, ]} > </Popup>
function handleInfo(e) { console.log(e) console.log('通過函式方式呼叫彈窗...') let el = svPopup({ title: '標題', content: `<div style="padding:20px;"> <p>函式式呼叫:<em style="color:#999;">svPopup({...})</em></p> </div>`, btns: [ { text: '取消', click: () => { // 關閉彈窗 el.$set({open: false}) } }, { text: '確認', style: 'color:#09f;', click: () => { svPopup({ type: 'toast', icon: 'loading', content: '載入中...', opacity: .2, time: 2 }) } }, ] }) }
◆ Svelte彈窗編碼實現
- 支援如下引數自定義配置
<script> // 是否開啟彈窗bind:open={showDialog} export let open = false // 彈窗識別符號 // export let id = 'svpopup-' + Math.random().toString(32) export let id = undefined // 標題 export let title = '' // 內容 export let content = '' // 彈窗型別 export let type = '' // 自定義彈窗樣式 export let popupStyle = undefined // toast圖示 export let icon = '' // 是否顯示遮罩層 export let shade = true // 點選遮罩層是否關閉 export let shadeClose = true // 遮罩層透明度 export let opacity = '' // 是否顯示圓角 export let round = false // 是否顯示關閉圖示 export let xclose = false // 關閉圖示位置 export let xposition = 'right' // 關閉圖示顏色 export let xcolor = '#333' // 彈窗動畫 export let anim = 'scaleIn' // 彈窗位置 export let position = '' // 長按/右鍵彈窗 export let follow = null // 彈窗自動關閉時間 export let time = 0 // 彈窗層級 export let zIndex = 202203 // 彈窗按鈕組 export let btns = null /* export let btns = [ { text: '取消', style: 'color:#aaa', disabled: true, click: null }, { text: '確定', style: 'color:#f90', click: null } ] */ // 函式式開啟|關閉回撥 export let onOpen = undefined export let onClose = undefined // 接收函式式移除指令 export let remove = undefined // ... </script>
- 彈窗模板語法
<div class="sv__popup" class:opened class:sv__popup-closed={closeCls} id={id} style="z-index: {zIndex}" bind:this={el}> {#if bool(shade)}<div class="vui__overlay" on:click={shadeClicked} style:opacity></div>{/if} <div class="vui__wrap"> <div class="vui__wrap-section"> <div class="vui__wrap-child {type&&'popupui__'+type} anim-{anim} {position}" class:round style="{popupStyle}"> {#if title}<div class="vui__wrap-tit">{@html title}</div>{/if} {#if icon&&type=='toast'}<div class="vui__toast-icon">{@html toastIcon[icon]}</div>{/if} {#if $$slots.content} <div class="vui__wrap-cnt"><slot name="content" /></div> {:else} {#if content}<div class="vui__wrap-cnt">{@html content}</div>{/if} {/if} <slot /> {#if btns} <div class="vui__wrap-btns"> {#each btns as btn,index} <span class="btn"style="{btn.style}" on:click={e => btnClicked(e, index)}>{@html btn.text}</span> {/each} </div> {/if} {#if xclose}<span class="vui__xclose {xposition}" style="color: {xcolor}" on:click={hide}></span>{/if} </div> </div> </div> </div>
/** * @Desc svelte自定義多功能彈框元件 * @Time andy by 2022/3/15 * @About Q:282310962 wx:xy190310 */ <script> // ... import { onMount, afterUpdate, createEventDispatcher, tick } from 'svelte' const dispatch = createEventDispatcher() let opened = false let closeCls = undefined let toastIcon = { loading: '', success: '', fail: '', } const bool = (boolean) => JSON.parse(boolean) ? true : false onMount(() => { console.log('監聽彈窗開啟...') return () => { console.log('監聽彈窗關閉...') } }) afterUpdate(() => { // console.log('監聽彈窗更新...') /* if(opened) { if(!open) { opened = false dispatch('close') } }else if(open) { opened = true dispatch('open') } */ }) $: if(open) { show() }else { hide() } /** * 開啟彈窗 */ async function show() { if(opened) return opened = true dispatch('open') typeof onOpen == 'function' && onOpen() zIndex = getZIndex() + 1 // 倒數計時關閉 if(time) { index++ if(timer[index] != null) clearTimeout(timer[index]) timer[index] = setTimeout(() => { hide() }, parseInt(time)*1000) } // 長按|右鍵選單 if(follow) { // ... } } /** * 關閉彈窗 */ function hide() { if(!opened) return closeCls = true setTimeout(() => { opened = false closeCls = false open = false // ... }, 200) } // 點選遮罩層 function shadeClicked() { if(bool(shadeClose)) { hide() } } // ...// 臨界座標點 function getPos(x, y, ow, oh, winW, winH) { let l = (x + ow) > winW ? x - ow : x let t = (y + oh) > winH ? y - oh : y return [l, t] } </script>
Svelte官網有介紹,可以通過 new Component 來實現掛載元件到body上。
const component = new Component(options)
import App from './App.svelte'; const app = new App({ target: document.body, props: { // assuming App.svelte contains something like // `export let answer`: answer: 42 } });
https://svelte.dev/docs#run-time-client-side-component-api
import Popup from './Popup.svelte' let uuid = function() { return 'svpopup-' + Math.floor(Math.random() * 10000) } export function svPopup(options = {}) { options.id = uuid() const mountNode = document.createElement('div') document.body.appendChild(mountNode) const app = new Popup({ target: mountNode, props: { ...options, open: true, // 傳入函式移除指令 remove() { document.body.removeChild(mountNode) } } }) return app } export default Popup
通過如上寫法,就可以匯出一個 Popup 元件及 svPopup 函式呼叫。
OK,以上就是svelte實現自定義彈窗元件的一些分享,希望對大家有所幫助~~?