目标功能
在开发微信小程序时,我需要实现以下功能:
- 当用户向上滚动屏幕时,底部区域
bottom-area
要显示。 - 当用户向下滚动屏幕时,底部区域
bottom-area
要隐藏。
最初的实现方法
最初,我通过监听滚动事件来实现隐藏功能。当检测到用户向下滚动时,隐藏 bottom-area
,否则显示 bottom-area
。代码如下:
const scrollNew = ref<number>(0);
const scrollOld = ref<number>(0);
const isScrollDown = computed(() => scrollNew.value > scrollOld.value);
const isHidden = computed(() => isScrollDown.value);
onPageScroll((e) => {
scrollOld.value = scrollNew.value;
scrollNew.value = e.scrollTop;
});
遇到的问题
在 iOS 设备上,当用户滚动到页面底部并继续下拉时,会触发边界滚动橡皮筋效果,导致页面回弹。这种回弹会改变滚动状态 (回弹时向上滚动),使得 bottom-area
再次显示,违背了我们的初衷。
具体问题如下:
- 当用户滚动到底部并且继续下拉时,页面会回弹,导致
isScrollDown
状态改变。 - 这种状态改变使得
bottom-area
意外显示。
解决方案: 对底部区域进行特别判断
-
获取设备窗口高度:
使用uni.getSystemInfoSync
获取窗口高度,存储在变量viewportHeight
中。 -
实现滚动事件处理函数:
在滚动事件处理函数中,使用uni.createSelectorQuery
获取滚动位置信息,并判断是否滚动到底部。如果到达底部,隐藏bottom-area
;否则,根据滚动方向决定是否隐藏。
详细步骤
步骤一:获取窗口高度
使用 uni.getSystemInfoSync
获取设备的系统信息,包括窗口高度:
const systemInfo = uni.getSystemInfoSync();
const viewportHeight = systemInfo.windowHeight;
步骤二:实现滚动事件处理函数
在滚动事件处理函数中,使用 uni.createSelectorQuery
获取滚动位置信息,并判断是否滚动到底部:
const handlePageScroll = debounce((e: any) => {
scrollOld.value = scrollNew.value;
scrollNew.value = e.scrollTop;
uni.createSelectorQuery().selectViewport().scrollOffset((res) => {
if (res.scrollTop + viewportHeight >= res.scrollHeight) {
isHidden.value = true;
} else {
isHidden.value = isScrollDown.value;
}
}).exec();
}, 50);
debounce()
是消抖函数
完整代码示例
<template>
<view class="forumdetailLayout">
<!-- 底部区域: 评论 点赞 上下页切换 -->
<view class="bottom-area" :class="{ hidden: isHidden }">
<wd-icon name="arrow-left" size="40rpx"></wd-icon>
<view class="edit">我有话要说...</view>
<view class="like">
<wd-icon name="thumb-up" size="40rpx"></wd-icon>
<view class="like-count">{{ forumDetail.likeCount }}</view>
</view>
<wd-icon name="arrow-right" size="40rpx"></wd-icon>
</view>
</view>
</template>
<script lang="ts" setup>
import { debounce } from "lodash-es"; // 引入 lodash 的 debounce 函数
const scrollNew = ref<number>(0);
const scrollOld = ref<number>(0);
const isHidden = ref<boolean>(false);
const isScrollDown = computed(() => scrollNew.value > scrollOld.value);
// 获取视口高度
const systemInfo = uni.getSystemInfoSync();
const viewportHeight = systemInfo.windowHeight;
// 添加debounced处理函数
const handlePageScroll = debounce((e: any) => {
scrollOld.value = scrollNew.value;
scrollNew.value = e.scrollTop;
uni
.createSelectorQuery()
.selectViewport()
.scrollOffset((res: any) => {
if (res.scrollTop + viewportHeight >= res.scrollHeight) {
isHidden.value = true;
} else {
isHidden.value = isScrollDown.value;
}
})
.exec();
}, 20);
onPageScroll(handlePageScroll);
onReachBottom(() => {
isHidden.value = true;
});
</script>
<style lang="scss" scoped>
.forumdetailLayout {
.bottom-area {
position: fixed;
bottom: 0;
z-index: 100;
box-sizing: border-box;
display: flex;
align-items: center; /* 中心对齐 */
justify-content: center; /* 在子元素之间均匀分布 */
width: 100%;
padding: 17rpx 30rpx env(safe-area-inset-bottom) 30rpx;
background-color: $background-color;
transition: transform 0.3s;
.edit {
flex: 1; /* 占据剩余空间 */
padding: 17rpx;
margin-right: 10rpx; /* 右边距 */
color: $text-color-grey;
background-color: white;
border-radius: 24rpx;
}
.like {
display: flex;
flex-direction: column;
align-items: center; /* 内部中心对齐 */
margin-right: 10rpx; /* 右边距 */
}
}
.bottom-area.hidden {
transform: translateY(100%);
}
}
</style>