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 ' } 标签: Javascript

添加新评论