封装理由
- 如下图所示,项目需要点击区域按钮出现一个弹出层,弹出层是在筛选条件下面显示的
- 大多数UI库的弹出层组件都是从顶部、底部、左侧、右侧、中间弹出,下拉选项组件又是需要占用一整行元素(不然弹出层的宽度就不是屏幕的宽度),这些UI组件都无法实现项目的需求(从指定的元素的指定位置弹出)
- 这是提供思路,如有不同情况可以根据项目需要修改代码
组件参数
| 属性 | 说明 | 示例 |
|---|---|---|
| popupShow | 控制弹窗的显示/隐藏(必填) | Boolean类型:true |
| offsetAmount | 弹窗的偏移量(必填) | String类型:'150px' |
| bgColor | 弹出层背景颜色 | 字符串:'#fff' |
| duration | 弹出层动画持续时间(单位ms) | 字符串:'500' |
| isMask | 是否显示遮罩层 | boolean:true |
| maskBgColor | 遮罩层的背景色 | 字符串:'rgba(0, 0, 0, 0.5)' |
| zIndex | 弹出层的层级 | number:100 |
封装代码
<template>
<div
class="mask"
v-if="props.isMask && popupShow"
:style="{
top: props.offsetAmount,
backgroundColor: props.maskBgColor,
'transition-duration': props.duration + 'ms',
zIndex: props.zIndex,
}"
></div>
<div
class="box"
:class="slideDirection"
:style="{
top: props.offsetAmount,
background: props.bgColor,
'animation-duration': props.duration + 'ms',
'transition-duration': props.duration + 'ms',
zIndex: props.zIndex,
}"
v-if="popupShow"
>
<slot> </slot>
</div>
</template>
<script setup>
import { ref, onMounted, computed, watch, getCurrentInstance } from 'vue';
import { onLoad } from '@dcloudio/uni-app';
const { proxy } = getCurrentInstance();
const props = defineProps({
offsetAmount: {
type: String,
required: true,
},
bgColor: {
type: String,
default: '#fff',
},
duration: {
type: String,
default: '500',
},
isMask: {
type: Boolean,
default: true,
},
maskBgColor: {
type: String,
default: 'rgba(0, 0, 0, 0.5)',
},
zIndex: {
type: Number,
default: 100,
},
});
const show = defineModel('show', { type: Boolean, required: true });
const popupShow = ref(false);
const slideDirection = ref(''); // 滑动方向
watch(
() => show.value,
(val) => {
if (val) {
// 设置滑动方向
slideDirection.value = 'top-show';
popupShow.value = true;
} else {
// 设置滑动方向
slideDirection.value = 'top-hide';
setTimeout(() => {
popupShow.value = false;
}, props.duration * 1 - 50);
}
}
);
onMounted(() => {});
onLoad((res) => {});
</script>
<style lang="scss" scoped>
.mask {
position: fixed;
width: 100%;
height: 100%;
transition: all 0.5s ease-in-out;
}
.box {
position: fixed;
width: 100%;
height: auto;
transition: all 0.5s ease-in-out;
}
.top-show {
animation: topShowAnimation 0.5s ease-in-out;
}
.top-hide {
animation: topHideAnimation 0.5s ease-in-out;
}
@keyframes topShowAnimation {
0% {
transform: translateY(-100%);
opacity: 0;
}
100% {
transform: translateY(0);
opacity: 1;
}
}
@keyframes topHideAnimation {
0% {
transform: translateY(0);
opacity: 1;
}
100% {
transform: translateY(-100%);
opacity: 0;
}
}
</style>
弹出层内容提供了插槽支持自定义
页面使用
tip : 页面使用比较麻烦,如有改进的方法欢迎留言
<div class="page-box" :style="pageBoxStyle">
<div class="father-box">
<button @click="appointPopupShow = !appointPopupShow">打开指定弹出位置的弹出层</button>
</div>
<appointPopup v-model:show="appointPopupShow" :offsetAmount="appointPopupOffsetAmount">
<div>插槽内容</div>
</appointPopup>
</div>
import { ref, watch, nextTick, onMounted } from 'vue';
onMounted(() => {
getAppointPopupOffsetAmount();
});
const appointPopupOffsetAmount = ref('');
const appointPopupShow = ref(false);
const pageBoxStyle = ref({});
watch(
() => appointPopupShow.value,
(val) => {
if (val) {
pageBoxStyle.value = {
height: '100vh',
overflow: 'hidden',
};
} else {
pageBoxStyle.value = {};
}
}
);
// 获取appointPopup的出行位置偏移量
async function getAppointPopupOffsetAmount() {
await new Promise((resolve) => setTimeout(resolve, 200));
await nextTick();
const box = uni.createSelectorQuery().select('father-box');
box
.boundingClientRect((data) => {
appointPopupOffsetAmount.value = data.top + data.height + 'px';
})
.exec();
}
页面使用解释
1.需要在页面的最外层添加一个盒子,盒子使用pageBoxStyle样式
解释:弹出层出现的时候需要让页面无法滚动
2.在 getAppointPopupOffsetAmount 函数中将 .father-box 改为你页面中需要在下方弹窗的元素的元素选择器
解释:因为是在指定元素的下方弹出,所以需要计算弹出的位置:指定元素的top距离+自身高度