前两天有给大家分享一个 svelte自定义Tabbar+Navbar组件。
明天给大家带来的是最新开发的svelte自定义手机端模态框组件SveltePopup。
如下图:在lib目录下新建一个Popup组件目录。
引入svelte-popup组件
弹窗组件反对组件式(Popup
)+函数式(svPopup
)两种调用形式。
import Popup, {svPopup} from '$lib/Popup'
- 组件式调用
<Popup bind:open={isShowDialog} xclose shadeClose="false" title="题目" content="显示内容信息" btns={[ {text: '确认', style: 'color:#f60;', click: () => isShowDialog=false}, ]} on:open={handleOpen} on:close={handleClose}> <svelte:fragment slot="content"><h3>自定义slot插槽显示内容</h3></svelte:fragment></Popup>
- 函数式调用
let el = svPopup({ title: '题目信息', content: '<p style='color:#09f;'>展现内容信息</p>', xclose: true, shadeClose: false, btns: [ {text: '勾销', click: () => { el.$set({open: false}) }}, {text: '确认', style: 'color:#f90;', click: () => handleOK}, ], onOpen: () => {}, onClose: () => {}})
一些简略的弹窗成果通过函数调用更加不便,对于一些功能丰富的弹窗展现,能够应用组件式slot自定义插槽形式展现。
<Popup bind:open={showConfirm} shadeClose="false" title="正告信息" xclose zIndex="2001" content="<div style='color:#00e0a1;padding:20px 40px;'>确认框(这里是确认框提示信息,这里确认框提示信息,这里是确认框提示信息)</div>" btns={[ {text: '勾销', click: () => showConfirm=false}, {text: '确定', style: 'color:#e63d23;', click: handleInfo}, ]}/><!-- 底部对话框 --><Popup bind:open={showFooter} anim="footer" type="footer" shadeClose="false" zIndex="1001" content="确定删除该条数据吗?删除后可在7天之内复原数据,超过7天后数据就无奈复原啦!" btns={[ {text: '复原', style: 'color:#00e0a1;', click: handleInfo}, {text: '删除', style: 'color:#ee0a24;', click: () => null}, {text: '勾销', style: 'color:#a9a9a9;', click: () => showFooter=false}, ]}/><!-- ActionSheet底部弹出式菜单 --><Popup bind:open={showActionSheet} anim="footer" type="actionsheet" zIndex="2020" content="弹窗内容,告知以后状态、信息和解决办法,形容文字尽量管制在三行内" btns={[ {text: '拍照', style: 'color:#09f;', disabled: true, click: handleInfo}, {text: '从手机相册抉择', style: 'color:#00e0a1;', click: handleInfo}, {text: '保留图片', style: 'color:#e63d23;', click: () => null}, {text: '勾销', click: () => showActionSheet=false}, ]}/><!-- ActionSheet底部弹出式菜单(仿微信weui-picker顶部按钮) --><Popup bind:open={showActionPicker} anim="footer" type="actionsheetPicker" round title="题目内容" btns={[ {text: '勾销', click: () => showActionPicker=false}, {text: '确定', style: 'color:#00e0a1;', click: () => null}, ]}> <!-- 自定义内容 --> <ul class="goods-list" style="padding:50px;text-align:center;"> <li>双肩包</li> <li>鞋子</li> <li>运动裤</li> </ul></Popup>
还反对函数式 多层混合 形式调用。
function handleInfo(e) { console.log(e) console.log('通过函数形式调用弹窗...') let el = svPopup({ title: '题目', content: `<div style="padding:20px;"> <p>函数式调用:<em style="color:#999;">svPopup({...})</em></p> </div>`, btns: [ { text: '勾销', click: () => { // 敞开弹窗 el.$set({open: false}) } }, { text: '确认', style: 'color:#09f;', click: () => { svPopup({ type: 'toast', icon: 'loading', content: '加载中...', opacity: .2, time: 2 }) } }, ] })}
同样也反对组件式和函数式混合调用。
svelte-popup参数配置
反对如下 20+ 参数混合调用。
<script> // 是否关上弹窗bind:open={showDialog} export let open = false // 弹窗标识符 // export let id = 'svpopup-' + Math.random().toString(32) export let id = undefined // 题目 export let title = '' // 内容 export let content = '' // 弹窗类型 export let type = '' // 自定义弹窗款式 export let popupStyle = undefined // toast图标 export let icon = '' // 是否显示遮罩层 export let shade = true // 点击遮罩层是否敞开 export let shadeClose = true // 遮罩层透明度 export let opacity = '' // 是否显示圆角 export let round = false // 是否显示敞开图标 export let xclose = false // 敞开图标地位 export let xposition = 'right' // 敞开图标色彩 export let xcolor = '#333' // 弹窗动画 export let anim = 'scaleIn' // 弹窗地位 export let position = '' // 长按/右键弹窗 export let follow = null // 弹窗主动敞开工夫 export let time = 0 // 弹窗层级 export let zIndex = 202203 // 弹窗按钮组 export let btns = null /* export let btns = [ { text: '勾销', style: 'color:#aaa', disabled: true, click: null }, { text: '确定', style: 'color:#f90', click: null } ] */ // 函数式关上|敞开回调 export let onOpen = undefined export let onClose = undefined // 接管函数式移除指令 export let remove = undefined // ...</script>
弹窗模板及js解决局部。
<div class="sv__popup" class:opened class:sv__popup-closed={closeCls} id={id} style="z-index: {zIndex}" bind:this={el}> {#if bool(shade)}<div class="vui__overlay" on:click={shadeClicked} style:opacity></div>{/if} <div class="vui__wrap"> <div class="vui__wrap-section"> <div class="vui__wrap-child {type&&'popupui__'+type} anim-{anim} {position}" class:round style="{popupStyle}"> {#if title}<div class="vui__wrap-tit">{@html title}</div>{/if} {#if icon&&type=='toast'}<div class="vui__toast-icon">{@html toastIcon[icon]}</div>{/if} {#if $$slots.content} <div class="vui__wrap-cnt"><slot name="content" /></div> {:else} {#if content}<div class="vui__wrap-cnt">{@html content}</div>{/if} {/if} <slot /> {#if btns} <div class="vui__wrap-btns"> {#each btns as btn,index} <span class="btn"style="{btn.style}" on:click={e => btnClicked(e, index)}>{@html btn.text}</span> {/each} </div> {/if} {#if xclose}<span class="vui__xclose {xposition}" style="color: {xcolor}" on:click={hide}></span>{/if} </div> </div> </div></div>/** * @Desc svelte自定义挪动端弹窗组件 * @Time andy by 2022/3/15 * @About Q:282310962 wx:xy190310 */<script> // ... import { onMount, afterUpdate, createEventDispatcher, tick } from 'svelte' const dispatch = createEventDispatcher() let opened = false let closeCls = undefined let toastIcon = { loading: '', success: '', fail: '', } const bool = (boolean) => JSON.parse(boolean) ? true : false onMount(() => { console.log('监听弹窗开启...') return () => { console.log('监听弹窗敞开...') } }) afterUpdate(() => { // console.log('监听弹窗更新...') /* if(opened) { if(!open) { opened = false dispatch('close') } }else if(open) { opened = true dispatch('open') } */ }) $: if(open) { show() }else { hide() } /** * 关上弹窗 */ async function show() { if(opened) return opened = true dispatch('open') typeof onOpen == 'function' && onOpen() zIndex = getZIndex() + 1 // 倒计时敞开 if(time) { index++ if(timer[index] != null) clearTimeout(timer[index]) timer[index] = setTimeout(() => { hide() }, parseInt(time)*1000) } // 长按|右键菜单 if(follow) { // ... } } /** * 敞开弹窗 */ function hide() { if(!opened) return closeCls = true setTimeout(() => { opened = false closeCls = false open = false // ... }, 200) } // 点击遮罩层 function shadeClicked() { if(bool(shadeClose)) { hide() } } // ...// 临界坐标点 function getPos(x, y, ow, oh, winW, winH) { let l = (x + ow) > winW ? x - ow : x let t = (y + oh) > winH ? y - oh : y return [l, t] }</script>
一开始开发的时候只反对组件式调用。想着如果能反对函数式调用(插入组件至body,敞开即移除)就好了。
起初在svelte官网发现 new Component
能够传入 props
参数,试了下,发现果然能够实现这种成果。const component = new Component(options)
如下是官网给的例子
https://svelte.dev/docs#run-t...
import App from './App.svelte';const app = new App({ target: document.body, props: { // assuming App.svelte contains something like // `export let answer`: answer: 42 }});
于是新建一个popup.js。
import Popup from './Popup.svelte'let uuid = function() { return 'svpopup-' + Math.floor(Math.random() * 10000)}export function svPopup(options = {}) { options.id = uuid() const mountNode = document.createElement('div') document.body.appendChild(mountNode) const app = new Popup({ target: mountNode, props: { ...options, open: true, // 传入函数移除指令 remove() { document.body.removeChild(mountNode) } } }) return app}export default Popup
通过如上形式就完满解决了导出 组件式+函数式 的调用形式了。
通过一系列的学习,发现svelte还是挺不错的,尤其编译运行够快,体积够小。不过惟一有点遗憾的是还没有找到相似vue全局引入组件的办法。