在 UniApp 开发中,swiper 组件默认提供了强大的滑动功能,但在实际应用中,我们经常需要限制用户在首页(索引 0)不能再向前(右滑),在尾页(最大索引)不能再向后(左滑)。

最终采用的优化方案:即时阻止 + 回弹兜底

我们最终采用的方案是结合了触摸事件的 实时干预 和 @change 事件的 保障回弹,以达到最佳的稳定性和用户体验。

通过在 <swiper-item> 上监听触摸事件,我们可以绕开 swiper 对事件的拦截,并在用户开始滑动时立即判断是否需要阻止。

  • 监听事件:

    • @touchstart="handleTouchStart"

    • @touchmove="handleTouchMove"

    • @touchend="disableTouch = false"

  • 核心逻辑 (handleTouchMove):

    • 记录滑动距离 diffX = currentX - startX。

    • 设置一个合理的 滑动阈值(如 10-15px),以过滤掉手指的微小抖动。

    • 在首页 (current === 0) 时: 如果 diffX > 阈值(尝试右滑),立即设置 disableTouch = true,阻止 swiper 继续接收手势。

    • 在尾页 (current === maxIndex) 时: 如果 diffX < -阈值(尝试左滑),立即设置 disableTouch = true。

2. 回弹兜底(最终保障)

为了应对 touchmove 拦截失败的极端情况,我们保留了 @change 事件中的回弹逻辑。

  • 监听事件: @change="changeSwiperType"

  • 核心逻辑 (changeSwiperType):

    • 在 @change 触发后,如果 newIndex 不符合边界规则(例如从尾页切换到了下一页),则立刻执行:

      1. disableTouch = true

      2. state.current = lastIndex(强制跳回原页)

      3. 使用 setTimeout 延迟解除 disableTouch,确保动画完成后恢复正常手势。

<script setup>

const disableTouch = ref(false); // 控制 Swiper 禁用滑动
const startPointX = ref(0);      // 记录触摸开始时的 X 坐标
const totalPage = 4;             // 假设您的 nearby.vue 有 4 个页面
const THRESHOLD = 15;            // 滑动阈值(防止微小抖动触发禁用)

const state = reactive({
    current: 0,
    all: [{}],
});

function handleTouchStart(e) {
    if (e.touches.length > 0) {
        startPointX.value = e.touches[0].clientX;
        disableTouch.value = false;
    }
}

/**
 * 触摸移动:根据滑动方向在边界页立即阻止 (核心逻辑)
 */
function handleTouchMove(e) {
    if (e.touches.length === 0) return;

    const currentX = e.touches[0].clientX;
    const diffX = currentX - startPointX.value; // > 0 为右滑,< 0 为左滑
    
    // 1. 当前在首页 (0):只允许向左滑
    if (state.current === 0) {
        // 尝试右滑 (diffX > 0) 且超过阈值
        if (diffX > THRESHOLD) {
            disableTouch.value = true;
        } else {
            disableTouch.value = false;
        }
        return;
    }

    // 2. 当前在尾页 (totalPage - 1):只允许向右滑
    if (state.current === totalPage - 1) {
        // 尝试左滑 (diffX < 0) 且超过阈值
        if (diffX < -THRESHOLD) {
            disableTouch.value = true;
        } else {
            disableTouch.value = false;
        }
        return;
    }
    
    // 非边界页,允许正常滑动
    disableTouch.value = false;
}



/**
 * Swiper 切换事件 (@change) - 作为回弹兜底
 */
function changeSwiperType(e) {
    const newIndex = e.detail.current;
    const lastIndex = state.current;
    
    const isSwipingRight = newIndex < lastIndex; // 索引变小 (向右滑/回退)
    const isSwipingLeft = newIndex > lastIndex;  // 索引变大 (向左滑/前进)

    // 1. 首页 (0) 发生意外右滑 -> 强制回弹
    if (lastIndex === 0 && isSwipingRight) {
        disableTouch.value = true; 
        state.current = 0;
        setTimeout(() => {
            disableTouch.value = false;
        }, 50); 
        return;
    }

    // 2. 尾页 (totalPage - 1) 发生意外左滑 -> 强制回弹
    if (lastIndex === totalPage - 1 && isSwipingLeft) {
        disableTouch.value = true; 
        state.current = lastIndex; 
        
        setTimeout(() => {
            disableTouch.value = false;
        }, 50); 
        return;
    }

    // 正常切换
    state.current = newIndex;
    handleTabChange(newIndex); // 保持原有逻辑
    disableTouch.value = false; 
}


</script>

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐