svelte组件:svelte3自定义桌面PC端对话框组件svelte-layer

2022年5月19日13:08:47

基于Svelte3.x开发pc网页版自定义弹窗组件svelteLayer

svelte-layer:基于svelte.js轻量级多功能pc桌面端对话框组件。支持多种弹窗类型、30+参数随意组合配置,整合了拖拽/四周缩放/最大化/记忆弹窗位置/全屏/自定义层级等功能。

svelte组件:svelte3自定义桌面PC端对话框组件svelte-layer

svelteLayer功能效果上有些类似layer.js插件。

◆ 快速引入

在需要使用组件功能的页面,引入组件。

import Layer, {svLayer} from '$lib/Layer'

svelte组件:svelte3自定义桌面PC端对话框组件svelte-layer

svelteLayer支持标签式+函数式两种调用方式。

  • 标签式调用
<!-- 询问框--><Layerbind:open={showConfirm}shadeClose="false" title="警告信息" xclose zIndex="2001" lockScroll={false}resize dragOut
    content="<div style='color:#00e0a1;padding:20px 40px;'>这里是确认框提示信息</div>"
    btns={[{text: '取消', click: ()=> showConfirm=false},
        {text: '确定', style: 'color:#e63d23;', click: handleInfo},
    ]}
/>
  • 函数式调用
function handleInfo(e) {
    let el= svLayer({
        title:'标题',
        content: `<div style="padding:20px;">
            <p>函数式调用:<em style="color:#999;">svLayer({...})</em></p>
        </div>`,
        resize:true,
        btns: [
            {
                text:'取消',
                click: ()=> {// 关闭弹窗
                    el.$set({open:false})
                }
            },
            {
                text:'确认',
                style:'color:#09f;',
                click: ()=> {
                    svLayer({
                        type:'toast',
                        icon:'loading',
                        content:'加载中...',
                        opacity: .2,
                        time:2
                    })
                }
            },
        ]
    })
}

svelte组件:svelte3自定义桌面PC端对话框组件svelte-layer

支持标签式和函数式混合搭配调用,还支持如上图动态加载外部组件。

svelte组件:svelte3自定义桌面PC端对话框组件svelte-layer

svelte组件:svelte3自定义桌面PC端对话框组件svelte-layer

svelte组件:svelte3自定义桌面PC端对话框组件svelte-layer

svelte组件:svelte3自定义桌面PC端对话框组件svelte-layer

svelte组件:svelte3自定义桌面PC端对话框组件svelte-layer

svelte组件:svelte3自定义桌面PC端对话框组件svelte-layer

◆ 参数配置

svelte-layer默认支持如下参数自定义配置。

<script context="module">
    let index= 0// 用于控制倒计时临时索引
    let lockNum = 0// 用于控制锁定屏幕临时索引
</script>

<script>// 是否打开弹窗bind:open={showDialog}
    export let open =false// 弹窗标识
    export let id = undefined// 标题
    export let title = ''// 内容
    export let content = ''// 弹窗类型
    export let type = ''// 自定义样式
    export let layerStyle = undefined// 自定义类名
    export let customClass = ''// toast图标
    export let icon = ''// 是否显示遮罩层
    export let shade =true// 点击遮罩层关闭
    export let shadeClose =true// 锁定屏幕
    export let lockScroll =true// 遮罩层透明度
    export let opacity = ''// 是否显示关闭图标
    export let xclose =false// 关闭图标位置
    export let xposition = 'right'// 关闭图标颜色
    export let xcolor = '#000'// 弹窗动画
    export let anim = 'scaleIn'// 弹出位置(auto | ['100px','50px'] | t | r | b | l | lt | rt | lb | rb)
    export let position = 'auto'// 抽屉弹窗
    export let drawer = ''// 右键弹窗定位
    export let follow =null// 弹窗自动关闭时间
    export let time = 0// 弹窗层级
    export let zIndex = 202204// 置顶弹窗
    export let topmost =false// 弹窗大小
    export let area = 'auto'// 弹窗最大宽度
    export let maxWidth = 375// 弹窗是否最大化
    export let maximize =false// 弹窗是否全屏
    export let fullscreen =false// 是否固定
    export let fixed =true// 是否拖拽
    export let drag = '.vlayer__wrap-tit'// 是否拖拽屏幕外
    export let dragOut =false// 限制拖拽方向 vertical|horizontal
    export let dragDir = ''// 拖拽结束回调 {width: 120, height: 120, x: 100, y: 100}
    export let dragEnd = undefined// 是否缩放
    export let resize =false// 弹窗按钮事件
    export let btns =null/*export let btns = [
        {text: '取消', style: 'color:red', disabled: true, click: null},
        {text: '确定', style: 'color:blue', click: null}
    ]*/// 函数式打开|关闭回调
    export let onOpen = undefined
    export let onClose= undefined
    export let beforeClose= undefined// 接收函数移除指令
    export let remove = undefined

    import { onMount, afterUpdate, createEventDispatcher, tick } from'svelte'
    const dispatch= createEventDispatcher()// ...
</script>

弹窗模板及核心逻辑处理。

<divclass="vui__layer" class:opened class:vui__layer-closed={closeCls}id={id}bind:this={el}><!-- 遮罩层-->
    {#if bool(shade)}<divclass="vlayer__overlay" on:click={shadeClicked}style:opacity></div>{/if}<!-- 主体--><divclass="vlayer__wrap {type&&'popui__'+type} anim-{anim}" style="{layerStyle}">
        {#if title}<divclass="vlayer__wrap-tit">{@html title}</div>{/if}
        {#if icon&&type=='toast'}<divclass="vlayer__toast-icon vlayer__toast-{icon}">{@html toastIcon[icon]}</div>{/if}<divclass="vlayer__wrap-cntbox"><!-- 判断content插槽是否存在-->
            {#if $$slots.content}<divclass="vlayer__wrap-cnt"><slotname="content"/></div>
            {:else}
                {#if content}<!-- iframe-->
                    {#if type=='iframe'}<divclass="vlayer__wrap-iframe"><iframescrolling="auto" allowtransparency="true" frameborder="0" src={content}></iframe></div><!-- message|notify|popover-->
                    {:else if type=='message' || type=='notify' || type=='popover'}<divclass="vlayer__wrap-cnt">
                        {#if icon}<iclass="vlayer-msg__icon {icon}">{@html messageIcon[icon]}</i>{/if}<divclass="vlayer-msg__group">
                            {#if title}<divclass="vlayer-msg__title">{@html title}</div>{/if}<divclass="vlayer-msg__content">{@html content}</div></div></div><!-- 加载动态组件-->
                    {:else if type == 'component'}<svelte:componentthis={content}/>
                    {:else}<divclass="vlayer__wrap-cnt">{@html content}</div>
                    {/if}
                {/if}
            {/if}<slot/></div><!-- 按钮组-->
        {#if btns}<divclass="vlayer__wrap-btns">
            {#each btns as btn,index}<spanclass="btn" class:btn-disabled={btn.disabled}style="{btn.style}">{@html btn.text}</span>
            {/each}</div>
        {/if}

        {#if xclose}<spanclass="vlayer__xclose" style="color: {xcolor}" on:click={hide}></span>
        {/if}
        {#if maximize}<spanclass="vlayer__maximize" on:click={maximizeClicked}></span>{/if}<!-- 缩放-->
        {#if resize}<spanclass="vlayer__groupresize"><iclass="vlayer__resize LT"></i><iclass="vlayer__resize RT"></i><iclass="vlayer__resize LB"></i><iclass="vlayer__resize RB"></i></span>
        {/if}</div><!-- 优化拖拽卡顿--><divclass="vlayer__dragfix"></div></div><script>/**
     * @Desc     Svelte.js桌面端对话框组件SvelteLayer
     * @Time     andy by 2022-04
     * @About    Q:282310962  wx:xy190310*/// ...
    onMount(()=> {
        console.log('监听弹窗开启')
        window.addEventListener('resize', autopos,false)return ()=> {
            console.log('监听弹窗关闭')
            window.removeEventListener('resize', autopos,false)
        }
    })

    afterUpdate(()=> {
        console.log('监听弹窗更新')
    })// 动态监听开启/关闭    $:if(open) {
        show()
    }else {
        hide()
    }/**
     * 开启弹窗*/
    asyncfunction show() {if(opened)return
        opened=true
        dispatch('open')typeof onOpen==='function'&& onOpen()// 避免获取弹窗宽高不准确        await tick()

        zIndex= util.getZIndex(zIndex)+1

        auto()
    }/**
     * 关闭弹窗*/function hide() {// ...    }// 弹窗位置function auto() {
        autopos()// 全屏弹窗if(fullscreen) {
            full()
        }// 拖拽|缩放        move()
    }// 弹窗定位function autopos() {if(!opened)return
        let ol, ot
        let pos= position
        let isfixed= bool(fixed)
        let vlayero= el.querySelector('.vlayer__wrap')if(!isfixed|| follow) {
            vlayero.style.position='absolute'
        }

        let area= [util.client('width'), util.client('height'), vlayero.offsetWidth, vlayero.offsetHeight]

        ol= (area[0]- area[2])/2
        ot= (area[1]- area[3])/2if(follow) {
            offset()
        }else {typeof pos==='object'? (
                ol= parseFloat(pos[0])||0, ot= parseFloat(pos[1])||0
            ) : (
                pos=='t'? ot=0 : 
                pos=='r'? ol= area[0]- area[2] : 
                pos=='b'? ot= area[1]- area[3] : 
                pos=='l'? ol=0 : 
                pos=='lt'? (ol=0, ot=0) : 
                pos=='rt'? (ol= area[0]- area[2], ot=0) : 
                pos=='lb'? (ol=0, ot= area[1]- area[3]) :
                pos=='rb'? (ol= area[0]- area[2], ot= area[1]- area[3]) :null
            )

            vlayero.style.left= parseFloat(isfixed? ol : util.scroll('left')+ ol)+'px'
            vlayero.style.top= parseFloat(isfixed? ot : util.scroll('top')+ ot)+'px'
        }
    }// 跟随定位function offset() {
        let ow, oh, ps
        let vlayero= el.querySelector('.vlayer__wrap')

        ow= vlayero.offsetWidth
        oh= vlayero.offsetHeight
        ps= util.getFollowRect(follow, ow, oh)
        tipArrow= ps[2]

        vlayero.style.left= ps[0]+'px'
        vlayero.style.top= ps[1]+'px'
    }// 最大化弹窗    asyncfunction full() {// ...    }// 复位弹窗    asyncfunction restore() {// ...    }// 拖拽缩放function move() {
        let isfixed= bool(fixed)
        let isdragOut= bool(dragOut)
        let c= {}

        let vlayero= el.querySelector('.vlayer__wrap')
        let otit= el.querySelector('.vlayer__wrap-tit')
        let ocnt= el.querySelector('.vlayer__wrap-cntbox')
        let obtn= el.querySelector('.vlayer__wrap-btns')
        let odrag= el.querySelector(drag)
        let oresize= el.querySelectorAll('.vlayer__resize')
        let ofix= el.querySelector('.vlayer__dragfix')// 拖拽if(odrag) {
            odrag.style.cursor= util.isIE()?'move' :'grab'
            util.on(odrag,'mousedown',function(e) {// ...            })
        }

        util.on(document,'mousemove',function(e) {if(c.dragTrigger) {
                let iL= e.clientX- c.pos[0]+ c.area[0]
                let iT= e.clientY- c.pos[1]+ c.area[1]
                let fixL= isfixed?0 : c.scroll[1]
                let fixT= isfixed?0 : c.scroll[2]
                let iMaxL= c.client[0]+ fixL- c.area[2]
                let iMaxT= c.client[1]+ fixT- c.area[3]
                let oMaxT= c.scroll[0]- c.area[3]// 边界检测if(isdragOut) {
                    iT= iT<0?0 : iT
                    iT= (iT> oMaxT)? oMaxT : iT
                }else {
                    iL= (iL< fixL)? fixL : iL
                    iL= (iL> iMaxL)? iMaxL : iL
                    iT= (iT< fixT)? fixT : iT
                    iT= (iT> iMaxT)? iMaxT : iT
                }// 记录拖拽弹窗坐标                c.dragPosition= {
                    width: c.area[2],
                    height: c.area[3],
                    x: iL,
                    y: iT
                }// 限制拖拽方向                dragDir=='horizontal'? (vlayero.style.left= iL+'px')
                :
                dragDir=='vertical'? (vlayero.style.top= iT+'px')
                :
                (vlayero.style.left= iL+'px', vlayero.style.top= iT+'px')
            }// 边角缩放if(c.resizeTrigger&& c.elem) {// ...            }
        })

        util.on(document,'mouseup',function() {
            c.dragTrigger&& (delete c.dragTrigger, ofix.style.display='none',typeof dragEnd==='function'&& dragEnd(dragPosition)
            )
            c.resizeTrigger&& (delete c.resizeTrigger, ofix.style.display='none'
            )
            document.onmouseup=null
        })
    }// 点击最大化按钮function maximizeClicked(e) {
        let o= e.targetif(o.classList.contains('maximized')) {
            restore()
        }else {
            full()
        }
    }//...</script>

svelte-layer还支持类型为message | notify | popover 弹窗。

svelte组件:svelte3自定义桌面PC端对话框组件svelte-layer

svLayer.message({})
svLayer.notify({})
svLayer.popover({})

调用方式如上,只支持函数式调用。

svelte-layer支持自定义拖拽区域drag: '#header' ,是否拖拽到窗口外dragOut:true 。还支持iframe弹窗类型type: 'iframe' ,配置topmost:true 即可让当前活动窗口保持置顶状态。

该组件还有一大亮点,就是支持动态引入外部组件。

import Counter from '$lib/Counter.svelte'

// 动态加载组件(函数式调用)
function showComponentLayer() {
    svLayer({
        type: 'component',
        title: '动态加载组件',
        content: Counter,
        resize: true,
        xclose: true,
        maximize: true,
        area: ['360px', '250px']
    })
}<!-- 组件调用--><Layerbind:open={showComponent}content="这里是内容信息" resize drag=".vlayer__wrap-cnt"
    btns={[{text: '确认', style: 'color:#f60;', click: ()=> showComponent=false},
    ]}
    on:open={handleOpen} on:close={handleClose}
><svelte:fragmentslot="content"><Counter/></svelte:fragment></Layer>

OK,基于Svelte.js开发pc端弹窗组件就分享到这里。希望对大家有一些帮助~ ????

  • 作者:xiaoyan2017
  • 原文链接:https://www.cnblogs.com/xiaoyan2017/p/16158044.html
    更新时间:2022年5月19日13:08:47 ,共 8372 字。