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


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

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

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

    ◆ 快速引入

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

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

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

    • 标签式调用
    复制代码
    <!-- 询问框 -->
    <Layer bind: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-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>
    复制代码

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

    复制代码
    <div class="vui__layer" class:opened class:vui__layer-closed={closeCls} id={id} bind:this={el}>
        <!-- 遮罩层 -->
        {#if bool(shade)}<div class="vlayer__overlay" on:click={shadeClicked} style:opacity></div>{/if}
        <!-- 主体 -->
        <div class="vlayer__wrap {type&&'popui__'+type} anim-{anim}" style="{layerStyle}">
            {#if title}<div class="vlayer__wrap-tit">{@html title}</div>{/if}
            {#if icon&&type=='toast'}<div class="vlayer__toast-icon vlayer__toast-{icon}">{@html toastIcon[icon]}</div>{/if}
            <div class="vlayer__wrap-cntbox">
                <!-- 判断content插槽是否存在 -->
                {#if $$slots.content}
                    <div class="vlayer__wrap-cnt"><slot name="content" /></div>
                {:else}
                    {#if content}
                        <!-- iframe -->
                        {#if type=='iframe'}
                        <div class="vlayer__wrap-iframe">
                            <iframe scrolling="auto" allowtransparency="true" frameborder="0" src={content}></iframe>
                        </div>
                        <!-- message|notify|popover -->
                        {:else if type=='message' || type=='notify' || type=='popover'}
                        <div class="vlayer__wrap-cnt">
                            {#if icon}<i class="vlayer-msg__icon {icon}">{@html messageIcon[icon]}</i>{/if}
                            <div class="vlayer-msg__group">
                                {#if title}<div class="vlayer-msg__title">{@html title}</div>{/if}
                                <div class="vlayer-msg__content">{@html content}</div>
                            </div>
                        </div>
                        <!-- 加载动态组件 -->
                        {:else if type == 'component'}
                        <svelte:component this={content}/>
                        {:else}
                        <div class="vlayer__wrap-cnt">{@html content}</div>
                        {/if}
                    {/if}
                {/if}
                <slot />
            </div>
    
            <!-- 按钮组 -->
            {#if btns}
            <div class="vlayer__wrap-btns">
                {#each btns as btn,index}
                    <span class="btn" class:btn-disabled={btn.disabled} style="{btn.style}">{@html btn.text}</span>
                {/each}
            </div>
            {/if}
    
            {#if xclose}
            <span class="vlayer__xclose" style="color: {xcolor}" on:click={hide}></span>
            {/if}
            {#if maximize}<span class="vlayer__maximize" on:click={maximizeClicked}></span>{/if}
            <!-- 缩放 -->
            {#if resize}
            <span class="vlayer__groupresize">
                <i class="vlayer__resize LT"></i>
                <i class="vlayer__resize RT"></i>
                <i class="vlayer__resize LB"></i>
                <i class="vlayer__resize RB"></i>
            </span>
            {/if}
        </div>
        <!-- 优化拖拽卡顿 -->
        <div class="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()
        }
    
        /**
         * 开启弹窗
         */
        async function 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]) / 2
    
            if(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'
        }
    
        // 最大化弹窗
        async function full() {
            // ...
        }
    
        // 复位弹窗
        async function 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.target
            if(o.classList.contains('maximized')) {
                restore()
            }else {
                full()
            }
        }
        
        //...
    </script>
    复制代码

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

    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']
        })
    }
    
    <!-- 组件调用 -->
    <Layer bind:open={showComponent} content="这里是内容信息" resize drag=".vlayer__wrap-cnt"
        btns={[
            {text: '确认', style: 'color:#f60;', click: () => showComponent=false},
        ]}
        on:open={handleOpen} on:close={handleClose}
    >
        <svelte:fragment slot="content">
            <Counter />
        </svelte:fragment>
    </Layer>
    复制代码

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

    最后附上一个svelte3+svelteKit聊天实例项目

    svelte3.x+sass仿微信app聊天:https://www.cnblogs.com/xiaoyan2017/p/16110203.html

     

  • 相关阅读:
    前端常用的 59 个工具类【持续更新】
    记录一次WLAN Panic导致的gcore问题分析
    AI:70-基于机器学习的水质检预测
    Docker 安装kong konga
    wodFtpDLX ActiveX 组件--Crack
    基于Matlab实现评价型模型求解方法(附上源码+数据)
    Gin 文件上传操作(单/多文件操作)
    主流X86-ARM-RISC-V-MIPS芯片架构分析
    【附录01】概率基本性质和法则推导证明
    EasyDarwin开源流媒体服务器
  • 原文地址:https://www.cnblogs.com/xiaoyan2017/p/16158044.html