基于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

svelte组件:svelte3自定义桌面PC端对话框组件svelte-layer的更多相关文章

  1. 基于Svelte3.x桌面端UI组件库Svelte UI

    Svelte-UI,一套基于svelte.js开发的桌面pc端ui组件库 最近一直忙于写svelte-ui,一套svelte3开发的桌面端ui组件库.在设计及功能上借鉴了element-ui组件库.所 ...

  2. vue3系列:vue3.0自定义全局弹层V3Layer|vue3.x pc桌面端弹窗组件

    基于Vue3.0开发PC桌面端自定义对话框组件V3Layer. 前两天有分享一个vue3.0移动端弹出层组件,今天分享的是最新开发的vue3.0版pc端弹窗组件. V3Layer 一款使用vue3.0 ...

  3. 基于Vue.js PC桌面端弹出框组件|vue自定义弹层组件|vue模态框

    vue.js构建的轻量级PC网页端交互式弹层组件VLayer. 前段时间有分享过一个vue移动端弹窗组件,今天给大家分享一个最近开发的vue pc端弹出层组件. VLayer 一款集Alert.Dia ...

  4. 基于React.js网页版弹窗|react pc端自定义对话框组件RLayer

    基于React.js实现PC桌面端自定义弹窗组件RLayer. 前几天有分享一个Vue网页版弹框组件,今天分享一个最新开发的React PC桌面端自定义对话框组件. RLayer 一款基于react. ...

  5. svelte组件:Svelte3自定义Navbar+Tabbr组件|svelte自定义插件

    基于Svelte3自定义组件Navbar+Tabbar沉浸式导航条|底部凸起菜单栏 Svelte 一种全新的构建用户界面的框架.当下热门的 Vue 和 React 在浏览器中需要做大量的工作,而 Sv ...

  6. 一款多功能的移动端滚动选择器,支持单选到多选、支持多级级联、提供自定义回调函数、提供update函数二次渲染、重定位函数、兼容pc端拖拽等等..

    https://github.com/onlyhom/mobileSelect.js/blob/master/docs/README-CN.md mobileSelect.js 一款多功能的移动端滚动 ...

  7. Taro自定义Modal对话框组件|taro仿微信、android弹窗

    基于Taro多端实践TaroPop:自定义模态框|dialog对话框|msg消息框|Toast提示 taro自定义弹出框支持编译到多端H5/小程序/ReactNative,还可以自定义弹窗类型/弹窗样 ...

  8. 教你PC端网易云音乐自定义代理,VIP免费听歌!

    今天分享一份福利吧,使用网易云音乐自定义代理实现免费听和下载VIP.极高音质.付费的歌曲,这里主要针对PC端电脑版的,需要自己写脚本运行. 01 安装node.js Node.js是一个让 JavaS ...

  9. MVC文件上传06-使用客户端jQuery-File-Upload插件和服务端Backload组件自定义控制器上传多个文件

    当需要在控制器中处理除了文件的其他表单字段,执行控制器独有的业务逻辑......等等,这时候我们可以自定义控制器. MVC文件上传相关兄弟篇: MVC文件上传01-使用jquery异步上传并客户端验证 ...

随机推荐

  1. 《前端运维》二、Nginx--3静态资源服务、跨域与其他

    一.静态资源服务 首先,静态资源一般是指客户端发送请求到Web服务器,web服务器从内存中取得相应的文件,返回给客户端,客户端解析并渲染出来.动态资源呢,则是由客户端发起请求,先交由web容器,web ...

  2. ioctl以及read阻塞型引发的思考

    1. 尝试strace 或 jstack 去追踪程序,发现某一个进程作为socket连接server出现如下的log(strace追踪): 1. ioctl(45,[0],0) = 0 2. .... ...

  3. 高度不定,宽100%,内一div高不确定,如何实现垂直居中?

    verticle-align: middle; 绝对定位50%加translateY(-50%) 绝对定位,上下左右全0,margin:auto

  4. 怎样将 GB2312 编码的字符串转换为 ISO-8859-1 编码的 字符串?

    String s1 = "你好"; String s2 = new String(s1.getBytes("GB2312"), "ISO-8859-1 ...

  5. vue使用svg,animate事件绑定无效问题及解决方法

    由于使用svg制作圆形进度条,但是进度展示的太生硬,没有过渡圆滑的效果,所以使用 animate(在svg元素里可以查到) 元素标签,但 这样使用了,还是没有效果,我前端使用的 vue ,所以通过 @ ...

  6. Linux重启网卡服务Failed to start LSB: Bring up/down networking.

    Linux网卡重启失败,使用 systemctl status network.service命令进行查看状态,发现启动有异常产生 network.service: control process e ...

  7. js技术之循环for

    案例:把所有单词以空格为分割并将首字母转为大写 <!DOCTYPE html><html lang="en"><head> <meta c ...

  8. 解决引用类型为什么打出的是地址值,又怎么改成输出属性值(toString()底层)

    一丶toString的源码解析: 一丶object的toString的源码解析: 集合中toString源码分析: 小结: 改成输出属性值 在父类中重写toString();方法 快捷键:Alt+In ...

  9. 惯性传感器(IMU)

    近两年来,车联网.自动驾驶.无人驾驶.汽车智能化.网联化等成为了汽车行业的热点话题,未来汽车一定是朝着安全.可靠及舒适的方向发展.而这一切背后的发展都离不开传感器的作用,今天我们就来聊聊用途越来越广的 ...

  10. cpu设计过程

    一款CPU是如何设计出来的? 前面一段,我们了解了芯片的制造过程,也就是如何从沙子中提取硅.把硅切成片,在片上通过离子注入实现PN结.实现各种二极管.三极管.CMOS管.从而实现千万门级大规模集成电路 ...