起因
我司最近在做一個H5,有一個模擬微信對話框的需求,具體需求如下。
- 對話內容固定,但需要一句一句顯示
- 對話內容超過一屏後,需要使對話內容上移
- 對話內容結束後,用戶可以上下滑動對話框,查看詳細對話內容
- 圖中灰色頭像表示獲取的用戶頭像
示例: 請看第二屏
初步設想
- 使對話內容一句一句顯示,腦子裏立馬閃現出setInterval定時器。
- 對話內容超過一屏,使對話內容上移,當然是改變父元素的scrollTop值啦
- 用戶可以上下滑動對話框,就類似於滾動條效果,設置父元素高度並且 overflow:hidden,子元素高度auto即可。
- 獲取用戶頭像,這個薛微複雜,留做下一篇文章。
遇到問題
局部滾動效果,以上想法(設置父元素高度並且 overflow:hidden)在PC端可以正常滑動,但 在移動端失效。
這種寫法,單獨寫沒有問題,但是IOS端出現卡頓現象,可以添加 -webkit-overflow-scrolling:touch; 解決。
但是,我司的H5頁面使用的swiper製作,大概是這個有一些影響,用戶滑動屏幕首先觸發了swiper的事件。(僅做設想,後續做進一步實踐)
於是在網上查了幾番,有以下幾種解決方法
- 用戶在解發touchmove事件時,改變元素的transform值
- 使用iscroll.js
- 使用swiper
改變元素的transform值
改變元素的transform值,需要判斷用戶的滑動方向。
判斷滑動方向時,先了解兩個事件
- touchstart :用戶手指按在屏幕上時觸發
- touchmove:用戶滑動屏幕時觸發
瞭解了這兩個事件,我們可以在用戶觸發touchstart事件時,記錄手指位置,在touchmove記錄獲取手指最後停留的位置
判斷 最後停留位置 - 初始位置= pageY- startY = 即用戶滑動方向
(pageY-startY)爲正數時,說明用戶向下滑動;爲負數時,說明用戶向上滑動。
$(".message-wrapper").on("touchstart", function (e) {
startY = e.type === 'touchmove' ? e.targetTouches[0].pageY : e.pageY;
})
$(".message-wrapper").on("touchmove", function (e) {
pageY = e.type === 'touchmove' ? e.targetTouches[0].pageY : e.pageY;
})
使用iscroll.js
網上有很多關於iscroll的資料,但是我查了一下官方的github,最近的更新在5年前,果斷不敢用~
使用swiper
查了一番,swiper裏的 swiper-scrollbar可以完美的實現這一功能,單獨寫這一功能,在真機測試沒有問題。然後移入到我們的項目中。出現以下幾個問題
局部滾動後的slide元素不顯示
分析原因
出現這個問題的原因,是由於我司的H5項目也是由swiper製作,這意味着每一屏就相當於一個slide,當添加用swiper完成的局部滾動時,會造成後面父元素的slide元素不顯示。
解決辦法
這涉及到多個swiper嵌套使用的問題,具體修改如下:
-
當頁面存在多個swiper,初始化時,儘量避免使用一樣的類名,如 .swiper-container,每個swiper有它單獨的類名
<div class="swiper-container main-swiper"> //父元素swiper <div class="swiper-wrapper"> <div class="swiper-slide slide1"></div> <div class="swiper-slide slide2"> <div class="swiper-container message-warp"> //子元素swiper <div class="swiper-wrapper message-wrapper"> <div class="swiper-slide message-slide"></div> </div> </div> </div> <div class="swiper-slide slide3"></div> </div> </div> //-------------------------------------------------------------swiper初始化
-
如果類名分開,父元素後續slide元素依然無法顯示
將子元素的初始化,寫在父元素初始化之前,加載時,優先初始化子元素swiper//初始化子swiper var scrollSwiper = new Swiper('.message-warp', { observer: true, observeParents: false, scrollbar: '.swiper-scrollbar', direction: 'vertical', slidesPerView: 'auto', mousewheelControl: true, freeMode: true, }) var swiper = new Swiper('.main-swiper', { direction: 'vertical', touchRatio: 0.5, loop: false, on: { init: function () { swiperAnimate(this); }, slideChangeTransitionEnd: function (e) { swiperAnimate(this) } } });
- 以上方法都不能使後續 元素顯示
swiper運行時,會先給元素添加visiblity:hidden;使元素隱藏,只給當前頁的visiblity設置爲visible;而swiper中,改元素顯示狀態的依據就是swiper-slide-active;
swiper默認給當前的slide添加swiper-slide-active類名。當頁面中存在swiper嵌套時,父元素的當前slide會添加該類名,子元素的當前slide也會添加該類名。
這樣當用戶滑出父元素的當前slide時父元素的swiper-slide-active被移除,而子元素的swiper-slide-active類名並沒有移除,造成swiper混亂,所以父元素後續slide的元素會無法顯示
解決辦法
我的做法是在父元素切換slide後,判斷頁面中swiper-slide-active的個數,如果存在一個以上,則說明子元素的類名沒有移除。
手動將子元素的swiper-slide-active類名移除即可。
暫時還沒有想到更好的方法,如果你有更好的方法,歡迎一起討論。
if ($(".swiper-slide-active").length == 2) {
$(".message-slide").removeClass("swiper-slide-active")
}