uniapp 弹出层

287 阅读3分钟

参数

参数说明示例
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>