一般而言,swiper的应用场景大多是两种:
但有的时候,面对奇怪的需求,我们需要改变,甚至让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切换的滑动效果,可能反而会更节省时间。以后拿到项目不能第一反应就是用库、插件,仔细分析需求与可用工具的契合度,酌情选择开发工具,否则可能会适得其反。
如有错误,欢迎指正!