小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。
uniapp 打造自用组件库 (四) 下沉式对话框
前言
本文将带领读者使用uniapp封装一些常用组件,方便日后开发时重复使用,当然文中封装的组件不可能适配所有应用场景,但是我希望读者可以跟着我的思路实现出来,然后可以在此基础上优化改进为自己合适的,本人一个前端小菜鸡,希望大佬们可以不吝赐教,也是对我的技术水平的提升
下沉式对话框
需求
前段时间,魅族发布了新的Flyme9,其中使用的一些新的组件风格非常友好,例如本文中实现的下沉式对话框,将对话框位置下沉到底部,方便大屏用户单手操作,并且动画优美,于是就用Uniapp实现了一个自用的,跨端表现效果良好,并且支持下滑关闭
效果展示
应用效果
应用代码
<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>