uniapp 打造自用组件库 (四) 下沉式对话框

842 阅读1分钟

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

uniapp 打造自用组件库 (四) 下沉式对话框

前言

本文将带领读者使用uniapp封装一些常用组件,方便日后开发时重复使用,当然文中封装的组件不可能适配所有应用场景,但是我希望读者可以跟着我的思路实现出来,然后可以在此基础上优化改进为自己合适的,本人一个前端小菜鸡,希望大佬们可以不吝赐教,也是对我的技术水平的提升

下沉式对话框

需求

前段时间,魅族发布了新的Flyme9,其中使用的一些新的组件风格非常友好,例如本文中实现的下沉式对话框,将对话框位置下沉到底部,方便大屏用户单手操作,并且动画优美,于是就用Uniapp实现了一个自用的,跨端表现效果良好,并且支持下滑关闭

image.png

效果展示

应用效果

image.png

应用代码
	<button type="default" @tap="isShow = true" >案例 退出登录</button>
	<Ydialog :show.sync="isShow" @change='change'>
			<view slot='title'>
				退出登录
			</view>
	</Ydialog>
export default {
		data() {
			return {
				isShow:false,
			}
		},
		methods: {
		
			change(type){
				if(type == 'confirm'){
					uni.showToast({
						title:'退出登录成功'
					})
					return
				}
			},
		}
	}

实现思路

首先制作一个蒙版放在最底层,然后上方放置view 为对话框 对话框中分为上下两部分,一部分放置提示信息,另一部分放置按钮,通过sync动态修改show,同时监听蒙版点击事件,监听对话框滑动事件,实现点击空白处,向下划动收起对话框,同时触发取消事件

完整实现代码

<template>
	<view class="make" @touchmove.stop.prevent @tap.stop="hidePopup" v-if="myshow"
		:style="{backgroundColor:client != '100%'?'rgba(0,0,0,0.3)':'rgba(0,0,0,0)'}">
		<view class="popup"
			:style="{transform:client != '100%'?'translateY('+client+'px)':'translateY(100%)',transition:!isTouch?'0.4s ease,transform 0.2s ease':'0.4s ease,transform 0s ease'}">
			<view class="box" @tap.stop @touchstart="touchstart" @touchmove="touchmove" @touchend="touchend">
				<view class="title">
					<slot name="title">
					</slot>
				</view>
				<view class="btns">
					<view @tap="tap('cancel')">
						<slot name="cancel">
							取消
						</slot>
					</view>
					<view style="margin-left: 30rpx;" @tap="tap('confirm')">
						<slot name="confirm">
							确定
						</slot>
					</view>
				</view>
			</view>

		</view>
	</view>
</template>
<script>
	export default {
		data() {
			return {
				myshow: false,
				clientY: 0,
				client: '100%',
				ismake: false,
				defaultNum: 0,
				isTouch: false
			};
		},
		props: {
			show: {
				type: Boolean,
				default: false
			}
		},
		watch: {
			show() {
				if (this.show) {
					this.myshow = true
					setTimeout(() => {

						this.client = 0
						this.defaultNum = 0
					}, 100)
				} else {
					this.hidePopup()

				}
			}
		},
		methods: {

			touchstart({
				touches
			}) {
				this.isTouch = true
				this.clientY = touches[0].clientY
				return
			},
			touchmove({
				touches
			}) {
				if (this.defaultNum == 0) {
					let num = (touches[0].clientY - this.clientY) + 0
					if (num > 0) {
						this.client = num
					}

				}
				return
			},
			touchend(end) {
				this.isTouch = false

				if (this.defaultNum == 0) {
					if (this.client >= 50) {
						this.hidePopup()
					} else {

						this.client = 0
						this.defaultNum = 0
					}
				}

				return
			},
			tap(type) {
				this.$emit('change', type)
				this.$emit('update:show', false)
			},
			hidePopup() {
				this.client = '100%'
				setTimeout(() => {
					this.$emit('update:show', false)
					this.myshow = false
				}, 200)
			},
		}
	}
</script>

<style lang="scss" scoped>
	.make {
		position: fixed;
		width: 100%;
		height: 100%;
		bottom: 0;
		right: 0;
		background-color: rgba(0, 0, 0, 1);
		transition: 0.2s;
		// background: linear-gradient(0deg, rgba(0, 0, 0, 0.2) 50%, rgba(0, 0, 0, 0) 90%);
		z-index: 9999;

		.popup {
			position: absolute;
			bottom: 0;
			height: 100%;
			width: 100%;
			display: flex;
			z-index: 9999;
			flex-direction: column;
			border-radius: 50rpx 50rpx 0 0;
			overflow: hidden;
			justify-content: flex-end;

			.box {
				margin: 60rpx 30rpx;

				border-radius: 30rpx;
				background-color: #fff;
				display: flex;
				flex-direction: column;

				.title {
					display: flex;
					justify-content: center;
					padding: 30rpx;
					font-size: 16px;
					font-weight: bold;
				}

				.btns {
					display: flex;
					padding: 30rpx;

					view {
						flex: 1;
						text-align: center;
						border-radius: 20rpx;
						padding: 25rpx 30rpx;
						background-color: #F9F9F9;
						color: #0f7ffc;
						font-weight: bold;

						&:active {
							background-color: #f2f2f2;
						}
					}
				}
			}

		}
	}
</style>