前几天有给大家分享一个 svelte+sass实战微信app界面聊天实例
明天要给大家分享的是全新研发的svelte.js自定义桌面版对话框组件SvelteLayer。
如下图:在lib目录下新建一个Layer组件目录。
引入svelte-layer组件
弹窗组件反对组件式(Layer
)+函数式(svLayer
)两种调用形式。
import Layer, {svLayer} from '$lib/Layer'
- 函数式写法
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 }) } }, ] })}
- 组件式写法
<!-- 询问框 --><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}, ]}/>
两种写法能够独自应用,如果是一些简单的弹窗场景,可思考两种写法混合调用。
svelte-layer配置参数
反对如下超过30+参数随便搭配调用。
<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>
弹窗模板及js逻辑解决模块。
<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还反对动静引入内部组件。
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>
另外还封装了相似element-ui种message/notify/popover弹窗类型。
/** * Message提醒音讯 * @param {object} options * @param {string} icon图标(info/success/error/warning/loading) */svLayer.message = function(options) { typeof options == 'string' && (options = {content: options}) return svLayer({ icon: 'info', anim: 'fadeInUp', position: 't', lockScroll: false, shade: false, time: 2, layerStyle: 'margin-top: 10px', ...options, title: null, type: 'message' })}/** * Notification告诉音讯 * @param {object} options * @param {string} icon图标(info/success/error/warning/loading) */svLayer.notify = function(options) { typeof options == 'string' && (options = {content: options}) return svLayer({ anim: 'fadeInRight', position: 'rt', lockScroll: false, shade: false, xclose: true, time: 5, layerStyle: 'margin: 20px 0 0 -20px', ...options, type: 'notify' })}/** * Popover气泡框 * @param {object} options * @param {string} icon图标(info/success/error/warning/loading) */svLayer.popover = function(options) { return svLayer({ anim: 'fadeIn', lockScroll: false, shade: false, xclose: true, ...options, type: 'popover' })}
好了,以上就是svelte.js开发自定义pc端弹窗插件的一些分享。后续还会分享一些svelte自定义组件及实战案例我的项目。