在swiper中使用长页面,以及嵌套多个swiper时滑动卡顿、无法滑动的问题。

本文介绍如何使用Swiper实现包含高度不确定长页面的滑动效果,解决内部轮播图锁定及滑动不流畅等问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言


一般而言,swiper的应用场景大多是两种:

  1. 满屏切换的H5页面
  2. pc&移动端各种样式的轮播图

但有的时候,面对奇怪的需求,我们需要改变,甚至让swiper实现一些无法实现的功能。

需求


近期接到一个h5项目,主体头部是一个选项卡,对应两个子页面,每个子页面的第一屏为一个满屏的kv,监测到向下滑动时平滑过渡到第二屏,而第二屏是一个长页面

思路


首先,要做滑动体验友好的抵抗/回弹效果,首先想到的是iScroll和Swiper,考虑到长页面中有其他的轮播、灯箱部分,最后选择使用Swiper做这次的项目。

疑问


之前并没有接触过slide高度不同的情况,就算有时做轮播,图片的尺寸不同,slide的宽高也是固定的,里边图片的部分添加如下css控制图片不变形。

width:100%;
height:auto;

更不要说某个slide是一个高度不确定的长页面了,直接给slide一个超出一屏的高度,是无法正常滑动的。

当然,如果把第二屏的长页面也写成满屏,加上overflow:hidden让页面在一屏里滚动,同时:-webkit-overflow-scrolling:touch提升一些用户的滑动体验,理论上是可以达到需求的效果,但是考虑如下结构:

<div class="swiper-container" id="swiper">
    <div class="swiper-wrapper">
        <div class="swiper-slide" id="kv"></div>
        <div class="swiper-slide" id="long">
            <div class="scroll-part"></div>
        </div>
    </div>
</div>

假设#kv为第一屏的满屏slide,#long为第二屏的长页面,.scroll-part#long内与之宽高相同的一个可内部滚动的div,那么当.scroll-part滑动到最顶部(同时也是#long应该触发slide切换事件的位置)时,并不会如预期那样由#long向上切换到了#kv,而是.scroll-part#long发生黏连,一起向下移动并漏出了浏览器的背景,如果此时松开手指,再次同向滑动,才会发生正常的slide切换。

同时考虑到-webkit-overflow-scrolling在部分安卓机上的表现并不如人意,故不采用此种方法。

那么,怎么用swiper来开发一个包含不定长度slide的项目?


过程


网上并没有类似的案例,不过幸运的是在github上,swiper的原作者给出了一种解决方案

var swiper = new Swiper('#swiper', {
    direction: 'vertical',
});
var startScroll, touchStart, touchCurrent;
swiper.slides.on('touchstart', function (e) {
    startScroll = this.scrollTop;
    touchStart = e.targetTouches[0].pageY;
}, true);
swiper.slides.on('touchmove', function (e) {
    touchCurrent = e.targetTouches[0].pageY;
    var touchesDiff = touchCurrent - touchStart;
    var slide = this;
    var onlyScrolling = 
            ( slide.scrollHeight > slide.offsetHeight ) && //allow only when slide is scrollable
            (
                ( touchesDiff < 0 && startScroll === 0 ) || //start from top edge to scroll bottom
                ( touchesDiff > 0 && startScroll === ( slide.scrollHeight - slide.offsetHeight ) ) || //start from bottom edge to scroll top
                ( startScroll > 0 && startScroll < ( slide.scrollHeight - slide.offsetHeight ) ) //start from the middle
            );
    if (onlyScrolling) {
        e.stopPropagation();
    }
}, true);

不过,这样只是解决了在一个长页面的slide里可以正常的上下滑动,由于这个项目中,长页面里还有几个横向的轮播,我又发现了很奇怪的现象,
1. 当且仅当长页面滑动到最顶端或者最底端时,内部的横向swiper才能正常滚动,否则是处于“锁定”的状态,
2. 在上下滑动的时候,如果touchstart的区域是横向swiper的所在的位置,此时的窗口可视区域和长页面会发生黏连,导致页面无法滚动。

打印上面代码中的关键所在onlyScrolling发现,只有在长页面的顶端或者底端时,值才是false,从而得出结论:

onlyScrolling为true时,滑动事件被阻止向内部swiper冒泡,导致内部swiper行为异常

对代码进行修改:

var swiper = new Swiper('#swiper', {
     direction: 'vertical'
 })
 var startScroll, //开始滚动时slide的scrollTop
     touchStart, //滑动开始鼠标y坐标
     touchCurrent,//滑动实时鼠标y坐标
     ifContains;//是否包含内部swiper
 swiper.slides.on('touchstart', function (e) {

     startScroll = this.scrollTop;
     touchStart = e.targetTouches[0].pageY;
     var target = e.target;

     var contains_top = $.contains(document.getElementById('swiper_inner1'), target);
     var contains_middle = $.contains(document.getElementById('swiper_inner2'), target);
     var contains_bottom = $.contains(document.getElementById('swiper_inner3'), target);

    //swiper_inner1、swiper_inner2、swiper_inner3为内部swiper

     if (contains_top || contains_middle || contains_bottom) {
         ifContains = true
     }
 }, true);
 swiper.slides.on('touchmove', function (e) {
     touchCurrent = e.targetTouches[0].pageY;
     var touchesDiff = touchCurrent - touchStart;//鼠标滑动y坐标差值
     var slide = this;
     var onlyScrolling =
         (slide.scrollHeight > slide.offsetHeight) &&
         (
             (touchesDiff < 0 && startScroll === 0) ||
             (touchesDiff > 0 && startScroll === (slide.scrollHeight - slide.offsetHeight)) ||
             (startScroll > 0 && startScroll < (slide.scrollHeight - slide.offsetHeight))
         );
     if (onlyScrolling && !ifContains) {
         e.stopPropagation();
     }
 }, true);
 swiper.slides.on('touchend', function (e) {
     ifContains = false;
 }, true)

由于后期需求的更改,长页面内的swiper仅支持点击按钮切换slide,所以上面的问题2也就不再是问题了,所以代码没有保留,主体思路是监听touch事件的x坐标,判断是横向滑动且滑动区域在内部swiper上,强制赋值onlyScrolling

此时如果还是存在垂直滑动不流畅的情况,可以考虑适时的将父swiper禁止切换slide,这样就不会影响内部的滑动,同时将body固定,模拟原生APP体验:

var LONG_SCROLLTOP = $('#long').get(0).scrollTop;

if(LONG_SCROLLTOP < 100){
    $('#long').removeClass('swiper-no-swiping');
}else{
    $('#long').addClass('swiper-no-swiping');
}

// .swiper-no-swiping是swiper插件的配置项,作用为禁止切换slide,详情参考swiper API

if(LONG_SCROLLTOP > 800){
    $('body').css('position','static');
}else{
    $('body').css('position','fixed');
}


结语


总之没想到用swiper写长页面这么多坑,其实最外层完全可以手写一个类似swiper切换的滑动效果,可能反而会更节省时间。以后拿到项目不能第一反应就是用库、插件,仔细分析需求与可用工具的契合度,酌情选择开发工具,否则可能会适得其反。

如有错误,欢迎指正!

<think>好的,我现在需要解决用户在UniApp中实现swiper组件上下滑动效果的问题。首先,我要回忆一下UniApp的swiper组件默认行为。根据官方文档,swiper通常是水平滚动的,用于轮播图等场景。用户希望改成上下滑动,也就是垂直方向滚动。 接下来,我需要确认swiper组件是否有垂直方向的属性。查阅资料发现,swiper组件有一个vertical属性,设置为true可以让滑动方向变为垂直。这应该是解决问题的关键。例如,在代码中添加vertical="true"或者:vertical="true"(如果使用动态绑定)。 然后,考虑可能的样式调整。swiper-item的高度可能需要设置为100%以确保每个滑块占满整个容器。否则,可能会出现滑动顺畅或者显示全的问题。需要检查样式代码,比如设置swiperswiper-item的高度为100vh或者固定高度,具体取决于页面布局。 另外,用户提到的参考引用中有提到滑动卡顿和自动轮播的问题。比如引用[1]中提到,快速滑动可能导致问题,可以通过使用@animationfinish代替@change事件来解决。这可能与滑动动画完成后的回调有关,需要确保在动画结束后再进行下一次操作,避免冲突。 引用[2]提到在swiper中包含video滑动流畅,这可能和组件渲染性能有关。虽然用户当前的问题是包含视频,但优化建议仍然有参考价值。比如,减少swiper-item的数量,或者使用条件渲染来动态加载内容,以提升性能。 还需要考虑用户在实现上下滑动可能遇到的其他问题。比如,Android和iOS平台可能有同的表现,需要测试多端兼容性。此外,手势冲突也是需要注意的地方,确保上下滑动会与其他手势(如页面滚动)产生冲突。 最后,生成相关问题要围绕用户可能进一步遇到的难点,比如性能优化、事件处理、多内容适配等。例如,如何处理滑动过程中的卡顿,或者如何实现自动轮播同允许手动滑动,这些都是常见的后续问题。</think>在UniApp中实现swiper组件的垂直滑动效果,可以通过以下步骤实现: ### 解决方案 1. **启用垂直模式** 在`<swiper>`标签中添加`vertical`属性,设置滑动方向为垂直: ```html <swiper vertical="true" :style="{height: '100vh'}"> <swiper-item><view class="item">第1屏</view></swiper-item> <swiper-item><view class="item">第2屏</view></swiper-item> </swiper> ``` 2. **样式配置要点** - 必须为swiper容器设置确定高度(如100vh) - 建议给swiper-item设置`height: 100%` ```css .item { height: 100%; display: flex; justify-content: center; align-items: center; } ``` 3. **性能优化** 当包含复杂内容(如视频),建议: - 使用`@animationfinish`替代`@change`事件避免快速滑动异常[^1] - 设置`circular="false"`关闭循环滑动提升性能 - 通过`current`参数控制当前页 ### 注意事项 - iOS平台需要设置`easing-function="default"`保证流畅度 - 安卓端建议限制`swiper-item`数量(超过5个) - 嵌套视频使用`uni.createVideoContext`控制播放状态[^2]
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值