最近做一個H5項目,數據交互量比較大,很多頁面都是從后台拿過來數據做一個列表顯示,這自然就遇到了滾動。
剛開始我直接使用css做法,直接添加overflow: scroll;但在微信端用戶滑動會直接將整個頁面拖動,露出頂部的域名和底部的黑色背景。用戶反映體驗不好,要改……好吧,自己動手。但這並不是好改的,因為在React中都是構建的是虛擬DOM,直接操作DOM也會對性能有一定影響。這時候網上搜了一下,拿出一個解決方案,獻出部分代碼:
import React from 'react'; import { render } from 'react-dom'; import Router from './app/router'; import '../common/css/base.styl'; import '../common/css/waaStyle.styl'; import '../common/css/page.styl'; var divDom = document.createElement('div'); divDom.setAttribute('id', 'wrap'); document.body.appendChild(divDom); document.body.addEventListener("touchmove",function (e) { e.preventDefault(); },false); render(<Router />, divDom);
這是在React的根頁面下直接禁用touchmove事件,禁止用戶touchmove,可以達到不出現域名和黑色邊框的效果。
由於禁止touchmove事件,在需要列表滾動的地方,overflow:scroll;監聽不到touchmove了,滑動變得無效,怎么解決呢?我們可以在需要滾動的地方禁止冒泡事件:
//首先要獲取需要禁止冒泡事件的dom,由於React是構建虛擬的dom,可以這樣拿到dom:ref= {(ref) => {this.dom = ref}}; componentDidMount () { this.dom.addEventListener("touchmove",function (e) { e.stopPropagation(); },false); } //dom在頁面掛載完成后,禁止冒泡事件。
加上這樣的代碼后,就會發現,整個頁面仍然是禁止滑動,列表頁可以正常滾動,但是,當列表滾動到底的時候,用戶繼續滑動,整個頁面仍然會跟着滑動,又露出我們不想看到的部分,感覺好氣呀……
冷靜分析一下,這條路是走不通的,由於列表區域禁止了冒泡事件,那么用戶只要在列表區域滑動,那么你在body上做的禁止滑動就是沒有效果的!
萬般無奈下,我有搬來了我的老伙計:IScroll.js。之所以選他,因為他有一個非常有用的方法:refresh();我只要在componentDidMount中實例化Iscroll,並且在React數據更新后再次refresh就可以了。那么開始做了。
React框架中引入我們想要的插件,只要這樣做就行:
import Iscroll from "moudle/iscroll/Iscroll";
然后頁面掛載完成后我們進行實例化
componentDidMount() { setTimout(function () {//由於手機性能的原因,我們在定時器里面進行實例化 this.myIScroll = new IScroll("#dom",{ mouseWheel: true, bounce: true, scrollbars: false, }); },10); }
一但React檢測到數據有更新,他就會自動刷新頁面,那么我們這個時候需要重新刷新IScroll:
componentDidUpdate() { this.myIScroll.refresh(); }
代碼做到這里,我們就會發現,即使禁止touchmove事件,我們依然可以使用滾動列表,問題完美解決!不過,此時客戶大手一揮,指着我們的滾動條,這個滾動條有點丑呀,能不能把它做得科技感一些……哎,數不清的星星,改不完的需求啊,不過,這個對於我們無(xia)所(chui)不(niu)能(bi)的程序猿也是可以做得。在IScroll中,它的滾動條是由兩個做了定位的div構成的,那么
我們在實例化IScroll的時候參數中設置:
this.myIScroll = new IScroll("#dom",{ scrollbars: 'custom',//即scrollbars的值設置成字符串:“custom”即可 });
然后我們在樣式表中使用兩個類名:
.iScrollIndicator和.iScrollVerticalScrollbar.iScrollLoneScrollbar寫入樣式,這樣組成滾動條的兩個div就可以長成我們希望的樣子啦!
但是根據需求,有的列表很長,需要分頁加載,那么就需要判斷用戶拖動后是否需要加載下一頁的數據了。
基本的思路是,利用this.y得到向上移動的距離,用里面框的高度減去移動的距離,再減去外面框的高度,如果小於某一個高度,我們去請求數據,之后重新渲染就行了。但是,我查閱了IScroll的相關代碼,文檔上並沒有寫出內外兩個框高度的接口,也曾經試着自己在源碼里添加獲得框高度的方法,但是在實例化后獲取這個值總會有各種各樣的問題。最后經過仔細研究IScroll源碼,發現外面框的高度就是實例化后的:this.wrapperHeight,而里面框的高度就是:this.scrollerHeight,這樣我們減去this.y的絕對值后,就可以判斷出是否需要加載新的數據了。給大爺們線上代碼:
componentDidMount () { var self = this; const options = { preventDefault: false, zoom: false, mouseWheel: true, probeType: 3, bounce: true, scrollbars: true, }; this.iScrollInstance = new IScroll(this.scrollContent,options); this.iScrollInstance.on('scrollEnd', function() { var shouldGet = this.scrollerHeight + this.y - this.wrapperHeight;//注意this.y一定是非正數,所以這里是加啦! if (shouldGet < 200 && this.directionY === 1 && !self.state.loading) { //我這里設置的臨界值是200,即列表滾動結束后,如果里框的高度還有小於200px的內容沒有顯示過就會去加載新的數據。 if(self.state.pageNo >= self.state.totalPage) { Action.update({noMore: true}); } else { self.state.pageNo ++; Action.getList({pageNo: self.state.pageNo}); } } }); }
做到這里,我們利用IScroll想解決的需求都可以滿足了……