在swiper中使用長頁面,以及巢狀多個swiper時滑動卡頓、無法滑動的問題。
前言
一般而言,swiper的應用場景大多是兩種:
- 滿屏切換的H5頁面
- 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
#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切換的滑動效果,可能反而會更節省時間。以後拿到專案不能第一反應就是用庫、外掛,仔細分析需求與可用工具的契合度,酌情選擇開發工具,否則可能會適得其反。
如有錯誤,歡迎指正!