近来在学习移动设备的应用开发,接触了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', { |
var myScroll = new IScroll('#wrapper', { //Event: refresh ... |
从上面两者的示例代码对比,以前的声明定义是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示例改动后的代码:
<script type="text/javascript"> var myScroll,
pullDownEl, pullDownOffset,
pullUpEl, pullUpOffset, _maxScrollY;
var generatedCount = 0; function pullDownAction () {
setTimeout(function () { // <-- Simulate network congestion, remove setTimeout from production!
var el, li, i;
el = document.getElementById('thelist'); for (i=0; i<3; i++) {
li = document.createElement('li');
li.innerHTML = '(Pull down) Generated row ' + (++generatedCount);//Firefox does not suppose innerText, use innerHTML instead.
el.insertBefore(li, el.childNodes[0]);
} myScroll.refresh(); // Remember to refresh when contents are loaded (ie: on ajax completion)
}, 1000); // <-- Simulate network congestion, remove setTimeout from production!
} function pullUpAction () {
setTimeout(function () { // <-- Simulate network congestion, remove setTimeout from production!
var el, li, i;
el = document.getElementById('thelist'); for (i=0; i<3; i++) {
li = document.createElement('li');
li.innerHTML = '(Pull up) Generated row ' + (++generatedCount);//Firefox does not suppose innerText, use innerHTML instead.
el.appendChild(li, el.childNodes[0]);
} myScroll.refresh(); // Remember to refresh when contents are loaded (ie: on ajax completion)
}, 1000); // <-- Simulate network congestion, remove setTimeout from production!
} function loaded() {
pullDownEl = document.getElementById('pullDown');
pullDownOffset = pullDownEl.offsetHeight;
pullUpEl = document.getElementById('pullUp');
pullUpOffset = pullUpEl.offsetHeight; myScroll = new iScroll('wrapper', {
useTransition: true,
topOffset: pullDownOffset,
onRefresh: function () {
console.log('maxScrollY-0:'+this.maxScrollY);
_maxScrollY = this.maxScrollY = this.maxScrollY + pullUpOffset;
console.log('maxScrollY-1:'+this.maxScrollY);
if (pullDownEl.className.match('loading')) {
pullDownEl.className = '';
pullDownEl.querySelector('.pullDownLabel').innerHTML = 'Pull down to refresh...';
} else if (pullUpEl.className.match('loading')) {
pullUpEl.className = '';
pullUpEl.querySelector('.pullUpLabel').innerHTML = 'Pull up to load more...';
this.scrollTo(0,this.maxScrollY,0);
}
},
onScrollMove: function () {
console.log('maxScrollY-3:'+this.maxScrollY);
if (this.y > 5 && !pullDownEl.className.match('flip')) {
pullDownEl.className = 'flip';
pullDownEl.querySelector('.pullDownLabel').innerHTML = 'Release to refresh...';
this.minScrollY = 0;
} else if (this.y < 5 && pullDownEl.className.match('flip')) {
pullDownEl.className = '';
pullDownEl.querySelector('.pullDownLabel').innerHTML = 'Pull down to refresh...';
this.minScrollY = -pullDownOffset;
} else if (this.y <= (_maxScrollY - pullUpOffset) && !pullUpEl.className.match('flip')) {
pullUpEl.className = 'flip';
pullUpEl.querySelector('.pullUpLabel').innerHTML = 'Release to refresh...';
this.maxScrollY = this.maxScrollY - pullUpOffset;
} else if (this.y > (_maxScrollY - pullUpOffset) && pullUpEl.className.match('flip')) {
pullUpEl.className = '';
pullUpEl.querySelector('.pullUpLabel').innerHTML = 'Pull up to load more...';
this.maxScrollY = this.maxScrollY + pullUpOffset;
}
},
onScrollEnd: function () {
if (pullDownEl.className.match('flip')) {
pullDownEl.className = 'loading';
pullDownEl.querySelector('.pullDownLabel').innerHTML = 'Loading...';
console.log('pull down---scroll end');
pullDownAction(); // Execute custom function (ajax call?)
} else if (pullUpEl.className.match('flip')) {
pullUpEl.className = 'loading';
pullUpEl.querySelector('.pullUpLabel').innerHTML = 'Loading...';
console.log('pull up---scroll end');
pullUpAction(); // Execute custom function (ajax call?)
}
}
}); setTimeout(function () { document.getElementById('wrapper').style.left = '0'; }, 800);
} document.addEventListener('touchmove', function (e) { e.preventDefault(); }, false); document.addEventListener('DOMContentLoaded', function () { setTimeout(loaded, 200); }, false);
</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代码:
<script type="text/javascript"> var myScroll,
pullDownEl, pullDownOffset,
pullUpEl, pullUpOffset, _maxScrollY; var generatedCount = 0; function pullDownAction(){
setTimeout(function () { // <-- Simulate network congestion, remove setTimeout from production!
var el, li, i;
el = document.querySelector('#scroller ul'); for (i=0; i<3; i++) {
li = document.createElement('li');
li.innerHTML = '(Pull down) Generated row ' + (++generatedCount);//Firefox does not suppose innerText, use innerHTML instead.
el.insertBefore(li, el.childNodes[0]);
}
if(myScroll){
myScroll.refresh(); // Remember to refresh when contents are loaded (ie: on ajax completion)
}
}, 1000); // <-- Simulate network congestion, remove setTimeout from production!
} function pullUpAction () {
setTimeout(function () { // <-- Simulate network congestion, remove setTimeout from production!
var el, li, i;
el = document.querySelector('#scroller ul'); for (i=0; i<3; i++) {
li = document.createElement('li');
li.innerHTML = '(Pull up) Generated row ' + (++generatedCount);//Firefox does not suppose innerText, use innerHTML instead.
el.appendChild(li, el.childNodes[0]);
}
if(myScroll){
myScroll.refresh(); // Remember to refresh when contents are loaded (ie: on ajax completion)
}
}, 1000); // <-- Simulate network congestion, remove setTimeout from production!
} function loaded() {
pullDownEl = document.querySelector('#pullDown');
if (pullDownEl) {
pullDownOffset = pullDownEl.offsetHeight;
} else {
pullDownOffset = 0;
}
pullUpEl = document.querySelector('#pullUp');
if (pullUpEl) {
pullUpOffset = pullUpEl.offsetHeight;
} else {
pullUpOffset = 0;
} console.log('pullDownOffset:'+pullDownOffset);
console.log('pullUpOffset:'+pullUpOffset); //Options of IScroll
var myOptions = {
mouseWheel: true,
scrollbars: true,
fadeScrollbars: true,
probeType:1,
startY:-pullDownOffset
};
myScroll = new IScroll('#wrapper',myOptions);
console.log('maxScrollY-1:'+myScroll.maxScrollY);
_maxScrollY = myScroll.maxScrollY = myScroll.maxScrollY + pullUpOffset;
console.log('maxScrollY-2:'+myScroll.maxScrollY); var isScrolling = false; //Event: scrollStart
myScroll.on("scrollStart", function() {
if(this.y==this.startY){
isScrolling = true;
}
console.log('start-y:'+this.y);
}); //Event: scroll
myScroll.on('scroll', function(){
if (this.y >= 5 && pullDownEl && !pullDownEl.className.match('flip')) {
pullDownEl.className = 'flip';
pullDownEl.querySelector('.pullDownLabel').innerHTML = 'Release to refresh';
//this.minScrollY = 0;
} else if (this.y < 5 && pullDownEl && pullDownEl.className.match('flip')) {
pullDownEl.className = '';
pullDownEl.querySelector('.pullDownLabel').innerHTML = 'Pull down to refresh';
//this.minScrollY = -pullDownOffset;
}else if (this.y <= (_maxScrollY - pullUpOffset) && pullUpEl && !pullUpEl.className.match('flip')) {
pullUpEl.className = 'flip';
pullUpEl.querySelector('.pullUpLabel').innerHTML = 'Release to refresh';
//this.maxScrollY = this.maxScrollY;
this.maxScrollY = this.maxScrollY - pullUpOffset;
} else if (this.y > (_maxScrollY - pullUpOffset) && pullUpEl && pullUpEl.className.match('flip')) {
pullUpEl.className = '';
pullUpEl.querySelector('.pullUpLabel').innerHTML = 'Pull up to load more';
//this.maxScrollY = pullUpOffset;
this.maxScrollY = this.maxScrollY + pullUpOffset;
} console.log('y:'+this.y);
}); //Event: scrollEnd
myScroll.on("scrollEnd", function() {
console.log('scroll end');
console.log('directionY:'+this.directionY);
console.log('y1:'+this.y);
console.log('maxScrollY-3:'+this.maxScrollY);
if (pullDownEl && !pullDownEl.className.match('flip') && this.y > this.options.startY) {
console.log('resume');
this.scrollTo(0, this.options.startY,800);
}
else if (pullDownEl && pullDownEl.className.match('flip')){
pullDownEl.className = 'loading';
pullDownEl.querySelector('.pullDownLabel').innerHTML = 'Loading...';
// Execute custom function (ajax call?)
if (isScrolling) {
console.log('before pull down action:'+this.y);
pullDownAction();
console.log('after pull down action:'+this.y);
}
}
else if (pullUpEl && pullUpEl.className.match('flip')) {
console.log('pull up loading');
pullUpEl.className = 'loading';
pullUpEl.querySelector('.pullUpLabel').innerHTML = 'Loading...';
// Execute custom function (ajax call?)
if (isScrolling) {
console.log('before pull up action:'+this.y);
pullUpAction();
console.log('after pull up action:'+this.y);
}
}
isScrolling = false;
}); //Event: refresh
myScroll.on("refresh", function() { console.log('maxScrollY-4:'+this.maxScrollY);
_maxScrollY = this.maxScrollY = this.maxScrollY+pullUpOffset;
console.log('maxScrollY-5:'+this.maxScrollY); if (pullDownEl && pullDownEl.className.match('loading')) {
pullDownEl.className = '';
pullDownEl.querySelector('.pullDownLabel').innerHTML = 'Pull down to refresh';
this.scrollTo(0,this.options.startY,0);
} else if (pullUpEl && pullUpEl.className.match('loading')) {
pullUpEl.className = '';
pullUpEl.querySelector('.pullUpLabel').innerHTML = 'Pull up to load more';
this.scrollTo(0,this.maxScrollY,0);
} console.log('refresh finished!');
}); setTimeout(function () { document.getElementById('wrapper').style.left = '0'; }, 500); } document.addEventListener('touchmove', function (e) { e.preventDefault(); }, false); </script>
运行效果:
iScroll-4: | |||
iScroll-5: | |||
后话:按照官网的介绍iScroll-5比iScroll-4更快,性能更好。但在上面的拉动刷新的示例中,iScroll-5在firefox中运行时比iScroll-4要占用更多的CPU,感觉iScroll-4要流畅得多,但仅限于拉动功能的比较,至于iScroll的其他功能没有测试对比过,所以也不敢以偏概全地断言说iScroll-5的性能就比上iScroll-4。有兴趣的朋友可以去测试一下,测完后给我分享一下结果,谢谢!