iScroll-5拉动刷新功能实现与iScroll-4上拉刷新的一点改进

近来在学习移动设备的应用开发,接触了jQuery mobile,在网上查阅相关资料时发现一个叫”iScroll“的小插件。其实这个iScroll插件跟jQuery mobile没有多大关系,并不是基于jQuery mobile类库开发的js插件,是一个独立的js插件,使用它时不必预先引用jquery或jquery mobile类库。关于iScroll的详细介绍可以去它的官网了解或者去GitHub(https://github.com/cubiq/iscroll/)下载它的源码学习。iScroll现在已经更新为iScroll-5,但并没有加入太多新的功能,只是对前一个版本iScroll-4进行重构优化与修复,而且官方消息说不再对iScroll-4进行维护和技术支持,建议使用新版本。官方说法如下:

iScroll-5与iScroll-4的差别还挺大的,首先在声明定义上写法就不一样了。

iScroll-4: iScroll-5:
var myScroll = new iScroll('wrapper', {
    useTransition: true,
    topOffset: pullDownOffset,
    onRefresh: function () {
        ...
    },
   onScrollStart: function () { 
     ...
   },
   onScrollMove: function () { 
     ...
   },
   onScrollEnd: function () { 
     ...
   }
   
   .... 
});

 

var myScroll = new IScroll('#wrapper', {
    mouseWheel: true,
    scrollbars: true,
    startY:-pullDownOffset
    ...
});

//Event: scroll start
myScroll.on("scrollStart", function() {
    ...
});
    
//Event: scroll
myScroll.on('scroll', function(){
    ...
});
    
//Event: scrollEnd
myScroll.on("scrollEnd", function() {
    ...
});
//Event: refresh
myScroll.on("refresh", function() {
    ...
});
...

 

 

从上面两者的示例代码对比,以前的声明定义是new iScroll('wrapper'...),现在变为new IScroll('#wrapper'...),类名变为大写I开头了,容器的Id要加上前缀#,某些参数名称改变了(以前的topOffset变为startY和startX),关键是事件(event)不再是从options参数中指定,而是用on去注册等等。

还有一个重大的不同是,iScroll-5按照功能点不同自身又划分为不同的版本,官方划分如下:

还有其他的差异就不再逐个列出了,大家可以到官网去了解。

其实iScroll之所以吸引我,主要是在iScroll-4中的demo里面有一个叫”pull-to-refresh“的示例,也就是我们说拉动更新,包括列表内容的下拉和上拉更新。这个功能在触摸的移动应用上经常用到,可以不夸张的说是必不可少的。但这个重要的示例在新版iScroll-5的demo中却找不到了。不知什么原因作者将它去掉了(据说iScroll-4的pull-to-refresh这个示例并不是作者的杰作),我在网上找了很久都没发现有关于iScroll-5官方的pull-to-refresh示例,个别实现的例子倒也是有一些,但效果都不是很令人满意。为了加深和巩固对iScroll的学习,本人就参考iScroll-4的pull-to-refresh示例来实现iScroll-5的拉动刷新功能,同时也对iScroll-4的pull-to-refresh示例根据个人需求进行了一点改进。

首先给出iScroll-4的pull-to-refresh示例改动后的代码:

  1 <script type="text/javascript">
  2 
  3 var myScroll,
  4     pullDownEl, pullDownOffset,
  5     pullUpEl, pullUpOffset, _maxScrollY;
  6 var generatedCount = 0;
  7 
  8 function pullDownAction () {
  9     setTimeout(function () {    // <-- Simulate network congestion, remove setTimeout from production!
 10         var el, li, i;
 11         el = document.getElementById('thelist');
 12 
 13         for (i=0; i<3; i++) {
 14             li = document.createElement('li');
 15             li.innerHTML = '(Pull down) Generated row ' + (++generatedCount);//Firefox  does not suppose innerText, use innerHTML instead.
 16             el.insertBefore(li, el.childNodes[0]);
 17         }
 18         
 19         myScroll.refresh();        // Remember to refresh when contents are loaded (ie: on ajax completion)
 20     }, 1000);    // <-- Simulate network congestion, remove setTimeout from production!
 21 }
 22 
 23 function pullUpAction () {
 24     setTimeout(function () {    // <-- Simulate network congestion, remove setTimeout from production!
 25         var el, li, i;
 26         el = document.getElementById('thelist');
 27 
 28         for (i=0; i<3; i++) {
 29             li = document.createElement('li');
 30             li.innerHTML = '(Pull up) Generated row ' + (++generatedCount);//Firefox  does not suppose innerText, use innerHTML instead.
 31             el.appendChild(li, el.childNodes[0]);
 32         }
 33         
 34         myScroll.refresh();        // Remember to refresh when contents are loaded (ie: on ajax completion)
 35     }, 1000);    // <-- Simulate network congestion, remove setTimeout from production!
 36 }
 37 
 38 function loaded() {
 39     pullDownEl = document.getElementById('pullDown');
 40     pullDownOffset = pullDownEl.offsetHeight;
 41     pullUpEl = document.getElementById('pullUp');    
 42     pullUpOffset = pullUpEl.offsetHeight;
 43     
 44     
 45     myScroll = new iScroll('wrapper', {
 46         useTransition: true,
 47         topOffset: pullDownOffset,
 48         onRefresh: function () {
 49             console.log('maxScrollY-0:'+this.maxScrollY);
 50             _maxScrollY = this.maxScrollY = this.maxScrollY + pullUpOffset;
 51             console.log('maxScrollY-1:'+this.maxScrollY);
 52             if (pullDownEl.className.match('loading')) {
 53                 pullDownEl.className = '';
 54                 pullDownEl.querySelector('.pullDownLabel').innerHTML = 'Pull down to refresh...';
 55             } else if (pullUpEl.className.match('loading')) {
 56                 pullUpEl.className = '';
 57                 pullUpEl.querySelector('.pullUpLabel').innerHTML = 'Pull up to load more...';
 58                 this.scrollTo(0,this.maxScrollY,0);
 59             }
 60         },
 61         onScrollMove: function () {
 62             console.log('maxScrollY-3:'+this.maxScrollY);
 63             if (this.y > 5 && !pullDownEl.className.match('flip')) {
 64                 pullDownEl.className = 'flip';
 65                 pullDownEl.querySelector('.pullDownLabel').innerHTML = 'Release to refresh...';
 66                 this.minScrollY = 0;
 67             } else if (this.y < 5 && pullDownEl.className.match('flip')) {
 68                 pullDownEl.className = '';
 69                 pullDownEl.querySelector('.pullDownLabel').innerHTML = 'Pull down to refresh...';
 70                 this.minScrollY = -pullDownOffset;
 71             } else if (this.y <= (_maxScrollY - pullUpOffset) && !pullUpEl.className.match('flip')) {
 72                 pullUpEl.className = 'flip';
 73                 pullUpEl.querySelector('.pullUpLabel').innerHTML = 'Release to refresh...';
 74                 this.maxScrollY = this.maxScrollY - pullUpOffset;
 75             } else if (this.y > (_maxScrollY - pullUpOffset) && pullUpEl.className.match('flip')) {
 76                 pullUpEl.className = '';
 77                 pullUpEl.querySelector('.pullUpLabel').innerHTML = 'Pull up to load more...';
 78                 this.maxScrollY = this.maxScrollY + pullUpOffset;
 79             }
 80         },
 81         onScrollEnd: function () {
 82             if (pullDownEl.className.match('flip')) {
 83                 pullDownEl.className = 'loading';
 84                 pullDownEl.querySelector('.pullDownLabel').innerHTML = 'Loading...';                
 85                 console.log('pull down---scroll end');
 86                 pullDownAction();    // Execute custom function (ajax call?)
 87             } else if (pullUpEl.className.match('flip')) {
 88                 pullUpEl.className = 'loading';
 89                 pullUpEl.querySelector('.pullUpLabel').innerHTML = 'Loading...';
 90                 console.log('pull up---scroll end');                
 91                 pullUpAction();    // Execute custom function (ajax call?)
 92             }
 93         }
 94     });
 95     
 96     
 97     setTimeout(function () { document.getElementById('wrapper').style.left = '0'; }, 800);
 98 }
 99 
100 document.addEventListener('touchmove', function (e) { e.preventDefault(); }, false);
101 
102 document.addEventListener('DOMContentLoaded', function () { setTimeout(loaded, 200); }, false);
103 </script>

上面代码高亮标注(黄色底色)的地方是主要的改动点,对于第15,30行标注的innerHTML原来例子是innerText,但我发现在firefox运行后新增的li会显示不了内容(应该是firebox不支持innerText),改为innerHTML后就正常显示了。其他处的改动主要是针对maxScrollY这个变量,这样改主要是为了让列表内容滚动到底部时上拉前不显示提示栏。

iScroll-4 示例改动前: iScroll-4 示例改动后:

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

下面参照iScroll-4改动后的push-to-refresh示例来实现iScroll-5的拉动刷新功能。使用iScroll-5来实现的话,要引用的js类库是iScroll-5细分后的iscroll-probe.js,按照前面的划分介绍,此细分版本主要是为了探查当前滚动位置(x,y)。在实现过程中我发现类库有一小处源码是需要改动的,主要是解决鼠标滑轮向顶部滚动时,不显示下拉提示栏(这个问题在iScroll-4下是不存在的,应该跟iScroll-5去掉了minScrollY这个变量有关),我们希望在下拉时才会出现提示栏。

解决办法其实也不复杂,只需改动一下iscroll-probe.js文件里面的第1122行处的一小段代码。如下图:

之所以这样修改,主要是参考iScroll-4里面的源码。如下图:

好,源文件iscroll-probe.js的修改就完成了,下面给出iScroll-5拉动刷新功能的页面js代码:

  1 <script type="text/javascript">
  2 
  3 var myScroll, 
  4     pullDownEl, pullDownOffset,
  5     pullUpEl, pullUpOffset, _maxScrollY;
  6     
  7 var generatedCount = 0;
  8 
  9 function pullDownAction(){
 10         setTimeout(function () {    // <-- Simulate network congestion, remove setTimeout from production!
 11             var el, li, i;
 12             el = document.querySelector('#scroller ul');
 13     
 14             for (i=0; i<3; i++) {
 15                 li = document.createElement('li');
 16                 li.innerHTML = '(Pull down) Generated row ' + (++generatedCount);//Firefox  does not suppose innerText, use innerHTML instead.
 17                 el.insertBefore(li, el.childNodes[0]);
 18             }
 19             if(myScroll){
 20                 myScroll.refresh();        // Remember to refresh when contents are loaded (ie: on ajax completion)
 21             }
 22         }, 1000);    // <-- Simulate network congestion, remove setTimeout from production!
 23 }
 24 
 25 function pullUpAction () {
 26     setTimeout(function () {    // <-- Simulate network congestion, remove setTimeout from production!
 27         var el, li, i;
 28         el = document.querySelector('#scroller ul');
 29 
 30         for (i=0; i<3; i++) {
 31             li = document.createElement('li');
 32             li.innerHTML = '(Pull up) Generated row ' + (++generatedCount);//Firefox  does not suppose innerText, use innerHTML instead.
 33             el.appendChild(li, el.childNodes[0]);
 34         }
 35         if(myScroll){
 36             myScroll.refresh();        // Remember to refresh when contents are loaded (ie: on ajax completion)
 37         }
 38     }, 1000);    // <-- Simulate network congestion, remove setTimeout from production!
 39 }
 40 
 41 function loaded() {
 42     pullDownEl = document.querySelector('#pullDown');
 43     if (pullDownEl) {
 44         pullDownOffset = pullDownEl.offsetHeight;
 45     } else {
 46         pullDownOffset = 0;
 47     }
 48     pullUpEl = document.querySelector('#pullUp');    
 49     if (pullUpEl) {
 50         pullUpOffset = pullUpEl.offsetHeight;
 51     } else {
 52         pullUpOffset = 0;
 53     }
 54     
 55     console.log('pullDownOffset:'+pullDownOffset);
 56     console.log('pullUpOffset:'+pullUpOffset);
 57     
 58     //Options of IScroll
 59     var myOptions = {
 60             mouseWheel: true,
 61             scrollbars: true,
 62             fadeScrollbars: true,
 63             probeType:1,
 64             startY:-pullDownOffset
 65         };
 66     myScroll = new IScroll('#wrapper',myOptions);
 67     console.log('maxScrollY-1:'+myScroll.maxScrollY);
 68     _maxScrollY = myScroll.maxScrollY = myScroll.maxScrollY + pullUpOffset;
 69     console.log('maxScrollY-2:'+myScroll.maxScrollY);
 70     
 71     var isScrolling = false;
 72     
 73     //Event: scrollStart
 74     myScroll.on("scrollStart", function() {
 75         if(this.y==this.startY){
 76             isScrolling = true;
 77         }
 78         console.log('start-y:'+this.y);
 79     });
 80     
 81     //Event: scroll
 82     myScroll.on('scroll', function(){
 83         if (this.y >= 5 && pullDownEl && !pullDownEl.className.match('flip')) {
 84             pullDownEl.className = 'flip';
 85             pullDownEl.querySelector('.pullDownLabel').innerHTML = 'Release to refresh';
 86             //this.minScrollY = 0;
 87         } else if (this.y < 5  && pullDownEl && pullDownEl.className.match('flip')) {
 88             pullDownEl.className = '';
 89             pullDownEl.querySelector('.pullDownLabel').innerHTML = 'Pull down to refresh';
 90             //this.minScrollY = -pullDownOffset;
 91         }else if (this.y <= (_maxScrollY - pullUpOffset) && pullUpEl && !pullUpEl.className.match('flip')) {
 92             pullUpEl.className = 'flip';
 93             pullUpEl.querySelector('.pullUpLabel').innerHTML = 'Release to refresh';
 94             //this.maxScrollY = this.maxScrollY;
 95             this.maxScrollY = this.maxScrollY - pullUpOffset;
 96         } else if (this.y > (_maxScrollY - pullUpOffset) && pullUpEl && pullUpEl.className.match('flip')) {
 97             pullUpEl.className = '';
 98             pullUpEl.querySelector('.pullUpLabel').innerHTML = 'Pull up to load more';
 99             //this.maxScrollY = pullUpOffset;
100             this.maxScrollY = this.maxScrollY + pullUpOffset;
101         }
102                     
103         console.log('y:'+this.y);
104     });
105     
106     //Event: scrollEnd
107     myScroll.on("scrollEnd", function() {
108         console.log('scroll end'); 
109         console.log('directionY:'+this.directionY);
110         console.log('y1:'+this.y);
111         console.log('maxScrollY-3:'+this.maxScrollY);
112         if (pullDownEl && !pullDownEl.className.match('flip') && this.y > this.options.startY) {
113             console.log('resume'); 
114             this.scrollTo(0, this.options.startY,800);
115           }
116         else if (pullDownEl && pullDownEl.className.match('flip')){
117                 pullDownEl.className = 'loading';
118                 pullDownEl.querySelector('.pullDownLabel').innerHTML = 'Loading...';                
119                 // Execute custom function (ajax call?)
120                 if (isScrolling) {
121                     console.log('before pull down action:'+this.y); 
122                     pullDownAction();
123                       console.log('after pull down action:'+this.y); 
124                   }
125         }
126         else if (pullUpEl && pullUpEl.className.match('flip')) {
127                 console.log('pull up loading'); 
128                 pullUpEl.className = 'loading';
129                 pullUpEl.querySelector('.pullUpLabel').innerHTML = 'Loading...';    
130                 // Execute custom function (ajax call?)
131                 if (isScrolling) {            
132                     console.log('before pull up action:'+this.y); 
133                     pullUpAction();    
134                     console.log('after pull up action:'+this.y); 
135                 }
136         }
137         isScrolling = false;
138     });
139     
140     //Event: refresh
141     myScroll.on("refresh", function() {
142           
143          console.log('maxScrollY-4:'+this.maxScrollY);
144          _maxScrollY = this.maxScrollY = this.maxScrollY+pullUpOffset;
145          console.log('maxScrollY-5:'+this.maxScrollY);
146          
147          if (pullDownEl  && pullDownEl.className.match('loading')) {
148                 pullDownEl.className = '';
149                 pullDownEl.querySelector('.pullDownLabel').innerHTML = 'Pull down to refresh';
150                 this.scrollTo(0,this.options.startY,0);
151          } else if (pullUpEl && pullUpEl.className.match('loading')) {
152                 pullUpEl.className = '';
153                 pullUpEl.querySelector('.pullUpLabel').innerHTML = 'Pull up to load more';
154                 this.scrollTo(0,this.maxScrollY,0);
155          }
156          
157          console.log('refresh finished!'); 
158     });
159     
160     setTimeout(function () { document.getElementById('wrapper').style.left = '0'; }, 500);
161     
162 }
163 
164 document.addEventListener('touchmove', function (e) { e.preventDefault(); }, false);
165 
166 </script>

运行效果:

 iScroll-4:
 iScroll-5:

后话:按照官网的介绍iScroll-5比iScroll-4更快,性能更好。但在上面的拉动刷新的示例中,iScroll-5在firefox中运行时比iScroll-4要占用更多的CPU,感觉iScroll-4要流畅得多,但仅限于拉动功能的比较,至于iScroll的其他功能没有测试对比过,所以也不敢以偏概全地断言说iScroll-5的性能就比上iScroll-4。有兴趣的朋友可以去测试一下,测完后给我分享一下结果,谢谢!

posted @ 2015-01-18 15:10  Yghost  Views(8959)  Comments(9Edit  收藏  举报