前言
各位小夥伴們,國慶假期玩的愉快嚒!假期結束後的第一天上班,是不是還沉浸在節日的氣氛中,哈哈哈~~ ok 開始正事吧。
趁著國慶假期一直在搗鼓Nuxt.js專案開發,由於專案中多個地方需要用到彈窗元件,一開始是想著使用Vant元件庫中的Popup元件。後來經過再三考慮決定自己造一個。
vue自定義Model彈出框元件VPopup
介紹
VPopup 基於Vue.js構造的移動端元件。彙集了有贊Vant及京東NutUI元件庫中的 Popup彈出層、Notify通知資訊、Dialog對話方塊、ActionSheet底部皮膚框及Toast載入框 等功能。
還支援右鍵選單/長按彈窗,類似微信長按彈窗效果。支援自定義蒙層透明度。
如上圖:在例項專案中的部分應用。
使用
在main.js中全域性引入元件。
import Popup from './components/popup'
Vue.use(Popup)
元件支援標籤式及函式式兩種呼叫方法。
<!-- 標籤式呼叫 -->
<template>
<view id="app">
...
<!-- VPopup模板 -->
<v-popup
v-model="showPopup"
anim="scaleIn"
title="標題內容"
content="彈窗內容,描述文字儘量控制在三行內!"
shadeClose="false"
xclose
:btns="[
{...},
{...},
]"
/>
</view>
</template>
// 函式式呼叫
<script>
export default {
...
methods: {
handleShowDialog() {
let $pop = this.$vpopup({
title: '標題內容',
content: '彈窗內容,描述文字儘量控制在三行內!',
anim: 'scaleIn',
shadeClose: false,
xclose: true,
onOpen: () => {
console.log('vpopup is opened!')
},
btns: [
{text: '關閉'},
{
text: '確定',
style: 'color:#00e0a1',
click: () => {
$pop.close()
}
}
]
});
}
}
}
</script>
提供了多種風格皮膚,普通msg、Toast提示、微信/Android/IOS彈窗、ActionSheet底部彈出層、Dialog對話方塊、長按彈窗。另外還支援多種動畫彈出及自定義彈出方向(上/右/下/左)。
呼叫非常簡單,傳入配置引數即可。
<!-- msg提示框 -->
<v-popup v-model="showMsg" anim="fadeIn" content="msg提示框測試(3s後視窗關閉)" shadeClose="false" time="3" />
<v-popup v-model="showMsgBg" anim="footer" content="自定義背景顏色" shade="false" time="2"
popup-style="background:rgba(0,0,0,.6);color:#fff;"
/>
<!-- 詢問框 -->
<v-popup v-model="showConfirm" shadeClose="false" title="警告資訊" xclose z-index="2001"
content="<div style='color:#00e0a1;padding:20px 40px;'>確認框(這裡是確認框提示資訊)</div>"
:btns="[
{text: '取消', click: () => showConfirm=false},
{text: '確定', style: 'color:#e63d23;', click: handleInfo},
]"
/>
<!-- Toast輕提示彈窗 -->
<v-popup v-model="showToast" type="toast" icon="loading" time="2" content="載入中..." />
<!-- Android風格彈窗 -->
<v-popup v-model="showAndroid1" type="android" shadeClose="false" xclose title="標題內容" z-index="2001"
content="彈窗內容,告知當前狀態、資訊和解決方法,描述文字儘量控制在三行內"
:btns="[
{text: '知道了', click: () => showAndroid1=false},
{text: '確定', style: 'color:#00e0a1;', click: handleInfo},
]"
>
</v-popup>
<!-- ActionSheet底部選單 -->
<v-popup v-model="showActionSheet" anim="footer" type="actionsheet" :z-index="1011"
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},
]"
/>
彈窗實現
支援如下自定義引數配置,隨意搭配出想實現的效果。
@@Props
------------------------------------------
v-model 當前元件是否顯示
title 標題
content 內容(支援自定義插槽內容)
type 彈窗型別(toast | footer | actionsheet | actionsheetPicker | android/ios)
popupStyle 自定義彈窗樣式
icon toast圖示(loading | success | fail)
shade 是否顯示遮罩層
shadeClose 是否點選遮罩時關閉彈窗
opacity 遮罩層透明度
round 是否顯示圓角
xclose 是否顯示關閉圖示
xposition 關閉圖示位置(left | right | top | bottom)
xcolor 關閉圖示顏色
anim 彈窗動畫(scaleIn | fadeIn | footer | fadeInUp | fadeInDown)
position 彈出位置(top | right | bottom | left)
follow 長按/右鍵彈窗(座標點)
time 彈窗自動關閉秒數(1、2、3)
zIndex 彈窗層疊(預設8080)
btns 彈窗按鈕(引數:text|style|disabled|click)
@@$emit
------------------------------------------
open 開啟彈出層時觸發(@open="xxx")
close 關閉彈出層時觸發(@close="xxx")
@@Event
------------------------------------------
onOpen 開啟彈窗回撥
onClose 關閉彈窗回撥
自定義彈窗模板
<template>
<div v-show="opened" class="nuxt__popup" :class="{'nuxt__popup-closed': closeCls}" :id="id">
<div v-if="JSON.parse(shade)" class="nuxt__overlay" @click="shadeClicked" :style="{opacity}"></div>
<div class="nuxt__wrap">
<div class="nuxt__wrap-section">
<div class="nuxt__wrap-child" :class="['anim-'+anim, type&&'popui__'+type, round&&'round', position]" :style="popupStyle">
<div v-if="title" class="nuxt__wrap-tit" v-html="title"></div>
<div v-if="type=='toast'&&icon" class="nuxt__toast-icon" :class="['nuxt__toast-'+icon]" v-html="toastIcon[icon]"></div>
<template v-if="$slots.content"><div class="nuxt__wrap-cnt"><slot name="content" /></div></template>
<template v-else><div v-if="content" class="nuxt__wrap-cnt" v-html="content"></div></template>
<slot />
<div v-if="btns" class="nuxt__wrap-btns">
<span v-for="(btn,index) in btns" :key="index" class="btn" :style="btn.style" v-html="btn.text"></span>
</div>
<span v-if="xclose" class="nuxt__xclose" :class="xposition" :style="{'color': xcolor}" @click="close"></span>
</div>
</div>
</div>
</div>
</template>
/**
* @Desc VueJs自定義彈窗元件VPopup
* @Time andy by 2020-10-06
* @About Q:282310962 wx:xy190310
*/
<script>
let $index = 0, $lockCount = 0, $timer = {};
export default {
props: {
...
},
data() {
return {
opened: false,
closeCls: '',
toastIcon: {
...
}
}
},
watch: {
value(val) {
const type = val ? 'open' : 'close';
this[type]();
},
},
methods: {
// 開啟彈窗
open() {
if(this.opened) return;
this.opened = true;
this.$emit('open');
typeof this.onOpen === 'function' && this.onOpen();
if(JSON.parse(this.shade)) {
if(!$lockCount) {
document.body.classList.add('nt-overflow-hidden');
}
$lockCount++;
}
if(this.time) {
$index++;
if($timer[$index] !== null) clearTimeout($timer[$index])
$timer[$index] = setTimeout(() => {
this.close();
}, parseInt(this.time) * 1000);
}
if(this.follow) {
this.$nextTick(() => {
let obj = this.$el.querySelector('.nuxt__wrap-child');
let oW, oH, winW, winH, pos;
oW = obj.clientWidth;
oH = obj.clientHeight;
winW = window.innerWidth;
winH = window.innerHeight;
pos = this.getPos(this.follow[0], this.follow[1], oW, oH, winW, winH);
obj.style.left = pos[0] + 'px';
obj.style.top = pos[1] + 'px';
});
}
},
// 關閉彈窗
close() {
if(!this.opened) return;
this.closeCls = true;
setTimeout(() => {
this.opened = false;
this.closeCls = false;
if(JSON.parse(this.shade)) {
$lockCount--;
if(!$lockCount) {
document.body.classList.remove('nt-overflow-hidden');
}
}
if(this.time) {
$index--;
}
this.$emit('input', false);
this.$emit('close');
typeof this.onClose === 'function' && this.onClose();
}, 200);
},
shadeClicked() {
if(JSON.parse(this.shadeClose)) {
this.close();
}
},
btnClicked(e, index) {
let btn = this.btns[index];
if(!btn.disabled) {
typeof btn.click === 'function' && btn.click(e)
}
},
// 獲取彈窗座標點
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>
獲取並傳入右鍵/長按地方座標點,即可實現類似微信右鍵選單效果。
<v-popup v-model="showContextMenu" type="contextmenu" :follow="fwPos" opacity=".85"
:btns="[
{text: '標為未讀', click: handleContextPopup},
{text: '置頂聊天', style: 'color:#00e0a1;'},
{text: '確定要刪除該條資訊嗎?刪除後不可恢復!', style: 'color:#09f;'},
{text: '刪除', style: 'color:#e63d23;', click: () => showContextMenu=false},
]"
>
</v-popup>
handleContextMenu(e) {
this.showContextMenu = true;
let points = [e.clientX, e.clientY];
this.fwPos = points;
},
OK,基於Vue.js|Nuxt.js自定義彈層Popup就分享到這裡。希望對大家有些幫助!??
目前該元件已經在NuxtJS專案中開發應用,屆時也會分享出來。
最後附上一個最近例項專案
flutter/dart仿微信App聊天例項|flutter聊天室
本作品採用《CC 協議》,轉載必須註明作者和本文連結