You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
244 lines
7.5 KiB
244 lines
7.5 KiB
<template>
|
|
<view class="you-touchbox" :class="{touchend:isTouchEnd}" @touchstart="onTouchStart"
|
|
@touchmove.stop.prevent="onTouchmove" @touchend="onTouchend"
|
|
:style="{top: top + distance + 'px',zIndex,height:maxTop*windowHeight+'px'}">
|
|
<view class="you-touchbox-content" :style="customStyle" @click.stop.prevent>
|
|
<view class="touch-line-box">
|
|
<view class="touch-line"></view>
|
|
</view>
|
|
<slot></slot>
|
|
|
|
</view>
|
|
</view>
|
|
</template>
|
|
|
|
<script>
|
|
|
|
export default {
|
|
name: "touchbox",
|
|
props: {
|
|
// 禁用滑动
|
|
disable: {
|
|
type: Boolean,
|
|
default: false
|
|
},
|
|
zIndex: {
|
|
type: [Number, String],
|
|
default: 100
|
|
},
|
|
// 自定义样式
|
|
customStyle: [String, Object],
|
|
// 自定义安全区域
|
|
customSafeArea: {
|
|
type: Object,
|
|
default () {
|
|
return {
|
|
h5Top: null,
|
|
wxTop: null,
|
|
bottom: null
|
|
}
|
|
}
|
|
},
|
|
// 最高top
|
|
maxTop: {
|
|
type: [Number, String],
|
|
default: 1
|
|
},
|
|
// 最低top
|
|
minTop: {
|
|
type: [Number, String],
|
|
default: 0.5
|
|
},
|
|
// 初始top
|
|
initTop: {
|
|
type: [Number, String],
|
|
default: 'min'
|
|
},
|
|
// 自动复位
|
|
auto: {
|
|
type: Boolean,
|
|
default: true
|
|
},
|
|
// 最高最低限制
|
|
limit: {
|
|
type: Boolean,
|
|
default: true
|
|
}
|
|
},
|
|
data() {
|
|
return {
|
|
windowHeight: null, // 可使用区域高度
|
|
windowTop: null,
|
|
touchStartY: null, // 开始滑动时的Y轴坐标
|
|
top: null, // 上拉框top
|
|
max: null, // maxTop
|
|
min: null, // minTop
|
|
distance: 0, // 滑动距离
|
|
isTouchEnd: false, // 是否滑动结束Flag
|
|
boundary: null, // 规定盒子复位为最大或最小状态的分界线,默认为最大最小状态的中间
|
|
startTime: null, // 开始滑动时间
|
|
};
|
|
},
|
|
mounted() {
|
|
let {
|
|
windowHeight, // 可使用窗口高度
|
|
windowTop // 可使用窗口的顶部位置
|
|
} = uni.getSystemInfoSync()
|
|
this.windowHeight = windowHeight
|
|
this.windowTop = windowTop
|
|
let {
|
|
h5Top,
|
|
wxTop,
|
|
bottom
|
|
} = this.customSafeArea
|
|
// H5自定义安全区域
|
|
// #ifndef MP
|
|
if (h5Top) {
|
|
this.windowHeight -= h5Top
|
|
windowTop += h5Top
|
|
}
|
|
// #endif
|
|
if (bottom) this.windowHeight -= bottom
|
|
// 上拉框最大高度top
|
|
if (this.maxTop <= 1) this.max = this.windowHeight * (1 - +this.maxTop)
|
|
else this.max = +this.maxTop
|
|
// 上拉框最小高度top
|
|
if (this.minTop <= 1) this.min = this.windowHeight * (1 - +this.minTop)
|
|
else this.min = this.windowHeight - (+this.minTop)
|
|
// 初始的上拉框top
|
|
if (['min', 'max'].includes(this.initTop)) this.top = this.initTop === 'min' ? this.min : this.max
|
|
else if (this.initTop <= 1) this.top = this.windowHeight * (1 - +this.initTop)
|
|
else this.top = +this.initTop
|
|
// 微信自定义安全区域
|
|
// #ifdef MP
|
|
if (wxTop) {
|
|
this.max += wxTop === 'menuBtn' ? uni.getMenuButtonBoundingClientRect().bottom + 10 : wxTop
|
|
this.top += wxTop
|
|
}
|
|
// #endif
|
|
// H5自定义安全区域
|
|
// #ifndef MP
|
|
this.max += windowTop
|
|
this.min += windowTop
|
|
this.top += windowTop
|
|
// #endif
|
|
this.boundary = (this.max + this.min) / 2 // 规定盒子复位为最大或最小状态的分界线,默认为最大最小状态的中间
|
|
},
|
|
methods: {
|
|
setBottom(value) {
|
|
if (this.auto) this.isTouchEnd = true
|
|
if (value <= 1) this.top = this.windowHeight * (1 - +value)
|
|
else this.top = (this.windowHeight - +value)
|
|
// #ifndef MP
|
|
this.top += this.windowTop
|
|
// #endif
|
|
},
|
|
onTouchStart(e) {
|
|
if (this.disable) return
|
|
this.isTouchEnd = false // 关闭滑动结束Flag,作用为是否开启上拉框的过渡效果
|
|
// #ifndef APP-NVUE
|
|
this.top = e.currentTarget.offsetTop // 手指触碰到上拉框时,将上拉框top设置为上拉框当前的top
|
|
// #endif
|
|
// #ifdef APP-NVUE
|
|
this.touchStartY = e.touches[0].screenY // 此次滑动开始时的Y轴坐标
|
|
// #endif
|
|
// #ifndef APP-NVUE
|
|
this.touchStartY = e.touches[0].clientY // 此次滑动开始时的Y轴坐标
|
|
// #endif
|
|
this.startTime = Date.now() // 设置滑动开始时间
|
|
},
|
|
onTouchmove(e) {
|
|
if (this.disable) return
|
|
// #ifdef APP-NVUE
|
|
let distance = e.touches[0].screenY - this.touchStartY // 滑动的距离
|
|
// #endif
|
|
// #ifndef APP-NVUE
|
|
let distance = e.touches[0].clientY - this.touchStartY // 滑动的距离
|
|
// #endif
|
|
if (this.limit) { // 是否开启高度限制
|
|
let nowTop = this.top + distance // 此次滑动的上拉框top值
|
|
if (nowTop < this.max || nowTop > this.min) {
|
|
// 如果滑动时上拉框top小于最大高度top就让上拉框top等于最大高度top
|
|
// 如果滑动时上拉框top大于最小高度top就让上拉框top等于最小高度top
|
|
this.top = nowTop < this.max ? this.max : this.min
|
|
this.distance = 0 // 滑动距离归零
|
|
// #ifdef APP-NVUE
|
|
this.touchStartY = e.touches[0].screenY // 此次滑动开始时的Y轴坐标
|
|
// #endif
|
|
// #ifndef APP-NVUE
|
|
this.touchStartY = e.touches[0].clientY // 此次滑动开始时的Y轴坐标
|
|
// #endif
|
|
return
|
|
}
|
|
}
|
|
this.distance = distance // 更新滑动距离
|
|
},
|
|
onTouchend(e) {
|
|
if (this.disable) return
|
|
this.top = this.top + this.distance // 更新上拉框top
|
|
if (this.auto) { // 是否开启自动复位
|
|
this.isTouchEnd = true // 开启滑动结束Flag
|
|
let time = (Date.now() - this.startTime) / 1000 // 滑动时间为 滑动结束时间 - 滑动开始时间
|
|
let speed = Math.abs(this.distance) / time // 滑动速度为 滑动距离/滑动时间
|
|
if (speed > 800) { // 如果速度大于800px/秒
|
|
this.top = this.distance > 0 ? this.min : this.max // 根据滑动方向设置top的值
|
|
// return this.distance = 0 // 滑动距离归零
|
|
} else {
|
|
|
|
if (this.top < this.boundary) this.top = this.max // 如果上拉框top小于分界线就让上拉框top等于最大高度top
|
|
else this.top = this.min // 如果上拉框top大于分界线就让上拉框top等于最小高度top
|
|
}
|
|
}
|
|
// 滑动结束时top信息
|
|
this.$emit('get-end-detail', {
|
|
minTop: this.min,
|
|
maxTop: this.max,
|
|
curTop: this.top
|
|
})
|
|
this.distance = 0 // 滑动距离归零
|
|
}
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
.you-touchbox {
|
|
position: fixed;
|
|
left: 0;
|
|
right: 0;
|
|
bottom: 0;
|
|
}
|
|
|
|
.touchend {
|
|
transition-property: top;
|
|
transition-duration: .6s;
|
|
}
|
|
|
|
.you-touchbox-content {
|
|
/* #ifdef APP-NVUE */
|
|
flex: 1;
|
|
/* #endif */
|
|
/* #ifndef APP-NVUE */
|
|
height: 100%;
|
|
/* #endif */
|
|
background-color: #fff;
|
|
}
|
|
|
|
|
|
.touch-line-box {
|
|
padding: 5px 0 10px;
|
|
/* #ifdef APP-NVUE */
|
|
align-items: center;
|
|
/* #endif */
|
|
}
|
|
|
|
.touch-line {
|
|
/* #ifndef APP-NVUE */
|
|
margin: 0 auto;
|
|
/* #endif */
|
|
width: 45px;
|
|
height: 5px;
|
|
border-radius: 25px;
|
|
background: rgba(51, 51, 51, 0.2);
|
|
}
|
|
</style>
|
|
|