1. 程式人生 > >記:使用IScroll.js 開發picker日曆元件遇到的問題級經驗總結

記:使用IScroll.js 開發picker日曆元件遇到的問題級經驗總結

IScroll中文文件

第一個問題: 邊界留白

  

  就是這種,上邊界(最小),下邊界(最大)有兩個列表的位置是不能選擇的。解決的辦法是:

  

  在HTML中,新增空白節點就行了。

 

第二個問題:初始化之後的滾動停止的事件的第二個引數問題。  

var myScroll = new IScroll('#wrapper');
myScroll.on('scrollEnd', function(){
    //這裡一定不要寫成es6箭頭函式
    //要執行的程式碼
    //這個函式沒有引數
});

(1) 第二的個引數,是個函式。它沒有引數,而且不要寫成,不要寫成,不要寫成箭頭的形式。因為這函式裡面的this,是繫結的一些有用資訊,比如:this.y是當前滾動的距離等。還有哪些資訊可以看 文件中的 滾動條資訊 這一欄。如果寫成ES6的形式,那this指向就變了,這樣就獲取不了所需的資訊。

(2) 第二個引數是沒有形參的。即沒有任何可使用的引數。

 

第三個問題:定義了snap選項,但是滾動有偏差

  開發的日曆選擇元件picker是使用rem單位自適應的,雖然在配置項中 有個options.snap,官方說可以對齊到固定的位置和元素,但是在使用自適應單位的情況中,這個配置並沒有展現出真正的效果,滾動的時候一定會出現偏差。

  那怎麼解決這個自適應的問題呢?由於是在滾動結束之後,位置才出現的偏差。那麼我就在滾動結束之後立馬呼叫修正位置的函式就行了。

  我是在vue中使用的。所以定義下面的函式,因為有 年,月,日,時四個滾動項。所以需要判斷是哪一個正在滾動

fixPos: function(target,num) {
    var step = Math.abs(Math.round(target.y / this.itemHeight));
    var maxYearLen = this.yearArr.length;
    var maxMonthLen = this.monthArr.length;
    var maxDayLen = this.dayArr.length;
    var maxHourLen = this.hourArr.length;
    switch(num){
        case 0:
            step = step > maxYearLen ? maxYearLen - 1 : step;
            break;
        case 1:
            step = step > maxMonthLen ? maxMonthLen - 1 : step;
            break;
        case 2:
            step = step > maxDayLen ? maxDayLen - 1 : step;
            break;
        case 3:
            step = step > maxHourLen ? maxHourLen - 1 : step;
            break;
    }
    var fixPos = step * this.itemHeight;//重新計算較為精確的位置
    target.y = fixPos;//重置原來的滾動距離
    this.selectArr[num] = step;//這是儲存每個列表滾動的索引值
    target.scrollTo(0, -fixPos);//這是滾動到修正後的位置
},

(1) 大致的思路就是:首先用當前滾動的距離,來除以滾動內容中,每個列表的高度。然後取最近似的值,就是當前應該滾動的列表的個數。

(2) 如果出現突然滾動到最底部,這時候需要滾動的個數大於了滾動內容的最大列表個數,那麼就糾正一下個數為最大列表數 - 1。

(3) 然後設定較為精確的滾動距離。再滾動到指定的位置。

(4) this.itemHeight是在created生命週期的時候就宣告的 this.itemHeight = (document.body.offsetWidth / 750) * 100 * 0.8;  相當於在375px寬度下,每個列表就是40px的高度。在iPhone5 320px下,就是34.133334了。

  呼叫的時候: 

var yearScroll = new IScroll('#calendarYear');
var that = this;
yearScroll.on('scrollEnd', function() {
    that.fixPos(this, 0);
})

  

第四個問題:使用自適應單位時最大滾動距離不準確

  這個問題和第二個問題類似,解決的方法:

fixMaxScrollY: function(target, num) {
    var yearLen = this.yearArr.length - 1;
    var monthLen = this.monthArr.length - 1;
    var dayLen = this.dayArr.length - 1;
    var hourLen = this.hourArr.length - 1;
    switch(num) {
        case 0:
            target.maxScrollY = -(Math.round(yearLen * this.itemHeight));
            break;
        case 1:
            target.maxScrollY = -(Math.round(monthLen * this.itemHeight));
            break;
        case 2:
            target.maxScrollY = -(Math.round(dayLen * this.itemHeight));
            break;
        case 3:
            target.maxScrollY = -(Math.round(hourLen * this.itemHeight));
            break;
    }
},

(1)  例項化後的滾動物件,有個最大滾動值maxScrollY,主要也是根據滾動內容的列表長度來重置最大滾動距離

(2)  因為有四個滾動的內容項,所以需要傳入當前是第幾個滾動的內容。

  呼叫的時候:

var yearScroll = new IScroll('#calendarYear');
this.fixMaxScrollY(yearScroll, 0);

 

第五個問題: 日曆元件的內容是在點選某個按鈕之後再觸發顯示的,最初是隱藏。但就是這個原因,導致顯示出來的內容滾動不了。

  解決的辦法是:使用 xxx.refresh() 重新整理函式。這個函式具體的說明可以看文件中 重新整理 這個選項內容

  在控制組件顯示的函式中,呼叫重新整理的方法。

this.$nextTick(function(){
    this.scrollTarget[0].refresh();
    this.scrollTarget[1].refresh();
    this.scrollTarget[2].refresh();
    this.scrollTarget[3].refresh();
    //如果預設隱藏,則必須修復滾動的位置
    this.scrollTarget[0].scrollTo(0, -this.itemHeight * this.selectArr[0]);
    this.scrollTarget[1].scrollTo(0, -this.itemHeight * this.selectArr[1]);
    this.scrollTarget[2].scrollTo(0, -this.itemHeight * this.selectArr[2]);
    this.scrollTarget[3].scrollTo(0, -this.itemHeight * this.selectArr[3]);
})

(1) scrollTarget 是初始化滾動例項之後,儲存的滾動例項。因為多次會用到。

(2) 因為有年,月,日,時四個滾動內容,所以要重新整理四個滾動器。

(3) 如果日曆有最初的滾動位置,那麼也會出現不能跳到指定的位置的問題。所以,也需要初始化最初始的位置。

(4) selectArr 是滾動器滾動的索引值。比如我月份是1月到12月,當前滾動到了6月,那麼此時selectArr[1] 就是5 。

(5) 官方是推薦用setTimeout來使用重新整理,但是我使用的是vue來開發的,所以,這裡用vm.$nextTick()來代替setTimeout。

 

第六個問題: 日曆元件複用時,只能初始化滾動第一個日曆元件,而且第一個日曆元件滾動還是有問題。

  這個問題產生的原因很簡單:因為 IScroll 在初始化例項的時候, var myScroll = new IScroll('.wrapper');  它這個css選擇器使用的是querySelector 而不是 querySelectorAll,所以iScroll只會作用到選擇器選中元素的第一個。如果你需要對多個物件使用iScroll,你需要構建自己的迴圈機制。這是官方的說法。那麼怎樣建立迴圈機制呢?難道我要在初始化的時候還要迴圈去 new IScroll(xxx)建立例項嗎?

  其實沒必要。只需要改一改原始碼就行了。

  我使用的是 iscroll-lite 這個版本。這裡面 在定義 IScroll 這個函式的時候有這段程式碼

this.wrapper = typeof el == 'string' ? document.querySelector(el) : el;

  它每次初始化時,只選擇了 el  滾動物件的第一個元素,那麼,我只需要傳入當前是第幾個日曆元件,再改成querySelectorAll就行了。即:

this.wrapper = typeof el == 'string' ? document.querySelectorAll(el)[childIndex] : el;

  然後在這個定義的 IScroll 函式的引數中,再增加一個引數,表示第幾個元素。

  然後在初始化滾動例項的時候:

var options = {
    snap: '.calendar-scroll-item', //對齊的位置,相當於自動糾正每次移動的距離
    //scrollbars: true,//是否顯示滾動條
}
//初始化滾動
var yearScroll = new IScroll('.calendar-scroll>.calendarYear', options , this.curIndex);

  當然這個時候,css選擇器就不要用 id 了。

  然後在 元件的 prop 裡面新增一個屬性。

curIndex:{
    type:Number,
    default:0
}

(1) 預設只使用一個元件,即不傳這個prop 的話就是預設初始化第一個元件的滾動內容。

  使用元件的時候:引入,註冊等步驟就省略了

<calendar :cur-index='0'/>
<calendar :cur-index='1'/>
<calendar :cur-index='2'/>

   這樣不管使用多少個,都能正常初始化滾動了。而且互不影響 。注意,如果傳的是數字,需要v-bind 告訴vue這不是字串,是表示式。不然傳過去的是字串

&n