一、先看一下弹框实现效果

二、对话框跟随手势下拉实现
从上面的实现效果图中可以发现,弹窗会跟随手势向下移动进行同步移动,想要实现这个功能,需要获取我们的手势向下的移动距离,然后再把这个距离设置给弹窗,作为向下隐藏的距离。
即:弹窗向下移动的距离 = 手势向下移动的距离
1.手势向下移动距离的计算公式
手势向下移动的距离 = 手势当前向下移动到的Y坐标 - 手势开始移动的Y坐标
在uniapp
中提供了可以兼容多端的监听页面上元素的触摸方法:
1.手指开始触摸元素
element.touchstart:
element.touchstart(options: Object): Promise<void>
2.手指触摸元素后移动。
element.touchmove:
element.touchmove(options: Object): Promise<void>
3.手指结束触摸元素
element.touchend:
element.touchend(options: Object): Promise<void>
options 字段定义如下:
字段 | 类型 | 必填 | 默认值 | 说明 |
---|---|---|---|---|
touches | array | 是 | - | 触摸事件,当前停留在屏幕中的触摸点信息的数组 |
changedTouches | array | 是 | - | 触摸事件,当前变化的触摸点信息的数组 |
2.获取手势向下移动的距离
下面将使用选项api 来实现相应功能,目的也是为了同时兼容 vue2
和vue3
版本。
js
<template>
<uni-popup ref="popup" type="bottom">
<view class="content"
@touchstart="touchstart"
@touchmove="touchmove"
>
...
</view>
</uni-popup>
</template>
<script>
export default {
data() {
return {
// 手势向下移动距离
moveDistance: 0
}
},
methods: {
// 开始触摸元素
touchstart(e) {
this.startY = e.changedTouches[0].clientY;
this.startIme = Date.now()
this.toumoveTime = Date.now()
},
// 触摸元素后移动
touchmove(e) {
const clientY = e.changedTouches[0].clientY;
// 忽略上划
if (clientY < this.startY) {
return
}
// 下滑,计算手势移动距离
this.moveDistance = clientY - this.startY
},
}
}
</script>
通过e.changedTouches[0].clientY
可获取当前屏幕触摸的Y坐标。
3.弹框向下隐藏同步手指向下触摸移动距离
可以通过css
属性transform
来设置弹窗向下移动距离,来达到隐藏弹窗的效果。
transform: `translateY(${this.moveDistance}px)`
js
<script>
export default {
data() {
return {
// 手势向下移动距离
moveDistance: 0
}
},
computed: {
contentStyle() {
return {
transform: `translateY(${this.moveDistance}px)`,
}
}
},
}
</script>
<template>
<uni-popup ref="popup" type="bottom">
<view class="content"
...
:style="contentStyle"
>
...
</view>
</uni-popup>
</template>
三、关闭弹窗时机
上面已经完成了弹窗的随手势进行移动,下面将确定弹窗隐藏的时机,即满足条件调用this.$refs.popup.close()
方法关闭弹窗。经过我的一番测试,发现抖音 的评论区弹窗具有两种关闭时机,分别为距离底部一定距离时 和手指开始、移动、结束触摸元素这三个事件快速完成时都会关闭弹窗。
关闭弹窗我们可以在元素上绑定
touchend
方法,当触发触摸结束方法时进行相关计算。
1.计算距离底部距离
距离底部距离 = 窗口高度 - 当前手指触摸结束的Y坐标
js
touchend(e) {
// 触摸结束Y坐标
const clientY = e.changedTouches[0].clientY;
// 获取窗口高度
const { windowHeight } = uni.getSystemInfoSync();
// 距离底部距离
const distance = windowHeight - clientY
}
2.计算手势完成时间
js
touchstart(e) {
this.startIme = Date.now()
},
touchend(e) {
// 手势完成时间(毫秒)
const toumoveTime = Date.now() - this.startIme
},
3.根据计算值,完成弹窗关闭逻辑
js
touchend(e) {
// 触摸结束Y坐标
const clientY = e.changedTouches[0].clientY;
// 获取窗口高度
const { windowHeight } = uni.getSystemInfoSync();
// 距离底部距离
const distance = windowHeight - clientY
// 滑动时间(毫秒),小于某个时间则自动close
const toumoveTime = Date.now() - this.startIme
// 必须触发了touchmove方法
if (this.moveDistance > 0 && (distance < this.bottomCloseValue || toumoveTime < this.moveCloseTime)) {
this.close();
} else {
this.moveDistance = 0;
}
},
当结束触摸时,如果满足设置的距离 和设置的触摸完成时间,则关闭弹窗;反之,则复原弹窗。
四、打开全屏
打开全屏主要是计算可用视口高度,并把弹窗内容高度设置为可用视口高度。
js
toggleFullScreen() {
if (this.isFullScrren) {
// 关闭全屏,高度设为内容高度
this.height = this.contentHeight
} else {
// 开启全屏,设为视口高度
this.height = this.windowHeight
}
this.isFullScrren = !this.isFullScrren
},
同时兼容ios微信小程序和App端顶部和底部安全区域适配
适配iphone
手机顶部和底部安全区域
js
const {
safeArea,
windowHeight,
screenHeight,
osName
} = uni.getSystemInfoSync();
// 适配顶部和底部安全区域, safeArea.height安全区域的高度,单位逻辑像素
const safeWindowHeight = windowHeight - (screenHeight - safeArea.bottom)
// 打开全屏的高度
this.windowHeight = safeWindowHeight
适配Android
手机app端顶部和底部安全区域
js
// #ifdef APP-PLUS
const {
windowHeight,
osName
} = uni.getSystemInfoSync();
if (osName === 'ios') {
// ios没设备
} else {
// android
打开全屏的高度
this.windowHeight = windowHeight
}
五、总结
以上实现主要有三部分内容:
1.弹窗跟随手势向下移动。
2.手势距离底部一定距离或者手势移动足够快关闭弹窗。
3.通过uni.getSystemInfoSync()
获取设备信息计算弹窗的全屏高度,兼容不同设备类型。
去体验插件:uniapp插件市场