简单实现 弹窗组件Popup
最近在开发需求上面发现了一个弹窗需求,平常赶需求进度的话一般都是直接使用vant里面直接使用组件,比较方便上手。最近刚好有机会摸鱼,遇到这个需求,干脆自己封装一下这个组件,以后肯定也会用上。顺便把在封装里面的一些小坑记录一下
实现的功能类似以下这张图:
这种组件其实最核心的就是弹窗,遮罩的动画和样式,其他的带有逻辑性的api都可以用父子组件的通信处理。
popup(组件复制可以直接使用,vue2.0的兼容性比较强,所以用的是vue2.6.1版本):
<template>
<div :style="{ '--theme-color': themeColor }">
<section v-if="isMask" class="mask" @click="closePopup()" />
<footer :class="['footer', getOpenType()]">
<p class="time-item" v-for="item in times" :key="item.key" :class="item.key === 1 ? 'border' : ''" @click="chooseTime(item)">
{{ item.time }}
</p>
<section class="cancel">取消</section>
</footer>
</div>
</template>
<script>
export default {
name: "popup",
props: {
value: {
type: Boolean,
default: false,
},
themeColor: {
type: String,
default: "#fff",
},
},
data() {
return {
isMask: false,
times: [
{ time: "最近一年", key: 1, days: 365 },
{ time: "最近三个月", key: 2, days: 90 },
{ time: "最近一个月", key: 3, days: 30 },
{ time: "最近七天", key: 4, days: 7 },
],
openType: 0, // 0 没有变化 1 打开 2 关闭
};
},
watch: {
value(oldValue, newValue) {
if (oldValue) {
this.isMask = true;
this.openType = 1;
}
if (newValue) {
this.openType = 2;
setTimeout(() => {
this.isMask = false;
}, 200);
}
},
},
methods: {
closePopup() {
this.$emit("input", false);
},
getOpenType() {
let type = this.openType === 1 ? "open" : this.openType === 2 ? "close" : "init";
return type;
},
chooseTime(item){
this.$emit("chooseTime",{...item});
}
},
};
</script>
<style scoped>
.popup {
top: 0;
left: 0;
width: 100%;
height: 100%;
font-size: 17px;
transition: all 0.5s;
}
.mask {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: #000000;
opacity: 0.5;
}
.footer {
position: fixed;
bottom: 0;
left: 0;
width: 100%;
border-radius: 12px;
background-color: var(--theme-color);
}
.border {
border-radius: 12px;
}
.init {
height: 0px;
}
.open {
height: 321px;
animation: slideContentUp 0.2s linear both;
}
.close {
height: 321px;
animation: slideContentDown 0.2s linear both;
}
.time-item {
height: 56px;
background-color: #ffffff;
line-height: 56px;
text-align: center;
border-bottom: 1px solid #f5f5f5;
}
.cancel {
margin-top: 12px;
height: 84px;
width: 100%;
line-height: 84px;
background-color: #ffffff;
text-align: center;
}
@keyframes slideContentUp {
from {
height: 0;
}
to {
height: 320px;
}
}
@keyframes slideContentDown {
from {
height: 320px;
}
to {
height: 0;
}
}
</style>
需要注意的地方做一下记录:
为什么一开始把这个弹窗的高度设置为0?
这边使用的是 animation 做一个动效处理。如果一开始没有将弹窗的高度设置为0的话,会出现这种情况:一进入页面,默认接收的是否接受弹窗的参数是 false,会导致一进入页面就会有弹窗向底部滑动的样式。所以这边做了一个watch监听,用 openType参数 来做弹窗展示的显示(0:不设置高度,1:打开弹窗的时候,给上高度 2:关闭弹窗,同理 )。
遮罩优化
在watch监听里面关闭弹窗的时候会用 setTimeOut 延时动画的时间后在关闭,提升用户的体验感
出现遮罩以后滚动鼠标,底部页面滚动的问题
1、在打开遮罩层的事件中添加以下代码来禁止滚动
document.documentElement.style.overflow = 'hidden';
2、在关闭遮罩层的事件中添加以下代码来允许滚动
document.documentElement.style.overflow = 'scroll';
在父组件使用
正常引入后
<Popup v-model="isShow" themeColor="#f5f5f5" @chooseTime="chooseTime" />
// isShow :控制弹窗
// themeColor : 主题颜色
// chooseTime :点击时间时触发
我这边项目比较简单,使用到的api就保留了三个。只要实现了基本的样式功能,理润上各位大哥在copy使用的时候,可以加上一些自己需求上的一些功能进行扩展。确实自己封装一个小组件在打包以后的大小会比使用vant的小很多。 好了,换个姿势摸摸鱼