svelte組件:Svelte自定義彈窗Popup組件|svelte移動端彈框組件


基於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實現自定義彈窗組件的一些分享,希望對大家有所幫助~~💪

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM