参数
| 参数 | 说明 | 示例 |
|---|
| v-model:show | 控制弹出层显示/隐藏 | boolean:true |
| position | 弹出位置 | 字符串:'center' |
| radius | 圆角 | 字符串:'10' |
| width | 弹出层宽度(仅position为center、left、right时有效) | 字符串:'50%' |
| height | 弹出层高度(仅position为center时有效) | 字符串:'200px' |
| bgColor | 弹出层背景颜色 | 字符串:'#fff' |
| duration | 弹出层动画持续时间(单位ms) | 字符串:'500' |
| isMask | 是否显示遮罩层 | boolean:true |
| maskCloseAble | 点击遮罩层是否可以关闭弹窗 | boolean:true |
| maskBgColor | 遮罩层的背景色 | 字符串:'rgba(0, 0, 0, 0.5)' |
| zIndex | 弹出层的层级 | number:100 |
代码
<template>
<div
class="mask"
@click="clickMask"
:style="{
backgroundColor: props.maskBgColor,
'transition-duration': props.duration + 'ms',
zIndex: props.zIndex,
}"
v-if="props.isMask && popupShow"
></div>
<div
class="box"
:class="slideDirection"
:style="[
boxStyle,
{
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({
position: {
type: String,
default: 'bottom',
},
radius: {
type: String,
default: '10',
},
width: {
type: String,
default: 'auto',
},
height: {
type: String,
default: 'auto',
},
bgColor: {
type: String,
default: '#fff',
},
duration: {
type: String,
default: '500',
},
isMask: {
type: Boolean,
default: true,
},
maskCloseAble: {
type: Boolean,
default: true,
},
maskBgColor: {
type: String,
default: 'rgba(0, 0, 0, 0.5)',
},
zIndex: {
type: Number,
default: 9999,
},
});
const show = defineModel('show', { type: Boolean, required: true });
const popupShow = ref(false);
const slideDirection = ref('');
const osName = ref(uni.getDeviceInfo().osName);
watch(
() => show.value,
(val) => {
if (val) {
popupShow.value = true;
switch (props.position) {
case 'top':
slideDirection.value = 'top-show';
break;
case 'bottom':
slideDirection.value = 'bottom-show';
break;
case 'center':
slideDirection.value = 'center-show';
break;
case 'left':
slideDirection.value = 'left-show';
break;
case 'right':
slideDirection.value = 'right-show';
break;
}
} else {
switch (props.position) {
case 'top':
slideDirection.value = 'top-hide';
break;
case 'bottom':
slideDirection.value = 'bottom-hide';
break;
case 'center':
slideDirection.value = 'center-hide';
break;
case 'left':
slideDirection.value = 'left-hide';
break;
case 'right':
slideDirection.value = 'right-hide';
break;
}
setTimeout(() => {
popupShow.value = false;
}, props.duration * 1 - 50);
}
}
);
const boxStyle = ref({});
onMounted(() => {
switch (props.position) {
case 'top':
boxStyle.value = {
top: 0,
bottom: 'auto',
left: 0,
width: '100%',
'border-bottom-left-radius': props.radius + 'px',
'border-bottom-right-radius': props.radius + 'px',
};
break;
case 'bottom':
boxStyle.value = {
top: 'auto',
bottom: 0,
left: 0,
width: '100%',
'border-top-left-radius': props.radius + 'px',
'border-top-right-radius': props.radius + 'px',
};
break;
case 'center':
boxStyle.value = {
width: props.width,
height: props.height,
top: '50%',
left: '50%',
'border-radius': props.radius + 'px',
};
break;
case 'left':
boxStyle.value = {
top: 0,
left: 0,
width: props.width,
height: '100%',
'border-top-right-radius': props.radius + 'px',
'border-bottom-right-radius': props.radius + 'px',
};
break;
case 'right':
boxStyle.value = {
top: 0,
right: 0,
width: props.width,
height: '100%',
'border-top-left-radius': props.radius + 'px',
'border-bottom-left-radius': props.radius + 'px',
};
break;
}
});
function clickMask() {
if (props.maskCloseAble) {
show.value = false;
}
}
</script>
<style lang="scss" scoped>
.mask {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
transition: all 0.5s ease-in-out;
}
.box {
position: fixed;
transform: translate(0);
transition: all 0.5s ease-in-out;
}
.bottom-show {
animation: bottomShowAnimation 0.5s ease-in-out;
}
.bottom-hide {
animation: bottomHideAnimation 0.5s ease-in-out;
}
@keyframes bottomShowAnimation {
0% {
transform: translateY(100%);
opacity: 0;
}
100% {
transform: translateX(0);
opacity: 1;
}
}
@keyframes bottomHideAnimation {
0% {
transform: translateY(0);
opacity: 1;
}
100% {
transform: translateY(100%);
opacity: 0;
}
}
.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;
}
}
.center-show {
animation: centerShowAnimation 0.5s ease-in-out forwards;
}
.center-hide {
animation: centerHideAnimation 0.5s ease-in-out forwards;
}
@keyframes centerShowAnimation {
0% {
transform: translate(-50%, -50%) scale(0);
opacity: 0;
}
100% {
transform: translate(-50%, -50%) scale(1);
opacity: 1;
}
}
@keyframes centerHideAnimation {
0% {
transform: translate(-50%, -50%) scale(1);
opacity: 1;
}
100% {
transform: translate(-50%, -50%) scale(0);
opacity: 0;
}
}
.left-show {
animation: leftShowAnimation 0.5s ease-in-out forwards;
}
.left-hide {
animation: leftHideAnimation 0.5s ease-in-out forwards;
}
@keyframes leftShowAnimation {
0% {
transform: translateX(-100%);
opacity: 0;
}
100% {
transform: translateX(0);
opacity: 1;
}
}
@keyframes leftHideAnimation {
0% {
transform: translateX(0);
opacity: 1;
}
100% {
transform: translateX(-100%);
opacity: 0;
}
}
.right-show {
animation: rightShowAnimation 0.5s ease-in-out forwards;
}
.right-hide {
animation: rightHideAnimation 0.5s ease-in-out forwards;
}
@keyframes rightShowAnimation {
0% {
transform: translateX(100%);
opacity: 0;
}
100% {
transform: translateX(0);
opacity: 1;
}
}
@keyframes rightHideAnimation {
0% {
transform: translateX(0);
opacity: 1;
}
100% {
transform: translateX(100%);
opacity: 0;
}
}
</style>
页面使用
<myPopup
v-model:show="xxPopup"
position="bottom"
radius="20"
bgColor="#fff"
:zIndex="200"
></myPopup>