小程序解决 iOS 边界滚动橡皮筋效果问题记录

目标功能

在开发微信小程序时,我需要实现以下功能:

  • 当用户向上滚动屏幕时,底部区域 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 意外显示。
解决方案: 对底部区域进行特别判断
  1. 获取设备窗口高度
    使用 uni.getSystemInfoSync 获取窗口高度,存储在变量 viewportHeight 中。

  2. 实现滚动事件处理函数
    在滚动事件处理函数中,使用 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>

### iOS 小程序页面滑动到底部后仍可拖动的原因 在 iOS 设备上,浏览器和操作系统的行为与其他平台有所不同。当用户滚动到页面底部时,iOS 的 Safari 浏览器允许用户继续拉动页面,这种行为被称为“橡皮筋效果”。这是由 iOS 系统自带的特性决定的,目的是为了提供更好的用户体验[^1]。 此外,在小程序开发过程中,如果未正确设置 `overflow` 属性或者使用了某些特定 CSS 属性(如 `-webkit-overflow-scrolling: touch`),可能会进一步加剧这一现象。该属性虽然提升了滚动性能,但也可能导致页面出现额外的空白区域或不可控的滚动行为[^4]。 --- ### 解决方案 以下是几种常见的解决方法: #### 方法一:通过 `scroll-view` 组件控制滚动范围 微信小程序提供了专门用于处理滚动场景的组件 `<scroll-view>`。可以通过将其作为页面最外层容器来限制滚动范围,并禁用不必要的滚动行为。 ```html <scroll-view scroll-y="{{true}}" style="height: 100vh;" bindscrolltolower="onReachBottom"> <!-- 页面内容 --> </scroll-view> ``` 上述代码中,设置了 `scroll-y` 属性为 `true` 来启用垂直方向的滚动功能;并通过绑定事件 `bindscrolltolower` 实现触底加载逻辑。这种方式能够有效避免页面超出边界后的无意义拖拽[^3]。 #### 方法二:CSS 隐藏多余滚动条并阻止溢出 对于不需要复杂交互的小程序界面来说,仅需简单调整样式即可解决问题。具体做法如下所示: ```css .forbid_iphoneSlide { width: 100%; height: 100vh; overflow-x: hidden !important; overflow-y: auto !important; /* 或者 hidden */ } ``` 此段代码强制规定 `.forbid_iphoneSlide` 类下的子元素不允许水平扩展屏幕宽度之外的部分显示出来,同时关闭纵向自动弹跳的效果。 #### 方法三:监听触摸事件动态干预 除了静态配置方式以外,还可以借助 JavaScript 动态捕获用户的操作动作,一旦检测到越界情况立即中断其执行流程。例如下面这段脚本实现了对顶部与底部边界的双重保护机制: ```javascript Page({ data: {}, handleTouchMove(e) { const scrollTop = this.selectComponent('.content').scrollTop; if (scrollTop === 0 && e.touches[0].pageY > e.changedTouches[0].pageY) { // 上拉停止 e.preventDefault(); } if (!this.data.reachedBottom && scrollTop + window.innerHeight >= document.body.scrollHeight) { // 下拉停止 e.preventDefault(); this.setData({ reachedBottom: true }); } }, }); ``` 需要注意的是这种方法可能会影响部分原生手势识别效率,因此应谨慎选用[^2]。 --- ### 总结 综上所述,针对 iOS 平台上存在的此类问题可以从多个角度出发寻找对策。推荐优先考虑利用官方支持的功能模块完成需求满足工作,比如采用 `<scroll-view>` 完成精细化管理;其次再辅以必要的视觉修饰手段优化最终呈现形态。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值