var IScrollPullUpDown = function (wrapperName,iScrollConfig,pullDownActionHandler,pullUpActionHandler) { var iScrollConfig,pullDownActionHandler,pullUpActionHandler,pullDownEl,pullDownOffset,pullUpEl,scrollStartPos; var pullThreshold=5; var me=this; function showPullDownElNow(className) { // Shows pullDownEl with a given className pullDownEl.style.transitionDuration=''; pullDownEl.style.marginTop=''; pullDownEl.className = 'pullDown '+className; } var hidePullDownEl = function (time,refresh) { // Hides pullDownEl pullDownEl.style.transitionDuration=(time>0?time+'ms':''); pullDownEl.style.marginTop=''; pullDownEl.className = 'pullDown scrolledUp'; // If refresh==true, refresh again after time+10 ms to update iScroll's "scroller.offsetHeight" after the pull-down-bar is really hidden... // Don't refresh when the user is still dragging, as this will cause the content to jump (i.e. don't refresh while dragging) if (refresh) setTimeout(function(){me.myScroll.refresh();},time+10); } function init() { var wrapperObj = document.querySelector('#'+wrapperName); var scrollerObj = wrapperObj.children[0]; if (pullDownActionHandler) { // If a pullDownActionHandler-function is supplied, add a pull-down bar at the top and enable pull-down-to-refresh. // (if pullDownActionHandler==null this iScroll will have no pull-down-functionality) pullDownEl=document.createElement('div'); pullDownEl.className='pullDown scrolledUp'; pullDownEl.innerHTML='下拉刷新'; scrollerObj.insertBefore(pullDownEl, scrollerObj.firstChild); pullDownOffset = pullDownEl.offsetHeight; } if (pullUpActionHandler) { // If a pullUpActionHandler-function is supplied, add a pull-up bar in the bottom and enable pull-up-to-load. // (if pullUpActionHandler==null this iScroll will have no pull-up-functionality) pullUpEl=document.createElement('div'); pullUpEl.className='pullUp'; pullUpEl.innerHTML='上拉加载更多'; scrollerObj.appendChild(pullUpEl); } me.myScroll = new IScroll(wrapperObj,iScrollConfig); me.myScroll.on('refresh',function() { if ((pullDownEl)&&(pullDownEl.className.match('loading'))) { pullDownEl.querySelector('.pullDownLabel').innerHTML = '下拉刷新'; if (this.y>=0) { // The pull-down-bar is fully visible: // Hide it with a simple 250ms animation hidePullDownEl(250,true); } else if (this.y>-pullDownOffset) { // The pull-down-bar is PARTLY visible: // Set up a shorter animation to hide it // Firt calculate a new margin-top for pullDownEl that matches the current scroll position pullDownEl.style.marginTop=this.y+'px'; // CSS-trick to force webkit to render/update any CSS-changes immediately: Access the offsetHeight property... pullDownEl.offsetHeight; // Calculate the animation time (shorter, dependant on the new distance to animate) from here to completely 'scrolledUp' (hidden) // Needs to be done before adjusting the scroll-positon (if we want to read this.y) var animTime=(250*(pullDownOffset+this.y)/pullDownOffset); // Set scroll positon to top // (this is the same as adjusting the scroll postition to match the exact movement pullDownEl made due to the change of margin-top above, so the content will not "jump") this.scrollTo(0,0,0); // Hide pullDownEl with the new (shorter) animation (and reset the inline style again). setTimeout(function() { // Do this in a new thread to avoid glitches in iOS webkit (will make sure the immediate margin-top change above is rendered)... hidePullDownEl(animTime,true); },0); } else { // The pull-down-bar is completely off screen: // Hide it immediately hidePullDownEl(0,true); // And adjust the scroll postition to match the exact movement pullDownEl made due to change of margin-top above, so the content will not "jump" this.scrollBy(0,pullDownOffset,0); } } if ((pullUpEl)&&(pullUpEl.className.match('loading'))) { pullUpEl.className = 'pullUp'; pullUpEl.querySelector('.pullUpLabel').innerHTML = '上拉加载更多'; } }); me.myScroll.on('scrollStart',function() { scrollStartPos=this.y; // Store the scroll starting point to be able to track movement in 'scroll' below }); me.myScroll.on('scroll',function() { if (pullDownEl||pullUpEl) { if((scrollStartPos==0)&&(this.y==0)) { // 'scroll' called, but scroller is not moving! // Probably because the content inside wrapper is small and fits the screen, so drag/scroll is disabled by iScroll // Fix this by a hack: Setting "myScroll.hasVerticalScroll=true" tricks iScroll to believe // that there is a vertical scrollbar, and iScroll will enable dragging/scrolling again... this.hasVerticalScroll=true; // Set scrollStartPos to -1000 to be able to detect this state later... scrollStartPos=-1000; } else if ((scrollStartPos==-1000) && (((!pullUpEl)&&(!pullDownEl.className.match('flip'))&&(this.y<0)) || ((!pullDownEl)&&(!pullUpEl.className.match('flip'))&&(this.y>0)))) { // Scroller was not moving at first (and the trick above was applied), but now it's moving in the wrong direction. // I.e. the user is either scrolling up while having no "pull-up-bar", // or scrolling down while having no "pull-down-bar" => Disable the trick again and reset values... this.hasVerticalScroll=false; scrollStartPos=0; this.scrollBy(0,-this.y, 0); // Adjust scrolling position to undo this "invalid" movement } } if (pullDownEl) { if (this.y > pullDownOffset+pullThreshold && !pullDownEl.className.match('flip')) { showPullDownElNow('flip'); this.scrollBy(0,-pullDownOffset, 0); // Adjust scrolling position to match the change in pullDownEl's margin-top pullDownEl.querySelector('.pullDownLabel').innerHTML = '界面刷新中...'; } else if (this.y < 0 && pullDownEl.className.match('flip')) { // User changes his mind... hidePullDownEl(0,false); this.scrollBy(0,pullDownOffset, 0); // Adjust scrolling position to match the change in pullDownEl's margin-top pullDownEl.querySelector('.pullDownLabel').innerHTML = '下拉刷新'; } } if (pullUpEl) { if (this.y < (this.maxScrollY - pullThreshold) && !pullUpEl.className.match('flip')) { pullUpEl.className = 'pullUp flip'; pullUpEl.querySelector('.pullUpLabel').innerHTML = '正在加载...'; } else if (this.y > (this.maxScrollY + pullThreshold) && pullUpEl.className.match('flip')) { pullUpEl.className = 'pullUp'; pullUpEl.querySelector('.pullUpLabel').innerHTML = '上拉加载更多'; } } }); me.myScroll.on('scrollEnd',function() { if ((pullDownEl)&&(pullDownEl.className.match('flip'))) { showPullDownElNow('loading'); pullDownEl.querySelector('.pullDownLabel').innerHTML = 'Loading...'; pullDownActionHandler(this); // Execute custom function (ajax call?) } if ((pullUpEl)&&(pullUpEl.className.match('flip'))) { pullUpEl.className = 'pullUp loading'; pullUpEl.querySelector('.pullUpLabel').innerHTML = 'Loading...'; pullUpActionHandler(this); // Execute custom function (ajax call?) } if (scrollStartPos=-1000) { // If scrollStartPos=-1000: Recalculate the true value of "hasVerticalScroll" as it may have been // altered in 'scroll' to enable pull-to-refresh/load when the content fits the screen... this.hasVerticalScroll = this.options.scrollY && this.maxScrollY < 0; } }); } init(); return me.myScroll; };