├── .babelrc ├── .gitignore ├── .project ├── LICENSE ├── README.md ├── dist ├── _debug │ ├── css │ │ ├── images │ │ │ ├── img_pulltorefresh_skin2_1.png │ │ │ ├── img_pulltorefresh_skin2_2.png │ │ │ └── img_pulltorefresh_skin2_3.png │ │ └── pulltorefresh.skin.css │ ├── pulltorefresh.bizlogic.impl.js │ ├── pulltorefresh.bizlogic.impl2.js │ ├── pulltorefresh.core.js │ ├── pulltorefresh.skin.default.js │ ├── pulltorefresh.skin.native.js │ ├── pulltorefresh.skin.type1.js │ ├── pulltorefresh.skin.type2.js │ ├── pulltorefresh.skin.type3.js │ ├── pulltorefresh.skin.type4.js │ └── pulltorefresh.skin.type5.js ├── css │ ├── images │ │ ├── img_pulltorefresh_skin2_1.png │ │ ├── img_pulltorefresh_skin2_2.png │ │ └── img_pulltorefresh_skin2_3.png │ └── pulltorefresh.skin.css ├── pulltorefresh.bizlogic.impl.js ├── pulltorefresh.bizlogic.impl2.js ├── pulltorefresh.core.js ├── pulltorefresh.skin.default.js ├── pulltorefresh.skin.native.js ├── pulltorefresh.skin.type1.js ├── pulltorefresh.skin.type2.js ├── pulltorefresh.skin.type3.js ├── pulltorefresh.skin.type4.js └── pulltorefresh.skin.type5.js ├── examples ├── README.md ├── css │ ├── common.css │ ├── common_search.css │ ├── demo_pullRefresh_skin_bizlogic_multi.css │ ├── demo_pullRefresh_skin_multi_iscroll5.css │ └── mui.min.css ├── fonts │ └── mui.ttf ├── html │ ├── bizlogic-impl │ │ ├── demo_pullRefresh_skin_bizlogic_complex.html │ │ ├── demo_pullRefresh_skin_bizlogic_default.html │ │ ├── demo_pullRefresh_skin_bizlogic_default_Interface.html │ │ └── demo_pullRefresh_skin_bizlogic_default_Interface_custom.html │ ├── index.html │ ├── multi-scroller │ │ ├── demo_pullRefresh_skin_multi_Iscroll5.html │ │ ├── demo_pullRefresh_skin_multi_bizlogic_default.html │ │ └── demo_pullRefresh_skin_multi_skin_default.html │ ├── other │ │ ├── demo_elasticCircle.html │ │ ├── demo_pullRefresh_only_pulldown.html │ │ └── demo_pullRefresh_only_pullup.html │ ├── skin │ │ ├── demo_pullRefresh_skin_default.html │ │ ├── demo_pullRefresh_skin_native.html │ │ ├── demo_pullRefresh_skin_type1.html │ │ ├── demo_pullRefresh_skin_type2.html │ │ ├── demo_pullRefresh_skin_type3.html │ │ ├── demo_pullRefresh_skin_type4.html │ │ └── demo_pullRefresh_skin_type5.html │ └── 可以参考不基于IScroll的.js ├── img │ └── gallery │ │ ├── img_testgallery1.jpg │ │ ├── img_testgallery2.jpg │ │ ├── img_testgallery3.jpg │ │ └── img_testgallery4.jpg ├── js │ ├── demo_pullRefresh_multi_skin.js │ ├── demo_pullRefresh_skin.js │ ├── epoint.moapi.v2.min.js │ ├── mui.js │ └── mustache.min.js └── json │ └── testList.json ├── gulpfile.js ├── package.json ├── src ├── README.md ├── core │ ├── handedata.js │ ├── pulltorefresh.core.js │ ├── pulltorefresh.iscroll.probe.js │ └── pulltorefresh.js ├── css │ ├── images │ │ ├── img_pulltorefresh_skin2_1.png │ │ ├── img_pulltorefresh_skin2_2.png │ │ └── img_pulltorefresh_skin2_3.png │ └── pulltorefresh.skin.css ├── pulltorefresh.bizlogic.impl.js ├── pulltorefresh.bizlogic.impl2.js ├── pulltorefresh.skin.default.js ├── pulltorefresh.skin.native.js ├── pulltorefresh.skin.type1.js ├── pulltorefresh.skin.type2.js ├── pulltorefresh.skin.type3.js ├── pulltorefresh.skin.type4.js └── pulltorefresh.skin.type5.js └── staticresource └── img ├── effect1.gif ├── effect2.gif ├── effect3.gif ├── effect4.gif ├── effect5.gif └── effect6.gif /.babelrc: -------------------------------------------------------------------------------- 1 | { "presets": ["es2015"] } 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (http://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # Typescript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | 60 | *.project 61 | .svn 62 | -------------------------------------------------------------------------------- /.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | demo.webpack.pulltorefresh 4 | 5 | 6 | 7 | 8 | 9 | com.aptana.ide.core.unifiedBuilder 10 | 11 | 12 | 13 | 14 | 15 | com.aptana.projects.webnature 16 | 17 | 18 | 19 | 1491361987988 20 | 21 | 26 22 | 23 | org.eclipse.ui.ide.multiFilter 24 | 1.0-name-matches-false-false-node_modules 25 | 26 | 27 | 28 | 1496904618570 29 | 30 | 26 31 | 32 | org.eclipse.ui.ide.multiFilter 33 | 1.0-name-matches-false-false-node_modules 34 | 35 | 36 | 37 | 1496988019611 38 | 39 | 26 40 | 41 | org.eclipse.ui.ide.multiFilter 42 | 1.0-name-matches-false-false-node_modules 43 | 44 | 45 | 46 | 1497925692470 47 | 48 | 26 49 | 50 | org.eclipse.ui.ide.multiFilter 51 | 1.0-name-matches-false-false-node_modules 52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Lichun Dai 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## H5下拉刷新皮肤系列 2 | 基于`IScroll`的全套下拉刷新皮肤。各式各样的皮肤。以及下拉刷新实现基类供自定义UI实现。 3 | 4 | ### Notice 5 | 6 | 这个老版本的已经不再维护了,后续维护新版下拉刷新 7 | 8 | 地址: 9 | 10 | [https://github.com/minirefresh/minirefresh](https://github.com/minirefresh/minirefresh) 11 | 12 | 特点: 13 | 14 | 零依赖(原生JS实现,不依赖于任何库) 15 | 16 | 多主题,官方提供多种主题(包括默认,applet-仿小程序,drawer3d-3d抽屉效果,taobao-仿淘宝等) 17 | 18 | 易拓展,三层架构,专门抽取UI层面,方便实现各种的主题,实现一套主题非常方便,并且几乎可以实现任何效果 19 | 20 | 高性能,良好的兼容性。动画采用css3+硬件加速,在大部分手机上都非常流畅,支持和各种Scroll的嵌套(包括mui scroll,IScroll,Swipe等),支持Vue 21 | 22 | 优雅的API和源码,API设计科学,简单,源码严谨,所有源码通过ESlint检测,并提供完善的showcase和文档 23 | 24 | 另外,Npm上也有相应发布。 25 | 26 | ### Effect(效果) 27 | 28 | * 效果1 29 | ![](https://dailc.github.io/pulltorefresh-h5-iscroll/staticresource/img/effect1.gif) 30 | 31 | * 效果2 32 | ![](https://dailc.github.io/pulltorefresh-h5-iscroll/staticresource/img/effect2.gif) 33 | 34 | * 效果3 35 | ![](https://dailc.github.io/pulltorefresh-h5-iscroll/staticresource/img/effect3.gif) 36 | 37 | * 效果4 38 | ![](https://dailc.github.io/pulltorefresh-h5-iscroll/staticresource/img/effect4.gif) 39 | 40 | * 效果5 41 | ![](https://dailc.github.io/pulltorefresh-h5-iscroll/staticresource/img/effect5.gif) 42 | 43 | * 效果6 44 | ![](https://dailc.github.io/pulltorefresh-h5-iscroll/staticresource/img/effect6.gif) 45 | 46 | * 示例页面 47 | [下拉刷新皮肤示例](https://dailc.github.io/pulltorefresh-h5-iscroll/examples/html/) 48 | 49 | ### How To Use(使用) 50 | 51 | * Require(引入脚本) 52 | 53 | ``` 54 | 55 | ``` 56 | 可以将`skin.default`替换为对应的皮肤 57 | 58 | * HTML Structure(页面结构) 59 | 60 | ``` 61 |
62 |
63 | ... 64 |
65 |
66 | ``` 67 | 譬如,如果基于mui的,就可以是`mui-scroller-wrapper`,其它的自己定义对应scroll样式即可 68 | 69 | * JS Initialization(JS初始化) 70 | 71 | ``` 72 | var pullToRefreshObj = new PullToRefreshTools.skin.default({ 73 | // 这里用默认设置 74 | container: '#pullrefresh', 75 | // down为null表示不要下拉刷新 76 | down: { 77 | callback: pullDownRefreshCallback, 78 | // 是否显示成功动画 79 | isSuccessTips: true, 80 | }, 81 | // up为null为不要上拉 82 | // 上拉有关 83 | up: { 84 | // 是否自动上拉加载-初始化是是否自动 85 | auto: true, 86 | 87 | callback: pullUpRefreshCallback 88 | }, 89 | scroll: { 90 | bounceTime: 500, 91 | // 回弹动画时间 92 | // 下拉刷新和上拉加载成功动画的时间 93 | successAnimationTime: 500 94 | }, 95 | }); 96 | ``` 97 | 具体可以将`PullToRefreshTools.skin.default`换为其它皮肤,其它更多操作参考示例 98 | 99 | * API(暴露出来的方法) 100 | 101 | ``` 102 | * finished //这是一个属性,用来控制当前上拉加载是否可用 103 | * refresh() //重置状态。譬如上拉加载关闭后需要手动refresh重置finished状态 104 | * pulldownLoading() //主动触发一个下拉刷新的动画(同时会触发下拉回调) 105 | * pullupLoading() //主动触发一个上拉加载的动画(同时会触发上拉回调) 106 | * endPullDownToRefresh() //关闭下拉刷新动画,重置状态 107 | * endPullUpToRefresh(finished) //关闭上拉加载动画,重置状态,如果finished,则不允许在上拉,除非再次refresh() 108 | ``` 109 | 关于更多的使用说明(如自定义UI类的实现,请参考最后的更多说明) 110 | 111 | * (Notice)注意 112 | * `default`皮肤和`type1`皮肤依赖于`mui.css` 113 | * 其它皮肤依赖于样式文件`pulltorefresh.skin.css` 114 | * 另外,也支持`require`方式引入,`require`后,请通过全局变量方式来使用,如`PullToRefresh.skin.defaults` 115 | 116 | * (Global Variable)相应的全局变量与JS文件 117 | 118 | ``` 119 | IScroll // 内部的IScroll5保留的全局变量 120 | PullToRefreshTools.core // pulltorefresh.core.js,可以通过这个文件实现自定义皮肤 121 | PullToRefreshTools.skin.defaults // pulltorefresh.skin.default.js 需要和关键字区分 122 | PullToRefreshTools.skin.type1 // pulltorefresh.skin.type1.js 123 | PullToRefreshTools.skin.type2 // pulltorefresh.skin.type2.js 124 | PullToRefreshTools.skin.type3 // pulltorefresh.skin.type3.js 125 | PullToRefreshTools.skin.type4 // pulltorefresh.skin.type4.js 126 | PullToRefreshTools.skin.natives // pulltorefresh.skin.native.js 需要和保留字区分 127 | PullToRefreshTools.bizlogic // pulltorefresh.bizlogic.implxx.js 系列,依赖于核心下拉刷新文件(随便一个皮肤即可) 128 | ``` 129 | 130 | 131 | ### 关于 132 | 下拉刷新所有皮肤内部都默认引入了IScroll5 **但是进行了一些轻微改动(主要是增加了功能,用来方便下拉刷新的实现,并不影响原本使用)** 133 | 因此如果项目中其它地方有用到IScroll5,无需在引入,直接通过`IScroll`即可使用 134 | 135 | 后续会定期更新皮肤 136 | 137 | ### 更多说明 138 | 139 | * [examples目录结构说明](https://github.com/dailc/pulltorefresh-h5-iscroll/tree/master/examples/html) 140 | * [源码使用说明](https://github.com/dailc/pulltorefresh-h5-iscroll/tree/master/src/) 141 | 142 | #### 相关博文 143 | 144 | * [H5下拉刷新,多种皮肤,便于拓展!](http://www.jianshu.com/p/ef3183adb896) 145 | 146 | ## 更新日志 147 | 148 | * 20170410 149 | * 版本`1.0.0` 150 | * 增加cmd引入支持 151 | * 修复IScroll内部`maxScrollY`引起的冲突 152 | * 20170518 153 | * 修复关闭上拉加载后,重复下拉刷新报错的bug 154 | * 20170526 155 | * 版本`2.0.0` 156 | * 从源码层面重新修改命名空间 157 | * 后续命名层面不会再有大的改动 158 | * 20170601 159 | * 内部源码优化 160 | * 不影响以前的使用 161 | * 20170608 162 | * 修改项目名称,同步修改示例资源路径 163 | * 20170609 164 | * 源码目录结构微调,不影响使用 165 | * 20170612 166 | * showcase将`targetPullToRefresh`简化为`skin` 167 | * 20170615 168 | * 版本`3.0.0` 169 | * API设计简化 170 | * 去除不推荐使用的mui皮肤 171 | * IScroll打包到内部 172 | * 20170621 173 | * 更新`native`皮肤引入时的bug-由于重复打包core,导致命名冲突 174 | * 20170626 175 | * 下拉刷新内部优化,调用方式默认变为`new`的使用方式 176 | * 20170711 177 | * 下拉刷新`bizlogic`的重构,优化代码 178 | * 20170814 179 | * 新增`type5`皮肤,仿照微信小程序的下拉效果 180 | * 20170830 181 | * 这个库已经不再维护,全力维护更好的下拉刷新[minirefresh](https://github.com/minirefresh/minirefresh) 182 | 183 | ## License (MIT) -------------------------------------------------------------------------------- /dist/_debug/css/images/img_pulltorefresh_skin2_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dailc/pulltorefresh-h5-iscroll/f221a5e60e6f6c1c955e99f3ea59c1f373ff08ae/dist/_debug/css/images/img_pulltorefresh_skin2_1.png -------------------------------------------------------------------------------- /dist/_debug/css/images/img_pulltorefresh_skin2_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dailc/pulltorefresh-h5-iscroll/f221a5e60e6f6c1c955e99f3ea59c1f373ff08ae/dist/_debug/css/images/img_pulltorefresh_skin2_2.png -------------------------------------------------------------------------------- /dist/_debug/css/images/img_pulltorefresh_skin2_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dailc/pulltorefresh-h5-iscroll/f221a5e60e6f6c1c955e99f3ea59c1f373ff08ae/dist/_debug/css/images/img_pulltorefresh_skin2_3.png -------------------------------------------------------------------------------- /dist/_debug/pulltorefresh.skin.native.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 作者: dailc 3 | * 创建时间: 2017/03/28 4 | * 版本: [1.0, 2017/05/26 ] 5 | * 版权: dailc 6 | * 描述: 这个皮肤是ejs或钉钉下的下拉刷新,使用了他们自带提供的原生下拉刷新,以及自定义生成上拉加载 7 | * 注意:分别在ejs下或者钉钉下依赖对应的库文件 8 | */ 9 | (function(exports, CommonTools) { 10 | 11 | var isSupportTouch = "ontouchend" in document ? true : false; 12 | //默认的下拉刷新每一个页面只支持一个,所以是单例模式 13 | var instance; 14 | //这里的isPullDown是指从上往下拉 15 | //pullup是指从下往上拉 16 | var isTouch = false, 17 | isPullDown = false; 18 | //定义一个global 19 | var global = {}; 20 | /** 21 | * 默认的设置参数 22 | */ 23 | var defaultSetting = { 24 | //下拉有关 25 | down: { 26 | callback: CommonTools.noop 27 | }, 28 | //上拉有关 29 | up: { 30 | //是否自动上拉加载-初始化是是否自动 31 | auto: false, 32 | //是否隐藏那个加载更多动画,达到默认加载效果 33 | show: true, 34 | contentrefresh: '正在加载...', 35 | callback: CommonTools.noop 36 | }, 37 | //注意,传给Mui时可以传 #id形式或者是 原生dom对象 38 | element: '#pullrefresh' 39 | }; 40 | var pulluploadingTips = '
{{contentrefresh}}
'; 41 | /** 42 | * @description 兼容ejs情况下的下拉刷新 43 | * 去除多余dom 44 | * @param {HTMLElement} elem 对应的dom 45 | */ 46 | function compatibleNative(elem) { 47 | //计划改变,ejs下拉刷新不使用scroll,否则不好计算什么时候可以滑动,而是直接去除这个dom 48 | // mui(elem).scroll({ 49 | // deceleration: 0.0005 //flick 减速系数,系数越大,滚动速度越慢,滚动距离越小,默认值0.0006 50 | // }); 51 | if (typeof elem === 'string') { 52 | elem = document.querySelector(elem); 53 | } 54 | if (CommonTools.os.ejs || CommonTools.os.dd) { 55 | //去除dom,ejs下拉刷新不需要scroll 56 | var pullRefreshDom = elem; 57 | pullRefreshDom.classList.remove('mui-scroll-wrapper'); 58 | var scrollDom = pullRefreshDom.querySelector('.mui-scroll'); 59 | if (scrollDom) { 60 | //pullRefreshDom.innerHTML = scrollDom.innerHTML; 61 | scrollDom.classList.remove('mui-scroll'); 62 | } 63 | } else { 64 | 65 | } 66 | 67 | } 68 | 69 | /** 70 | * @description 将string字符串转为html对象,默认创一个div填充 71 | * @param {String} strHtml 目标字符串 72 | * @return {HTMLElement} 返回处理好后的html对象,如果字符串非法,返回null 73 | */ 74 | function pareseStringToHtml(strHtml) { 75 | if (strHtml == null || typeof(strHtml) != "string") { 76 | return null; 77 | } 78 | //创一个灵活的div 79 | var i, a = document.createElement("div"); 80 | var b = document.createDocumentFragment(); 81 | a.innerHTML = strHtml; 82 | while (i = a.firstChild) b.appendChild(i); 83 | return b; 84 | } 85 | /** 86 | * @description 浏览器视口的高度 87 | * @return {Number} 返回具体高度 88 | */ 89 | function getWinHeight() { 90 | var windowHeight = 0; 91 | if (document.compatMode == "CSS1Compat") { 92 | windowHeight = document.documentElement.clientHeight; 93 | } else { 94 | windowHeight = document.body.clientHeight; 95 | } 96 | return windowHeight; 97 | } 98 | /** 99 | * @description 获取滚动条在Y轴上的滚动距离 100 | * @return {Number} 返回具体距离 101 | */ 102 | function getScrollTop() { 103 | var scrollTop = 0, 104 | bodyScrollTop = 0, 105 | documentScrollTop = 0; 106 | if (document.body) { 107 | bodyScrollTop = document.body.scrollTop || 0; 108 | } 109 | if (document.documentElement) { 110 | documentScrollTop = document.documentElement.scrollTop || 0; 111 | } 112 | scrollTop = (bodyScrollTop > documentScrollTop) ? bodyScrollTop : documentScrollTop; 113 | return scrollTop; 114 | } 115 | /** 116 | * @description 获取文档的总高度 117 | * @return {Number} 返回具体高度 118 | */ 119 | function getScrollHeight() { 120 | var scrollHeight = 0, 121 | bodyScrollHeight = 0, 122 | documentScrollHeight = 0; 123 | if (document.body) { 124 | bodyScrollHeight = document.body.scrollHeight; 125 | } 126 | 127 | if (document.documentElement) { 128 | documentScrollHeight = document.documentElement.scrollHeight; 129 | } 130 | scrollHeight = (bodyScrollHeight - documentScrollHeight > 0) ? bodyScrollHeight : documentScrollHeight; 131 | return scrollHeight; 132 | } 133 | /** 134 | * @constructor 构造函数 135 | * @description 定义下拉刷新构造函数 136 | * @param {HTMLElement} elem 137 | * @param {JSON} options 138 | */ 139 | function PullRefresh(options) { 140 | options = CommonTools.extend(true, {}, defaultSetting, options); 141 | 142 | var $this = this; 143 | var elem = options.container; 144 | $this.loadingUp = false; 145 | $this.finished = false; 146 | $this.options = options; 147 | if (typeof elem === 'string') { 148 | elem = document.querySelector(elem); 149 | } 150 | $this.elem = elem; 151 | 152 | //增加class 153 | $this.elem.classList.add('pulltorefresh-native-type'); 154 | setPullDownFunc(); 155 | 156 | if ($this.options.down) { 157 | if (CommonTools.os.dd) { 158 | //钉钉的下拉刷新 159 | //开启下拉刷新 160 | dd.ui.pullToRefresh.enable({ 161 | onSuccess: function() { 162 | //alert('下拉刷新成功,立刻收起'); 163 | $this.options.down.callback && $this.options.down.callback(); 164 | }, 165 | onFail: function() { 166 | //alert('下拉刷新失败'); 167 | dd.ui.pullToRefresh.stop(); 168 | } 169 | }); 170 | } else { 171 | //ejs的下拉刷新 172 | //开启下拉刷新 173 | if (ejs.nativeUI) { 174 | ejs.nativeUI.pullToRefresh.enable(function() { 175 | $this.options.down.callback && $this.options.down.callback(); 176 | }); 177 | } else { 178 | ejs.ui.pullToRefresh.enable({ 179 | success: function() { 180 | $this.options.down.callback && $this.options.down.callback(); 181 | }, 182 | error: function() { 183 | //alert('下拉刷新失败'); 184 | ejs.ui.pullToRefresh.stop(); 185 | } 186 | }); 187 | } 188 | 189 | } 190 | } 191 | 192 | //兼容原生环境下的处理 193 | compatibleNative($this.elem || '#pullrefresh'); 194 | } 195 | /** 196 | * @description 主动上拉加载更多 197 | */ 198 | PullRefresh.prototype.pullupLoading = function() { 199 | if (instance.options.up) { 200 | //加载更多 201 | loadMoreAnimation(true); 202 | //触发上拉回调 203 | instance.options.up.callback && instance.options.up.callback(); 204 | } 205 | }; 206 | PullRefresh.prototype.endPullUpToRefresh = function(isNoMore) { 207 | if (isNoMore) { 208 | this.finished = true; 209 | } 210 | if (instance.options.up) { 211 | //去除加载更多动画 212 | loadMoreAnimation(false); 213 | } 214 | }; 215 | PullRefresh.prototype.endPullDownToRefresh = function() { 216 | //关闭下拉刷新 217 | if (CommonTools.os.dd) { 218 | dd.ui.pullToRefresh.stop(); 219 | } else if (CommonTools.os.ejs) { 220 | if (ejs.nativeUI) { 221 | ejs.nativeUI.pullToRefresh.stop(); 222 | } else { 223 | ejs.ui.pullToRefresh.stop(); 224 | } 225 | } 226 | 227 | }; 228 | PullRefresh.prototype.refresh = function(refresh) { 229 | this.finished = false; 230 | }; 231 | /** 232 | * @description 设置下拉刷新相关 233 | */ 234 | function setPullDownFunc() { 235 | var x = 0, 236 | y = 0, 237 | scrollTop = 0; 238 | var b = document.body; 239 | //监听touch时间,模拟下拉 240 | var touchStartEvt = isSupportTouch ? 'touchstart' : 'mousedown'; 241 | b.addEventListener(touchStartEvt, function(evt) { 242 | //console.log('touchstart'); 243 | var touch; 244 | if (isSupportTouch) { 245 | touch = evt.touches[0]; //获取第一个触点 246 | } else { 247 | touch = evt; 248 | } 249 | x = Number(touch.pageX); //页面触点X坐标 250 | y = Number(touch.pageY); //页面触点Y坐标 251 | scrollTop = b.scrollTop; 252 | isTouch = true; 253 | //console.log('x = ' + x); 254 | //console.log('y = ' + y); 255 | //console.log('scrollTop = ' + scrollTop); 256 | }); 257 | var touchEndEvt = isSupportTouch ? 'touchend' : 'mouseup'; 258 | b.addEventListener(touchEndEvt, function(evt) { 259 | //console.log('touchend'); 260 | isTouch = false; 261 | isPullDown = false; 262 | }); 263 | var touchMoveEvt = isSupportTouch ? 'touchmove' : 'mousemove'; 264 | b.addEventListener(touchMoveEvt, function(evt) { 265 | //console.log('touchmove'); 266 | var touch; 267 | if (isSupportTouch) { 268 | touch = evt.touches[0]; //获取第一个触点 269 | } else { 270 | touch = evt; 271 | } 272 | var mx = Number(touch.pageX); //页面触点X坐标 273 | var my = Number(touch.pageY); //页面触点Y坐标 274 | 275 | //console.log("isTouch = " + isTouch) 276 | //console.log("y-my = " + (y-my)) 277 | //console.log("b.scrollTop = " + b.scrollTop); 278 | if (isTouch) { 279 | if (my - y > 30) { 280 | if (b.scrollTop == 0) { 281 | if (!isPullDown) { 282 | console.log("PullDown"); 283 | isPullDown = true; 284 | } 285 | } 286 | } 287 | //alert('scrollTop:'+scrollTop+',b.scrollTop:'+b.scrollTop+',y:'+y+',my:'+my); 288 | if (y - my > 100) { 289 | 290 | if (scrollTop == b.scrollTop) { 291 | if (!instance.loadingUp) { 292 | //console.log('my = ' + my); 293 | //console.log('y = ' + y); 294 | //console.log('scrollTop = ' + scrollTop); 295 | //console.log('b.scrollTop = ' + b.scrollTop); 296 | //console.log("pullup,finish:"+instance.finished); 297 | if (!instance.finished) { 298 | if (instance.options.up) { 299 | //加载更多 300 | loadMoreAnimation(true); 301 | //触发上拉回调 302 | instance.options.up.callback && instance.options.up.callback(); 303 | } 304 | } 305 | 306 | } 307 | } 308 | } 309 | 310 | } 311 | }); 312 | 313 | //ios下的补救措施 314 | var scrollFunc = function() { 315 | var y = getScrollTop(); 316 | var slider = document.getElementById('sliderSegmentedControl'); 317 | 318 | if ((y + getWinHeight()) === getScrollHeight()) { 319 | if (!instance.loadingUp) { 320 | if (!instance.finished) { 321 | if (instance.options.up) { 322 | //加载更多 323 | loadMoreAnimation(true); 324 | //触发上拉回调 325 | instance.options.up.callback && instance.options.up.callback(); 326 | } 327 | } 328 | } 329 | 330 | } 331 | }; 332 | document.onscroll = scrollFunc; 333 | } 334 | /** 335 | * @description 控制加载更多 336 | * @param {Boolean} more 337 | */ 338 | function loadMoreAnimation(more) { 339 | 340 | var dom = instance.elem; 341 | if (!dom) { 342 | return; 343 | } 344 | 345 | if (more) { 346 | if (!instance.loadingUp) { 347 | //显示loading 348 | var content = instance.options.up.contentrefresh || '正在加载...'; 349 | pulluploadingTips = pulluploadingTips.replace('{{contentrefresh}}', content); 350 | dom.appendChild(pareseStringToHtml(pulluploadingTips)); 351 | instance.loadingUp = true; 352 | } 353 | } else { 354 | if (instance.loadingUp) { 355 | //隐藏loading 356 | var loadingDom = dom.querySelector('.mui-pull-bottom-tips'); 357 | loadingDom && loadingDom.parentNode.removeChild(loadingDom); 358 | instance.loadingUp = false; 359 | } 360 | } 361 | } 362 | 363 | function initPullToRefresh(options) { 364 | if(instance) { 365 | return instance; 366 | } 367 | instance = new PullRefresh(options); 368 | 369 | if (options.up && options.up.auto) { 370 | // 如果设置了auto,则自动上拉一次 371 | instance.pullupLoading(); 372 | } 373 | 374 | return instance; 375 | } 376 | 377 | CommonTools.namespace('skin.natives', initPullToRefresh); 378 | 379 | })({}, PullToRefreshTools); -------------------------------------------------------------------------------- /dist/css/images/img_pulltorefresh_skin2_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dailc/pulltorefresh-h5-iscroll/f221a5e60e6f6c1c955e99f3ea59c1f373ff08ae/dist/css/images/img_pulltorefresh_skin2_1.png -------------------------------------------------------------------------------- /dist/css/images/img_pulltorefresh_skin2_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dailc/pulltorefresh-h5-iscroll/f221a5e60e6f6c1c955e99f3ea59c1f373ff08ae/dist/css/images/img_pulltorefresh_skin2_2.png -------------------------------------------------------------------------------- /dist/css/images/img_pulltorefresh_skin2_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dailc/pulltorefresh-h5-iscroll/f221a5e60e6f6c1c955e99f3ea59c1f373ff08ae/dist/css/images/img_pulltorefresh_skin2_3.png -------------------------------------------------------------------------------- /dist/css/pulltorefresh.skin.css: -------------------------------------------------------------------------------- 1 | /** 2 | * pulltorefresh-h5-iscroll - 一款基于IScroll5的H5下拉刷新实现,包括多种皮肤的实现 3 | * @version v3.0.0 4 | * @author 5 | */ 6 | .hidden{visibility:hidden}.pull-bottom-pocket,.pull-top-pocket{width:100%;height:74px;font-weight:700;font-size:.8em;background:inherit;overflow:hidden}.pull-block{text-align:center}.pull-caption{display:inline-block;margin-left:5px;text-align:center;vertical-align:middle;color:#777}.pull-bottom-pocket.loading .pull-loading-icon,.pulltorefresh-type2 .pull-top-pocket .pull-loading-icon{display:inline-block;vertical-align:middle;opacity:.5;width:40px;height:40px;background:url(images/img_pulltorefresh_skin2_1.png) 0 0 no-repeat;-webkit-background-size:40px 80px;-ms-background-size:40px 80px;background-size:40px 80px;-webkit-transition-property:-webkit-transform;-ms-transition-property:-webkit-transform;-webkit-transition-duration:250ms;-ms-transition-duration:250ms}.pulltorefresh-type2 .pull-top-pocket .pull-loading-icon{-webkit-transform:rotate(0) translateZ(0);-ms-transform:rotate(0) translateZ(0)}.pulltorefresh-type2 .pull-top-pocket.flip .pull-loading-icon{-webkit-transform:rotate(-180deg) translateZ(0);-ms-transform:rotate(-180deg) translateZ(0)}.pulltorefresh-type2 .pull-top-pocket.success .pull-loading-icon{display:inline-block;width:30px;height:30px;background:url(images/img_pulltorefresh_skin2_3.png) 0 0 no-repeat}.pulltorefresh-type2 .pull-top-pocket.error .pull-loading-icon{display:inline-block;width:30px;height:30px;background:url(images/img_pulltorefresh_skin2_2.png) 0 0 no-repeat}.pull-bottom-pocket.loading .pull-loading-icon,.pulltorefresh-type2 .loading .pull-loading-icon{background-position:0 100%;-webkit-transform:rotate(0) translateZ(0);-ms-transform:rotate(0) translateZ(0);-webkit-transition-duration:0s;-ms-transition-duration:0s;-webkit-animation-name:loading;-ms-animation-name:loading;-webkit-animation-duration:1s;-ms-animation-duration:1s;-webkit-animation-iteration-count:infinite;-ms-animation-iteration-count:infinite;-webkit-animation-timing-function:linear;-ms-animation-timing-function:linear}.pull-bottom-pocket.nomore .pull-loading-icon{display:none}.pulltorefresh-type3 .pull-top-pocket .pull-loading-icon{position:absolute;top:10px;left:50%;margin-left:-50px;width:40px;height:40px;border-radius:100%;z-index:100}.pulltorefresh-type3 .pull-top-pocket .pull-caption{position:absolute;top:10px;left:50%;line-height:40px}.pulltorefresh-type3 .pull-top-canvas{overflow:hidden;background-color:#fafafa;border-radius:40px;box-shadow:0 4px 10px #bbb;width:40px;height:40px;margin:0 auto}.pulltorefresh-type3 .pull-top-canvas canvas{width:40px}@-webkit-keyframes loading{from{-webkit-transform:rotate(0) translateZ(0)}to{-webkit-transform:rotate(360deg) translateZ(0)}}@-ms-keyframes loading{from{-ms-transform:rotate(0) translateZ(0)}to{-ms-transform:rotate(360deg) translateZ(0)}}.pulltorefresh-type4 .pull-top-pocket{position:absolute;height:100%}.pulltorefresh-type4 .pull-top-pocket .pull-caption{position:absolute;top:40px;width:100%;margin-left:0;line-height:40px;color:#d00324;text-align:center}.pulltorefresh-type4 .pie-anim-container{position:absolute;width:100%}.pie-anim-container .pie{position:absolute;width:40px;height:40px;left:50%;top:50%;margin-left:-20px;margin-top:-20px;border-radius:50%;-webkit-border-radius:50%;background:#d00324;color:#f2f3f4}.pie-anim-container .left-pie{display:block;background:currentColor;position:#d00324;left:0;top:0;height:100%;border-radius:100% 0 0 100%/50%;border-width:0;width:50%;transform-origin:left;-webkit-transform-origin:left;-moz-transform-origin:left}.pie-anim-container .anim-pie{position:absolute;top:0;left:0;width:50%;height:100%;display:block;border-width:0;border-radius:100% 0 0 100%/50%;-webkit-border-radius:100% 0 0 100%/50%;background:#d00324;transform-origin:right;-webkit-transform-origin:right;-moz-transform-origin:right}.pie-anim-container .fresh-progress{position:absolute;left:50%;top:50%;width:24px;height:24px;display:block;margin-left:-12px;margin-top:-12px;border:3px solid transparent;border-radius:50%;-webkit-border-radius:50%;border-right-color:#fff;border-top-color:#fff;border-left-color:#fff;border-left:2px;z-index:10}.pie-anim-container .progress-arrow{position:absolute;bottom:-4px;right:-1px;width:0;height:0;border-top:6px solid transparent;border-bottom:6px solid transparent;border-right:6px solid #fff;transform:rotate(-48deg);-webkit-transform:rotate(-48deg);-moz-transform:rotate(-48deg)}.pie-anim-container .error-tips{position:absolute;left:50%;top:50%;margin-left:-7px;margin-top:-15px;opacity:0}.pie-anim-container .error-tips .txt{color:#fff}.pie-anim-container .error-tips .left.txt{margin-left:-5px}.pie-anim-container .error-tips .right.txt{margin-left:10px}.pie-anim-container .moon{position:absolute;left:-2px;top:22px;width:18px;height:18px;border-radius:38%;-webkit-border-radius:38%;box-shadow:0 -3px 0 0 #fff;-webkit-box-shadow:0 -3px 0 0 #fff}.pie-anim-container .success-tips{opacity:0}.pie-anim-container .arrow-left{position:absolute;top:50%;left:50%;width:0;height:0;margin-left:-7px;margin-top:-11px;border-top:10px solid transparent;border-bottom:15px solid transparent;border-right:15px solid #fff;transform:rotate(-88deg);-webkit-transform:rotate(-88deg);-moz-transform:rotate(-88deg)}.pie-anim-container .arrow-right{position:absolute;top:50%;left:50%;width:0;height:0;margin-left:-9px;margin-top:-17px;border-top:15px solid transparent;border-bottom:15px solid transparent;border-right:15px solid #d00324;transform:rotate(-88deg);-webkit-transform:rotate(-88deg);-moz-transform:rotate(-88deg)}@keyframes progressrotate{0%{-webkit-transform:rotateZ(0);transform:rotateZ(0);-moz-transform:rotate(0)}100%{-webkit-transform:rotateZ(360deg);transform:rotateZ(360deg);-moz-transform:rotate(360deg)}}@-webkit-keyframes progressrotate{0%{-webkit-transform:rotateZ(0);transform:rotateZ(0);-moz-transform:rotate(0)}100%{-webkit-transform:rotateZ(360deg);transform:rotateZ(360deg);-moz-transform:rotate(360deg)}}@keyframes piebackground{0%{background:#d00324}100%{background:currentcolor}}@-webkit-keyframes piebackground{0%{background:#d00324}100%{background:currentcolor}}@keyframes pieanimaindex{0%{z-index:10;transform:rotate(360deg);-webkit-transform:rotate(360deg);-moz-transform:rotate(360deg)}50%{z-index:0;transform:rotate(180deg);-webkit-transform:rotate(180deg);-moz-transform:rotate(180deg)}100%{z-index:0;transform:rotate(0);-webkit-transform:rotate(0);-moz-transform:rotate(0)}}@-webkit-keyframes pieanimaindex{0%{z-index:10;transform:rotate(360deg);-webkit-transform:rotate(360deg);-moz-transform:rotate(360deg)}50%{z-index:0;transform:rotate(180deg);-webkit-transform:rotate(180deg);-moz-transform:rotate(180deg)}100%{z-index:0;transform:rotate(0);-webkit-transform:rotate(0);-moz-transform:rotate(0)}}.pulltorefresh-mui-type .mui-pull-top-tips{position:absolute;top:-20px;left:50%;margin-left:-25px;width:40px;height:40px;border-radius:100%;z-index:100}.pulltorefresh-mui-type .mui-bar~.mui-pull-top-tips{top:24px}.pulltorefresh-mui-type .mui-pull-top-wrapper{width:42px;height:42px;display:block;text-align:center;background-color:#efeff4;border:1px solid #ddd;border-radius:25px;background-clip:padding-box;box-shadow:0 4px 10px #bbb;overflow:hidden}.pulltorefresh-mui-type .mui-pull-top-tips.mui-transitioning{-webkit-transition-duration:.2s;transition-duration:.2s}.pulltorefresh-mui-type .mui-pull-top-tips .mui-pull-loading{margin:0}.pulltorefresh-mui-type .mui-pull-top-wrapper .mui-icon,.pulltorefresh-mui-type .mui-pull-top-wrapper .mui-spinner{margin-top:7px}.pulltorefresh-mui-type .mui-pull-bottom-tips{text-align:center;background-color:inherit;font-size:15px;line-height:40px;color:#777}.pulltorefresh-mui-type .mui-pull-top-canvas{overflow:hidden;background-color:#fafafa;border-radius:40px;box-shadow:0 4px 10px #bbb;width:40px;height:40px;margin:0 auto}.pulltorefresh-mui-type .mui-pull-top-canvas canvas{width:40px}.pulltorefresh-native-type .mui-pull-bottom-tips{position:relative;height:auto;width:100%;background-color:inherit;font-size:15px;color:#777}.pulltorefresh-native-type .mui-pull-bottom-tips .mui-spinner{position:absolute;top:0;left:40%}.pulltorefresh-native-type .mui-pull-bottom-tips .mui-pull-loading{position:absolute;top:2px;left:40%;margin-left:30px}.pulltorefresh-type5 .pull-top-pocket{position:absolute;top:0;left:0;height:50px}.ball-beat>div{opacity:.3;background-color:#444;width:8px;height:8px;border-radius:100%;margin:2px;display:inline-block}.loading .ball-beat div{-webkit-animation-fill-mode:both;animation-fill-mode:both;-webkit-animation:ball-beat 1s 0s infinite linear;animation:ball-beat 1s 0s infinite linear}.loading .ball-beat>div:nth-child(2){-webkit-animation-delay:.3s!important;animation-delay:.3s!important}.loading .ball-beat>div:nth-child(3){-webkit-animation-delay:.6s!important;animation-delay:.6s!important}.pulltorefresh-type5 .pull-block{margin-top:10px}.pulltorefresh-type5 .pull-top-pocket .pull-block{margin-top:20px}.pulltorefresh-type5 .pull-caption{margin-left:0;font-size:13px}.pulltorefresh-type5 .loading .pull-loading-icon{display:inline-block;width:16px;height:16px;background:0 0;border-radius:50%;border:1px solid gray;margin-right:8px;border-bottom-color:transparent;vertical-align:middle}.pulltorefresh-type5 .loading .rotate{-webkit-animation:progressrotate .6s linear infinite;animation:progressrotate .6s linear infinite}@-webkit-keyframes ball-beat{50%{opacity:1;-webkit-transform:scale(.75);transform:scale(.75)}100%{opacity:.3;-webkit-transform:scale(1);transform:scale(1)}}@keyframes ball-beat{50%{opacity:1;-webkit-transform:scale(.75);transform:scale(.75)}100%{opacity:.3;-webkit-transform:scale(1);transform:scale(1)}} -------------------------------------------------------------------------------- /dist/pulltorefresh.bizlogic.impl.js: -------------------------------------------------------------------------------- 1 | /** 2 | * pulltorefresh-h5-iscroll - 一款基于IScroll5的H5下拉刷新实现,包括多种皮肤的实现 3 | * @version v3.0.0 4 | * @author 5 | */ 6 | !function(e,t){!function(){e.dataProcessFn=[],e.dataProcess=function(t,s){s=s||{};var n=[].slice.call(arguments),i={code:0,message:"",data:null,status:0,debugInfo:{type:"未知数据格式"}};if(null==s.dataPath)return t;"string"==typeof s.dataPath&&(s.dataPath=[s.dataPath]);var a=s.isDebug||!1,o=s.dataPath,r=e.dataProcessFn,l=r.length,u=o.length,c=!1;if(!t)return i.message="接口返回数据为空!",i;n.push(i);for(var h=0;!c&&h0){for(var i="",a=0,o=e.length;a=n.initPageIndex?this.currPage:n.initPageIndex,n.error&&n.error(e,t,s)},_successRequest:function(e){var t=this.options;if(!e)return t.isDebug&&console.log("warning***返回的数据为空,请注意!"),this.isShouldNoMoreData=!1,void this._refreshState(!1);t.isDebug&&console.log("下拉刷新返回数据:"+JSON.stringify(e)),e=t.dataChange?t.dataChange(e):this._dataChangeDefault(e);var s=this._render(e);this.loadingMoreSuccess&&(this.loadingMoreSuccess(),this.loadingMoreSuccess=null),t.success&&"function"==typeof t.success&&t.success(e,this.isPullDown||this.currPage==t.initPageIndex),this._refreshState(!0,s)},_refreshState:function(e,t){var s=this.instance;t=t||0,s.setSuccessTips&&s.setSuccessTips("更新"+t+"条数据"),this.isPullDown&&(s.endPullDownToRefresh(e),s.finished&&(s.refresh(!0),this.isShouldNoMoreData=!0)),this.isShouldNoMoreData?s.endPullUpToRefresh(!1,e):s.endPullUpToRefresh(!0,e),this.loadingDown=!1,this.loadingUp=!1},_addEvent:function(){var e=this.listContainer,t=this.options,s=t.itemClick;"function"==typeof s&&mui(e).on(a,t.itemSelector,s)},refresh:function(){var e=this.options;e.up&&this.instance.enablePullUp?this.loadingUp||(this._clearResponseEl(),this.currPage=e.initPageIndex-1,this.loadingMore()):(this._clearResponseEl(),this._pullDownCallback())},loadingMore:function(e){var t=this.instance;this.loadingMoreSuccess=e,this.instance.finished&&(this.instance.refresh(!0),this.isShouldNoMoreData=!0),t.pullupLoading()},disablePullupToRefresh:function(){this.pullToRefreshInstance.disablePullupToRefresh()},enablePullupToRefresh:function(){this.pullToRefreshInstance.enablePullupToRefresh()},destroy:function(){mui(listContainer).off(),this.container=null,this.listContainer=null,this.instance=null,this.options=null}},e.namespace("bizlogic",s)}(PullToRefreshTools); -------------------------------------------------------------------------------- /dist/pulltorefresh.bizlogic.impl2.js: -------------------------------------------------------------------------------- 1 | /** 2 | * pulltorefresh-h5-iscroll - 一款基于IScroll5的H5下拉刷新实现,包括多种皮肤的实现 3 | * @version v3.0.0 4 | * @author 5 | */ 6 | !function(e,t){!function(){e.dataProcessFn=[],e.dataProcess=function(t,o){o=o||{};var s=[].slice.call(arguments),n={code:0,message:"",data:null,status:0,debugInfo:{type:"未知数据格式"}};if(null==o.dataPath)return t;"string"==typeof o.dataPath&&(o.dataPath=[o.dataPath]);var a=o.isDebug||!1,i=o.dataPath,l=e.dataProcessFn,r=l.length,c=i.length,u=!1;if(!t)return n.message="接口返回数据为空!",n;s.push(n);for(var p=0;!u&&p=s.defaultInitPageNum?s.currPage:s.defaultInitPageNum,s.options.bizlogic.errorRequestCallback&&s.options.bizlogic.errorRequestCallback(e,t,o)},s.prototype.successRequest=function(e,t){var s=this;if(!e)return s.options.isDebug&&console.log("warning***返回的数据为空,请注意!"),s.isShouldNoMoreData=!1,void s.refreshState(!1);if(s.options.isDebug&&console.log("下拉刷新返回数据:"+JSON.stringify(e)),e=s.options.bizlogic.changeResponseDataCallback?s.options.bizlogic.changeResponseDataCallback(e):s.defaultChangeResponseData(e),s.options.bizlogic.isRendLitemplateAuto){s.isPullDown&&s.clearResponseEl();var n=0;if(window.Mustache)if(e&&Array.isArray(e)&&e.length>0){for(var a="",i=0;in?o:n}function i(){var e=0,o=0,n=0;return document.body&&(o=document.body.scrollHeight),document.documentElement&&(n=document.documentElement.scrollHeight),e=o-n>0?o:n}function s(e){e=o.extend(!0,{},h,e);var t=this,l=e.container;t.loadingUp=!1,t.finished=!1,t.options=e,"string"==typeof l&&(l=document.querySelector(l)),t.elem=l,t.elem.classList.add("pulltorefresh-native-type"),c(),t.options.down&&(o.os.dd?dd.ui.pullToRefresh.enable({onSuccess:function(){t.options.down.callback&&t.options.down.callback()},onFail:function(){dd.ui.pullToRefresh.stop()}}):ejs.nativeUI?ejs.nativeUI.pullToRefresh.enable(function(){t.options.down.callback&&t.options.down.callback()}):ejs.ui.pullToRefresh.enable({success:function(){t.options.down.callback&&t.options.down.callback()},error:function(){ejs.ui.pullToRefresh.stop()}})),n(t.elem||"#pullrefresh")}function c(){var e=0,o=0,n=0,t=document.body,s=d?"touchstart":"mousedown";t.addEventListener(s,function(l){var u;u=d?l.touches[0]:l,e=Number(u.pageX),o=Number(u.pageY),n=t.scrollTop,m=!0});var c=d?"touchend":"mouseup";t.addEventListener(c,function(e){m=!1,f=!1});var p=d?"touchmove":"mousemove";t.addEventListener(p,function(e){var l;l=d?e.touches[0]:e;var u=(Number(l.pageX),Number(l.pageY));m&&(u-o>30&&0==t.scrollTop&&(f||(console.log("PullDown"),f=!0)),o-u>100&&n==t.scrollTop&&(a.loadingUp||a.finished||a.options.up&&(r(!0),a.options.up.callback&&a.options.up.callback())))});var h=function(){var e=u();document.getElementById("sliderSegmentedControl");e+l()===i()&&(a.loadingUp||a.finished||a.options.up&&(r(!0),a.options.up.callback&&a.options.up.callback()))};document.onscroll=h}function r(e){var o=a.elem;if(o)if(e){if(!a.loadingUp){var n=a.options.up.contentrefresh||"正在加载...";v=v.replace("{{contentrefresh}}",n),o.appendChild(t(v)),a.loadingUp=!0}}else if(a.loadingUp){var l=o.querySelector(".mui-pull-bottom-tips");l&&l.parentNode.removeChild(l),a.loadingUp=!1}}function p(e){return a?a:(a=new s(e),e.up&&e.up.auto&&a.pullupLoading(),a)}var a,d="ontouchend"in document,m=!1,f=!1,h={down:{callback:o.noop},up:{auto:!1,show:!0,contentrefresh:"正在加载...",callback:o.noop},element:"#pullrefresh"},v='
{{contentrefresh}}
';s.prototype.pullupLoading=function(){a.options.up&&(r(!0),a.options.up.callback&&a.options.up.callback())},s.prototype.endPullUpToRefresh=function(e){e&&(this.finished=!0),a.options.up&&r(!1)},s.prototype.endPullDownToRefresh=function(){o.os.dd?dd.ui.pullToRefresh.stop():o.os.ejs&&(ejs.nativeUI?ejs.nativeUI.pullToRefresh.stop():ejs.ui.pullToRefresh.stop())},s.prototype.refresh=function(e){this.finished=!1},o.namespace("skin.natives",p)}({},PullToRefreshTools); -------------------------------------------------------------------------------- /examples/README.md: -------------------------------------------------------------------------------- 1 | ### 说明 2 | 这里是showcase,更多说明请参考src目录的README -------------------------------------------------------------------------------- /examples/css/common.css: -------------------------------------------------------------------------------- 1 | /** 2 | * 公用css 3 | */ 4 | 5 | body, 6 | .mui-content { 7 | background-color: #FFFFFF; 8 | } 9 | 10 | 11 | /*重写头部导航栏bar背景色,颜色为 187bc2*/ 12 | 13 | .mui-bar-nav { 14 | background-color: #187bc2; 15 | z-index: 20; 16 | } 17 | 18 | 19 | /*重写title的颜色,666666 20 | title大小为18px*/ 21 | 22 | .mui-title { 23 | font-size: 18px; 24 | color: #ffffff; 25 | } 26 | 27 | 28 | /*重写返回按钮颜色*/ 29 | 30 | .mui-icon-left-nav { 31 | color: #FFFFFF; 32 | } 33 | 34 | 35 | /*右侧的info图标*/ 36 | 37 | .mui-icon-info-filled { 38 | color: #FFFFFF; 39 | } 40 | 41 | 42 | /*右侧文字按钮白色*/ 43 | 44 | .mui-bar-nav .mui-text-right { 45 | color: #FFFFFF; 46 | } 47 | 48 | 49 | /*重写按钮*/ 50 | 51 | .mui-btn-block { 52 | height: 45px; 53 | line-height: 45px; 54 | padding: 0; 55 | } 56 | 57 | .common-hidden { 58 | display: none; 59 | } 60 | 61 | 62 | /** 63 | * 64 | */ 65 | 66 | .tips-container { 67 | width: 100%; 68 | font-size: 15px; 69 | overflow: hidden; 70 | word-break: break-all; 71 | } 72 | 73 | .compare-container { 74 | width: 44%; 75 | } 76 | 77 | .tips-content { 78 | display: block; 79 | color: #007aff; 80 | } 81 | 82 | .show-img img { 83 | width: 200px; 84 | height: 200px; 85 | border: 1px solid #FAFAFA; 86 | } 87 | 88 | .red { 89 | color: red; 90 | } 91 | 92 | .f20 { 93 | font-size: 20px; 94 | } 95 | 96 | .m50 { 97 | margin-top: 50px; 98 | } 99 | 100 | .m15 { 101 | margin: 15px; 102 | } 103 | .mt10 { 104 | margin-top: 10px; 105 | } 106 | .mt35 { 107 | margin-top: 35px; 108 | } 109 | 110 | .mt50 { 111 | margin-top: 50px; 112 | } 113 | 114 | .pt0, 115 | .mui-bar-nav~.pt0 { 116 | padding-top: 0; 117 | } 118 | 119 | img { 120 | max-width: 100%; 121 | } 122 | 123 | /** 124 | * 最小化,变为1*1px挂在左上角 125 | * 之所以要这样是,有些原生如果隐藏不见,无法触发事件 126 | * 比如input file在4.x中隐藏不见无法触发click函数 127 | */ 128 | .minSize { 129 | position: absolute; 130 | width: 1px; 131 | height: 1px; 132 | padding: 0; 133 | margin: 0; 134 | border: 0; 135 | opacity: 0; 136 | } 137 | 138 | 139 | -------------------------------------------------------------------------------- /examples/css/common_search.css: -------------------------------------------------------------------------------- 1 | /** 2 | * 定义搜索的公用样式 3 | * dailc 4 | */ 5 | .fixed-searchbar { 6 | /*变为absolute布局,防止ios里有bug*/ 7 | position: absolute; 8 | top: 0; 9 | left: 0; 10 | width: 100%; 11 | z-index: 1; 12 | } 13 | 14 | /*************搜索栏目相关**********************/ 15 | /***通用样式***/ 16 | .common-input-row { 17 | position: relative; 18 | height: 44px; 19 | padding: 6px 10px 6px 10px; 20 | background-color: #eaeaea; 21 | overflow: hidden; 22 | } 23 | .common-input-row .btn-search { 24 | display: block; 25 | float: left; 26 | width: 22px; 27 | height: 22px; 28 | margin: 4px 0 0 8px; 29 | color: #333; 30 | } 31 | .common-input-row .btn-search:active { 32 | color: #8cd2a3; 33 | } 34 | /*输入框*/ 35 | .common-input-row input[type=text].common-input-clear { 36 | max-width: 80%; 37 | height: 30px; 38 | margin: 0 0 0 8px; 39 | padding-left: 2px; 40 | font-size: 12px; 41 | line-height: 14px; 42 | border: 0; 43 | } 44 | .common-input-row .common-input { 45 | background-color: #fff; 46 | border: 1px solid #e0e0e0; 47 | border-radius: 30px; 48 | } 49 | /***样式1***/ 50 | /*目前无自定义*/ 51 | /***样式2***/ 52 | /*样式2的搜索框*/ 53 | .common-input-type2 .common-input { 54 | margin-right: 30px; 55 | /*定制下border*/ 56 | border-radius: 5px; 57 | } 58 | /*样式2 中的右侧文字*/ 59 | .common-input-type2 .btn-search-txt { 60 | position: absolute; 61 | right: 5px; 62 | top: 10px; 63 | font-size: 14px; 64 | color: #000000; 65 | } 66 | .common-input-type2 .btn-search-txt:active { 67 | color: #333333; 68 | } 69 | /***样式3***/ 70 | .show-simple3 { 71 | background: #469bfe; 72 | } 73 | .common-input-type3 { 74 | height: 52px; 75 | padding: 12px 7px; 76 | border: 0; 77 | background: transparent; 78 | } 79 | .common-input-type3 .common-input { 80 | height: 28px; 81 | font-size: 15px; 82 | border: 0; 83 | border-radius: 6px; 84 | background: #2684f0; 85 | text-align: center; 86 | } 87 | .common-input-type3 .btn-search, 88 | .common-input-type3 .btn-search-txt { 89 | display: inline-block; 90 | float: none; 91 | color: #fff; 92 | } 93 | .common-input-type3 .btn-search { 94 | font-size: 20px; 95 | } 96 | .common-input-type3 .btn-search-txt:active, 97 | .common-input-type3 .btn-search:active { 98 | color: #469bfe; 99 | } 100 | /***样式4,mui的搜索,思路为两层,一层是搜索,一层是placeholder***/ 101 | /*去除背景*/ 102 | .common-input-type4 .mui-input-row { 103 | margin: 7px; 104 | } 105 | .common-input-type4 .mui-placeholder { 106 | color: #f0f0f0; 107 | } 108 | 109 | .common-input-type4 input[type=search] { 110 | background-color: #f0f0f0; 111 | } 112 | -------------------------------------------------------------------------------- /examples/css/demo_pullRefresh_skin_bizlogic_multi.css: -------------------------------------------------------------------------------- 1 | /** 2 | * div 自定义下拉刷新 3 | */ 4 | 5 | .mui-bar~.mui-content .mui-fullscreen { 6 | top: 44px; 7 | height: auto; 8 | } 9 | 10 | 11 | /******************滑动栏目***************************/ 12 | 13 | .mui-control-content { 14 | background-color: #f5f5f5; 15 | } 16 | 17 | .mui-control-content .mui-loading { 18 | margin-top: 50px; 19 | } 20 | 21 | 22 | /*重写一个比较短的滑动条*/ 23 | 24 | .mui-segmented-control.mui-segmented-control-inverted~.mui-slider-progress-bar { 25 | background-color: transparent; 26 | } 27 | 28 | .mui-segmented-control.mui-segmented-control-inverted~.mui-slider-progress-bar .custom-progressbar { 29 | height: 2px; 30 | width: 60%; 31 | margin: auto; 32 | background-color: #558fff; 33 | } 34 | 35 | .mui-segmented-control .mui-active { 36 | color: #558fff; 37 | } 38 | 39 | 40 | /*栏目标题*/ 41 | 42 | .mui-control-item { 43 | position: relative; 44 | font-size: 15px; 45 | color: #666666; 46 | } 47 | 48 | 49 | /*绘制标题右侧的线*/ 50 | 51 | .control-line-item:after { 52 | position: absolute; 53 | top: 25%; 54 | left: 0; 55 | width: 1px; 56 | height: 50%; 57 | content: ''; 58 | border-left: 1px solid #e6e6e6; 59 | } -------------------------------------------------------------------------------- /examples/css/demo_pullRefresh_skin_multi_iscroll5.css: -------------------------------------------------------------------------------- 1 | /** 2 | * div 自定义下拉刷新 3 | */ 4 | 5 | .mui-bar~.mui-content .mui-fullscreen { 6 | top: 44px; 7 | height: auto; 8 | } 9 | 10 | 11 | /******************滑动栏目***************************/ 12 | 13 | .mui-control-content { 14 | background-color: #f5f5f5; 15 | } 16 | 17 | .mui-control-content .mui-loading { 18 | margin-top: 50px; 19 | } 20 | 21 | 22 | /*重写一个比较短的滑动条*/ 23 | 24 | .mui-segmented-control.mui-segmented-control-inverted~.mui-slider-progress-bar { 25 | background-color: transparent; 26 | } 27 | 28 | .mui-segmented-control.mui-segmented-control-inverted~.mui-slider-progress-bar .custom-progressbar { 29 | height: 2px; 30 | width: 60%; 31 | margin: auto; 32 | background-color: #558fff; 33 | } 34 | 35 | .mui-segmented-control .mui-active { 36 | color: #558fff; 37 | } 38 | 39 | 40 | /*栏目标题*/ 41 | 42 | .mui-control-item { 43 | position: relative; 44 | font-size: 15px; 45 | color: #666666; 46 | } 47 | 48 | 49 | /*绘制标题右侧的线*/ 50 | 51 | .control-line-item:after { 52 | position: absolute; 53 | top: 25%; 54 | left: 0; 55 | width: 1px; 56 | height: 50%; 57 | content: ''; 58 | border-left: 1px solid #e6e6e6; 59 | } 60 | 61 | .mui-fullscreen .mui-segmented-control~.mui-slider-group { 62 | position: absolute; 63 | top: 40px; 64 | bottom: 0; 65 | width: 100%; 66 | height: 600px; 67 | } 68 | .mui-slider-scroll { 69 | position: absolute; 70 | z-index: 1; 71 | width: 100%; 72 | height: 100%; 73 | } 74 | -------------------------------------------------------------------------------- /examples/fonts/mui.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dailc/pulltorefresh-h5-iscroll/f221a5e60e6f6c1c955e99f3ea59c1f373ff08ae/examples/fonts/mui.ttf -------------------------------------------------------------------------------- /examples/html/bizlogic-impl/demo_pullRefresh_skin_bizlogic_complex.html: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 下拉刷新皮肤 bizlogic-complex 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 25 |
26 | 27 |
28 |
29 |
30 |
31 |
32 | 33 | 34 | 35 |
36 |
37 | 38 | 39 | 40 |
41 |
42 | 43 | 44 | 45 |
46 |
47 | 48 | 49 | 50 |
51 |
52 | 53 | 54 | 55 |
56 |
57 | 58 | 59 | 60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 | 75 |
76 |
77 | 78 | 79 |
80 |
81 |
    82 | 83 |
84 |
85 |
86 | 87 | 97 | 98 | 99 | 100 | 101 | 102 | 134 | 135 | -------------------------------------------------------------------------------- /examples/html/bizlogic-impl/demo_pullRefresh_skin_bizlogic_default.html: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 下拉刷新皮肤 bizlogic-impl 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 |
25 |
    26 | 27 |
28 |
29 |
30 | 31 | 41 | 42 | 43 | 44 | 45 | 46 | 59 | 60 | -------------------------------------------------------------------------------- /examples/html/bizlogic-impl/demo_pullRefresh_skin_bizlogic_default_Interface.html: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 下拉刷新皮肤 bizlogic-impl2 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 |
25 |
    26 | 27 |
28 |
29 |
30 | 31 | 41 | 42 | 43 | 44 | 45 | 46 | 68 | 69 | -------------------------------------------------------------------------------- /examples/html/bizlogic-impl/demo_pullRefresh_skin_bizlogic_default_Interface_custom.html: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 下拉刷新皮肤 bizlogic-impl2 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 |
24 |
    25 | 26 |
27 |
28 |
29 | 30 | 40 | 41 | 42 | 43 | 44 | 45 | 77 | 78 | -------------------------------------------------------------------------------- /examples/html/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | H5下拉刷新皮肤系列 8 | 9 | 10 | 11 | 12 |
13 |

H5下拉刷新皮肤系列

14 |
15 |
16 |

skin

17 | 37 |

multi-scroller

38 | 46 |

other

47 | 61 |

bizlogic

62 |
    63 |
  • 64 | impl 65 |
  • 66 |
  • 67 | impl2 68 |
  • 69 |
70 |
71 | 72 | 73 | 74 | -------------------------------------------------------------------------------- /examples/html/multi-scroller/demo_pullRefresh_skin_multi_Iscroll5.html: -------------------------------------------------------------------------------- 1 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 下拉刷新皮肤 multi Iscroll5 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 |
28 | 37 | 38 |
39 |
40 |
41 |
42 |
43 |
    44 | 45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
    53 | 54 |
55 | 56 |
57 |
58 |
59 |
60 | 61 |
62 |
63 | 64 | 65 | 66 | 67 | 108 | 109 | -------------------------------------------------------------------------------- /examples/html/multi-scroller/demo_pullRefresh_skin_multi_bizlogic_default.html: -------------------------------------------------------------------------------- 1 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 下拉刷新皮肤 multi bizlogic多div刷新 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 |
31 | 40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
    49 | 50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
    58 | 59 |
60 | 61 |
62 |
63 |
64 |
65 |
66 | 67 | 68 | 82 | 83 | 84 | 85 | 86 | 87 | 133 | 134 | -------------------------------------------------------------------------------- /examples/html/multi-scroller/demo_pullRefresh_skin_multi_skin_default.html: -------------------------------------------------------------------------------- 1 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 下拉刷新皮肤 multi skin多div刷新 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 |
32 | 41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
    50 | 51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
    59 | 60 |
61 | 62 |
63 |
64 |
65 |
66 |
67 | 68 | 69 | 70 | 71 | 72 | 79 | 80 | -------------------------------------------------------------------------------- /examples/html/other/demo_pullRefresh_only_pulldown.html: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 下拉刷新皮肤 only-pulldown 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 25 |
26 | 27 |
28 | 29 |
30 |
31 | 32 | 33 |
34 |
35 |
    36 | 37 |
38 |
39 |
40 | 41 | 42 | 43 | 44 | 52 | 53 | -------------------------------------------------------------------------------- /examples/html/other/demo_pullRefresh_only_pullup.html: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 下拉刷新皮肤 only-pullup 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 25 |
26 | 27 |
28 | 29 |
30 |
31 | 32 | 33 |
34 |
35 |
    36 | 37 |
38 |
39 |
40 | 41 | 42 | 43 | 44 | 52 | 53 | -------------------------------------------------------------------------------- /examples/html/skin/demo_pullRefresh_skin_default.html: -------------------------------------------------------------------------------- 1 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 下拉刷新皮肤 default 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 34 |
35 | 36 |
37 | 38 |
39 |
40 | 41 | 42 |
43 |
44 |
    45 | 46 |
47 |
48 |
49 | 50 | 51 | 52 | 53 | 59 | 60 | -------------------------------------------------------------------------------- /examples/html/skin/demo_pullRefresh_skin_native.html: -------------------------------------------------------------------------------- 1 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 下拉刷新皮肤 native 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 32 |
33 | 34 |
35 | 36 |
37 |
38 | 39 | 40 |
41 |
42 |
    43 | 44 |
45 |
46 |
47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 68 | 69 | -------------------------------------------------------------------------------- /examples/html/skin/demo_pullRefresh_skin_type1.html: -------------------------------------------------------------------------------- 1 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 下拉刷新皮肤 type1 21 | 22 | 23 | 24 | 30 | 31 | 32 | 33 | 37 |
38 | 39 |
40 | 41 |
42 |
43 | 44 | 45 |
46 |
47 |
    48 | 49 |
50 |
51 |
52 | 53 | 54 | 55 | 56 | 63 | 64 | -------------------------------------------------------------------------------- /examples/html/skin/demo_pullRefresh_skin_type2.html: -------------------------------------------------------------------------------- 1 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 下拉刷新皮肤 type2 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 31 |
32 | 33 |
34 | 35 |
36 |
37 | 38 | 39 |
40 |
41 |
    42 | 43 |
44 |
45 |
46 | 47 | 48 | 49 | 50 | 57 | 58 | -------------------------------------------------------------------------------- /examples/html/skin/demo_pullRefresh_skin_type3.html: -------------------------------------------------------------------------------- 1 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 下拉刷新皮肤 type3 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 31 |
32 | 33 |
34 | 35 |
36 |
37 | 38 | 39 |
40 |
41 |
    42 | 43 |
44 |
45 |
46 | 47 | 48 | 49 | 50 | 57 | 58 | -------------------------------------------------------------------------------- /examples/html/skin/demo_pullRefresh_skin_type4.html: -------------------------------------------------------------------------------- 1 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 下拉刷新皮肤 type4 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 31 |
32 | 33 |
34 | 35 |
36 |
37 | 38 | 39 |
40 |
41 |
    42 | 43 |
44 |
45 |
46 | 47 | 48 | 49 | 50 | 57 | 58 | -------------------------------------------------------------------------------- /examples/html/skin/demo_pullRefresh_skin_type5.html: -------------------------------------------------------------------------------- 1 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 下拉刷新皮肤 type5 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 31 |
32 | 33 |
34 | 35 |
36 |
37 | 38 | 39 |
40 |
41 |
    42 | 43 |
44 |
45 |
46 | 47 | 48 | 49 | 50 | 57 | 58 | -------------------------------------------------------------------------------- /examples/html/可以参考不基于IScroll的.js: -------------------------------------------------------------------------------- 1 | + function(t) { 2 | "use strict"; 3 | t.support = function() { 4 | var t = { 5 | touch: !!("ontouchstart" in window || window.DocumentTouch && document instanceof window.DocumentTouch) 6 | }; 7 | return t 8 | }(), t.touchEvents = { 9 | start: t.support.touch ? "touchstart" : "mousedown", 10 | move: t.support.touch ? "touchmove" : "mousemove", 11 | end: t.support.touch ? "touchend" : "mouseup" 12 | }, t.getTouchPosition = function(t) { 13 | return t = t.originalEvent || t, "touchstart" === t.type || "touchmove" === t.type || "touchend" === t.type ? { 14 | x: t.targetTouches[0].pageX, 15 | y: t.targetTouches[0].pageY 16 | } : { 17 | x: t.pageX, 18 | y: t.pageY 19 | } 20 | } 21 | }($), + function(t) { 22 | "use strict"; 23 | var s = function(s) { 24 | this.container = t(s), this.distance = 50, this.attachEvents() 25 | }; 26 | s.prototype.touchStart = function(s) { 27 | if(!this.container.hasClass("refreshing")) { 28 | var i = t.getTouchPosition(s); 29 | this.start = i, this.diffX = this.diffY = 0 30 | } 31 | }, s.prototype.touchMove = function(s) { 32 | if(!this.container.hasClass("refreshing")) { 33 | if(!this.start) return !1; 34 | if(!(this.container.scrollTop() > 0)) { 35 | var i = t.getTouchPosition(s); 36 | if(this.diffX = i.x - this.start.x, this.diffY = i.y - this.start.y, !(this.diffY < 0)) return this.container.addClass("touching"), s.preventDefault(), s.stopPropagation(), this.diffY = Math.pow(this.diffY, .8), this.statusArea.css("height", this.diffY), this.diffY < this.distance ? this.container.removeClass("pull-up").addClass("pull-down") : this.container.removeClass("pull-down").addClass("pull-up"), !1 37 | } 38 | } 39 | }, s.prototype.touchEnd = function() { 40 | return this.start = !1, this.diffY <= 0 || this.container.hasClass("refreshing") ? void 0 : (this.container.removeClass("touching"), this.container.removeClass("pull-down pull-up"), Math.abs(this.diffY) <= this.distance ? this.statusArea.css("height", 0) : (this.statusArea.css("height", 50), this.container.addClass("refreshing"), this.container.trigger("pull-to-refresh")), !1) 41 | }, s.prototype.attachEvents = function() { 42 | var s = this.container; 43 | s.addClass("dropload"); 44 | var i = ['
', '
', '
', '
', '
下拉刷新
', '
释放刷新
', '
正在刷新
']; 45 | this.statusArea = t(i.join("")).prependTo(s), s.on(t.touchEvents.start, t.proxy(this.touchStart, this)), s.on(t.touchEvents.move, t.proxy(this.touchMove, this)), s.on(t.touchEvents.end, t.proxy(this.touchEnd, this)) 46 | }; 47 | var i = function(t) { 48 | new s(t) 49 | }, 50 | o = function(s) { 51 | t(s).removeClass("refreshing"), t(s).find(".dropload-layer").css("height", 0) 52 | }; 53 | t.fn.pullToRefresh = function() { 54 | return this.each(function() { 55 | i(this) 56 | }) 57 | }, t.fn.pullToRefreshDone = function() { 58 | return this.each(function() { 59 | o(this) 60 | }) 61 | } 62 | }($); -------------------------------------------------------------------------------- /examples/img/gallery/img_testgallery1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dailc/pulltorefresh-h5-iscroll/f221a5e60e6f6c1c955e99f3ea59c1f373ff08ae/examples/img/gallery/img_testgallery1.jpg -------------------------------------------------------------------------------- /examples/img/gallery/img_testgallery2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dailc/pulltorefresh-h5-iscroll/f221a5e60e6f6c1c955e99f3ea59c1f373ff08ae/examples/img/gallery/img_testgallery2.jpg -------------------------------------------------------------------------------- /examples/img/gallery/img_testgallery3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dailc/pulltorefresh-h5-iscroll/f221a5e60e6f6c1c955e99f3ea59c1f373ff08ae/examples/img/gallery/img_testgallery3.jpg -------------------------------------------------------------------------------- /examples/img/gallery/img_testgallery4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dailc/pulltorefresh-h5-iscroll/f221a5e60e6f6c1c955e99f3ea59c1f373ff08ae/examples/img/gallery/img_testgallery4.jpg -------------------------------------------------------------------------------- /examples/js/demo_pullRefresh_multi_skin.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 作者: dailc 3 | * 时间: 2017-04-05 4 | * 描述: 这里是同时生成多个下拉刷新 5 | */ 6 | (function(exports) { 7 | /** 8 | * @description 初始化下拉刷新 9 | */ 10 | function initPullRefreshList(pullToRefreshBase, options) { 11 | options = options || {}; 12 | isAuto = options.isAuto||false; 13 | var container = options.container; 14 | var listContainer = options.listContainer; 15 | // 以下几个是测试加载更多,没有更多数据功能的 16 | // 当前页 17 | var currpage = 0; 18 | // 每页大小 19 | var pageSize = 10; 20 | // 总共大小,这里用来判断是否可以上拉加载 21 | // 实际业务中,可以不基于totalcount判断的,直接根据接口返回的数据进行判断 22 | var totalCount = 21; 23 | var pullToRefreshObj = new pullToRefreshBase({ 24 | // 这里用默认设置 25 | container: container, 26 | down: { 27 | callback: pullDownRefreshCallback, 28 | // 是否显示成功动画 29 | isSuccessTips: true, 30 | }, 31 | // down为null表示不要下拉刷新 32 | // down: null, 33 | // 上拉有关 34 | up: { 35 | // 是否自动上拉加载-初始化是是否自动 36 | auto: isAuto || false, 37 | 38 | callback: pullUpRefreshCallback 39 | }, 40 | scroll: { 41 | bounceTime: 500, // 回弹动画时间 42 | // 下拉刷新和上拉加载成功动画的时间 43 | successAnimationTime: 500, 44 | // 是否嵌套,嵌套的话就不会preventDefault了 45 | eventPassthrough: 'horizontal' 46 | }, 47 | }); 48 | 49 | function pullDownRefreshCallback() { 50 | var self = this; 51 | // console.log("下拉刷新"); 52 | setTimeout(function() { 53 | // 下拉刷新当前页变为0 54 | currpage = 0; 55 | // 测试每次添加10条 56 | testAppendData(pageSize, true); 57 | resetState(true); 58 | }, 1000); 59 | } 60 | 61 | function pullUpRefreshCallback() { 62 | var self = this; 63 | // console.log("上拉加载"); 64 | setTimeout(function() { 65 | //请求数据 66 | //当前页++ 67 | currpage++; 68 | //测试每次添加10条 69 | testAppendData(pageSize, false); 70 | resetState(false); 71 | }, 500); 72 | 73 | } 74 | /** 75 | * @description 测试添加数据 76 | * 真实情况添加的数据要根据接口返回数据映射 77 | * @param {Number} count 数量 78 | * @param {Boolean} isPullDown 是否是下拉刷新 79 | */ 80 | function testAppendData(count, isPullDown) { 81 | isPullDown = isPullDown || false; 82 | var fragment = document.createDocumentFragment(); 83 | 84 | var li; 85 | for(var i = 0; i < count; i++) { 86 | li = document.createElement('li'); 87 | li.className = 'mui-table-view-cell'; 88 | li.id = 'id_' + i; 89 | li.innerHTML = '测试item' + currpage + '-' + (i + 1); 90 | fragment.appendChild(li); 91 | } 92 | 93 | var dataContainer = document.querySelector(listContainer); 94 | // 添加-下拉刷新时先清除数据 95 | if(isPullDown) { 96 | dataContainer.innerHTML = ''; 97 | } 98 | dataContainer.appendChild(fragment); 99 | } 100 | /** 101 | * @description 重置状态 102 | * @param {Boolean} isPullDown 是否是上拉加载 103 | */ 104 | function resetState(isPullDown) { 105 | if(isPullDown) { 106 | pullToRefreshObj.endPullDownToRefresh(); 107 | if(pullToRefreshObj.finished) { 108 | pullToRefreshObj.refresh(true); 109 | } 110 | } 111 | // 判断当前页的数据是否已经大于totalCount 112 | var itemLength = document.querySelector(listContainer).children.length; 113 | if(itemLength >= totalCount) { 114 | pullToRefreshObj.endPullUpToRefresh(true); 115 | } else { 116 | pullToRefreshObj.endPullUpToRefresh(false); 117 | } 118 | } 119 | 120 | function refresh() { 121 | // 清空dom 122 | document.querySelector(listContainer).innerHTML = ''; 123 | currpage = -1; //这个必须要变 124 | // 手动将状态设为可以加载更多 125 | if(pullToRefreshObj.finished) { 126 | pullToRefreshObj.refresh(true); 127 | } 128 | // 当然也可以换为其它的刷新方法 129 | pullToRefreshObj.pullupLoading(); 130 | } 131 | return { 132 | refresh: refresh 133 | }; 134 | } 135 | 136 | 137 | 138 | exports.init = function(pullToRefreshObj) { 139 | initPullRefreshList(pullToRefreshObj, { 140 | isAuto:true, 141 | container:'#pullrefresh1', 142 | listContainer:'#listdata1' 143 | }); 144 | initPullRefreshList(pullToRefreshObj, { 145 | isAuto:false, 146 | container:'#pullrefresh2', 147 | listContainer:'#listdata2' 148 | }); 149 | }; 150 | 151 | window.demoPullToRefresh = exports; 152 | })({}); -------------------------------------------------------------------------------- /examples/js/demo_pullRefresh_skin.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 作者: dailc 3 | * 时间: 2017-04-05 4 | * 描述: 这里将下拉刷新skin里的通用代码抽取出来了 5 | */ 6 | (function(exports) { 7 | var pullToRefreshBiz, 8 | pullToRefreshBase; 9 | /** 10 | * @description 初始化下拉刷新 11 | */ 12 | function initPullRefreshList(isAuto,disablePullDown,disablePullUp) { 13 | //以下几个是测试加载更多,没有更多数据功能的 14 | //当前页 15 | var currpage = 0; 16 | //每页大小 17 | var pageSize = 10; 18 | //总共大小,这里用来判断是否可以上拉加载 19 | //实际业务中,可以不基于totalcount判断的,直接根据接口返回的数据进行判断 20 | var totalCount = 21; 21 | var pullToRefreshObj = new pullToRefreshBase({ 22 | //这里用默认设置 23 | container: '#pullrefresh', 24 | down: disablePullDown?null:{ 25 | callback: pullDownRefreshCallback, 26 | //是否显示成功动画 27 | isSuccessTips: true, 28 | }, 29 | //down为null表示不要下拉刷新 30 | //down: null, 31 | //上拉有关 32 | up: disablePullUp?null:{ 33 | //是否自动上拉加载-初始化是是否自动 34 | auto: isAuto || false, 35 | isFastLoading: true, 36 | callback: pullUpRefreshCallback 37 | }, 38 | scroll: { 39 | bounceTime: 500, //回弹动画时间 40 | //下拉刷新和上拉加载成功动画的时间 41 | successAnimationTime: 500 42 | }, 43 | }); 44 | 45 | function pullDownRefreshCallback() { 46 | var self = this; 47 | console.log("下拉刷新"); 48 | setTimeout(function() { 49 | //下拉刷新当前页变为0 50 | currpage = 0; 51 | //测试每次添加10条 52 | testAppendData(pageSize, true); 53 | resetState(true); 54 | }, 1000); 55 | } 56 | 57 | function pullUpRefreshCallback() { 58 | var self = this; 59 | console.log("上拉加载"); 60 | setTimeout(function() { 61 | //请求数据 62 | //当前页++ 63 | currpage++; 64 | //测试每次添加10条 65 | testAppendData(pageSize, false); 66 | resetState(false); 67 | }, 500); 68 | 69 | } 70 | /** 71 | * @description 测试添加数据 72 | * 真实情况添加的数据要根据接口返回数据映射 73 | * @param {Number} count 数量 74 | * @param {Boolean} isPullDown 是否是下拉刷新 75 | */ 76 | function testAppendData(count, isPullDown) { 77 | isPullDown = isPullDown || false; 78 | var fragment = document.createDocumentFragment(); 79 | 80 | var li; 81 | for(var i = 0; i < count; i++) { 82 | li = document.createElement('li'); 83 | li.className = 'mui-table-view-cell'; 84 | li.id = 'id_' + i; 85 | li.innerHTML = '测试item' + currpage + '-' + (i + 1); 86 | fragment.appendChild(li); 87 | } 88 | 89 | var dataContainer = document.getElementById('listdata'); 90 | //添加-下拉刷新时先清除数据 91 | if(isPullDown) { 92 | dataContainer.innerHTML = ''; 93 | } 94 | dataContainer.appendChild(fragment); 95 | } 96 | /** 97 | * @description 重置状态 98 | * @param {Boolean} isPullDown 是否是上拉加载 99 | */ 100 | function resetState(isPullDown) { 101 | if(isPullDown) { 102 | pullToRefreshObj.endPullDownToRefresh(); 103 | if(pullToRefreshObj.finished) { 104 | pullToRefreshObj.refresh(true); 105 | } 106 | } 107 | //判断当前页的数据是否已经大于totalCount 108 | var itemLength = document.getElementById('listdata').children.length; 109 | if(itemLength >= totalCount) { 110 | pullToRefreshObj.endPullUpToRefresh(true); 111 | } else { 112 | pullToRefreshObj.endPullUpToRefresh(false); 113 | } 114 | } 115 | 116 | function refresh() { 117 | //清空dom 118 | document.getElementById('listdata').innerHTML = ''; 119 | currpage = -1; //这个必须要变 120 | //手动将状态设为可以加载更多 121 | if(pullToRefreshObj.finished) { 122 | pullToRefreshObj.refresh(true); 123 | } 124 | //当然也可以换为其它的刷新方法 125 | pullToRefreshObj.pullupLoading(); 126 | } 127 | return { 128 | refresh: refresh 129 | }; 130 | 131 | } 132 | 133 | /** 134 | * @description 初始化 135 | */ 136 | function initSearch() { 137 | document.querySelector('#input-searchName').addEventListener('change', function() { 138 | var searchValue = document.getElementById('input-searchName').value; 139 | // 刷新这个业务下拉刷新 140 | pullToRefreshBiz.refresh(); 141 | console.log("搜索:" + searchValue); 142 | }); 143 | 144 | } 145 | 146 | exports.init = function(pullToRefreshObj,disablePullDown,disablePullUp) { 147 | pullToRefreshBase = pullToRefreshObj; 148 | initSearch(); 149 | pullToRefreshBiz = initPullRefreshList(true,disablePullDown,disablePullUp); 150 | }; 151 | 152 | window.demoPullToRefresh = exports; 153 | })({}); -------------------------------------------------------------------------------- /examples/js/mustache.min.js: -------------------------------------------------------------------------------- 1 | (function defineMustache(global, factory) { 2 | if (typeof exports === "object" && exports && typeof exports.nodeName !== "string") { 3 | factory(exports) 4 | } else if (typeof define === "function" && define.amd) { 5 | define(["exports"], factory) 6 | } else { 7 | global.Mustache = {}; 8 | factory(Mustache) 9 | } 10 | })(this, function mustacheFactory(mustache) { 11 | var objectToString = Object.prototype.toString; 12 | var isArray = Array.isArray || function isArrayPolyfill(object) { 13 | return objectToString.call(object) === "[object Array]" 14 | }; 15 | 16 | function isFunction(object) { 17 | return typeof object === "function" 18 | } 19 | 20 | function typeStr(obj) { 21 | return isArray(obj) ? "array" : typeof obj 22 | } 23 | 24 | function escapeRegExp(string) { 25 | return string.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, "\\$&") 26 | } 27 | 28 | function hasProperty(obj, propName) { 29 | return obj != null && typeof obj === "object" && propName in obj 30 | } 31 | var regExpTest = RegExp.prototype.test; 32 | 33 | function testRegExp(re, string) { 34 | return regExpTest.call(re, string) 35 | } 36 | var nonSpaceRe = /\S/; 37 | 38 | function isWhitespace(string) { 39 | return !testRegExp(nonSpaceRe, string) 40 | } 41 | var entityMap = { 42 | "&": "&", 43 | "<": "<", 44 | ">": ">", 45 | '"': """, 46 | "'": "'", 47 | "/": "/" 48 | }; 49 | 50 | function escapeHtml(string) { 51 | return String(string).replace(/[&<>"'\/]/g, function fromEntityMap(s) { 52 | return entityMap[s] 53 | }) 54 | } 55 | var whiteRe = /\s*/; 56 | var spaceRe = /\s+/; 57 | var equalsRe = /\s*=/; 58 | var curlyRe = /\s*\}/; 59 | var tagRe = /#|\^|\/|>|\{|&|=|!/; 60 | 61 | function parseTemplate(template, tags) { 62 | if (!template) return []; 63 | var sections = []; 64 | var tokens = []; 65 | var spaces = []; 66 | var hasTag = false; 67 | var nonSpace = false; 68 | 69 | function stripSpace() { 70 | if (hasTag && !nonSpace) { 71 | while (spaces.length) delete tokens[spaces.pop()] 72 | } else { 73 | spaces = [] 74 | } 75 | hasTag = false; 76 | nonSpace = false 77 | } 78 | var openingTagRe, closingTagRe, closingCurlyRe; 79 | 80 | function compileTags(tagsToCompile) { 81 | if (typeof tagsToCompile === "string") tagsToCompile = tagsToCompile.split(spaceRe, 2); 82 | if (!isArray(tagsToCompile) || tagsToCompile.length !== 2) throw new Error("Invalid tags: " + tagsToCompile); 83 | openingTagRe = new RegExp(escapeRegExp(tagsToCompile[0]) + "\\s*"); 84 | closingTagRe = new RegExp("\\s*" + escapeRegExp(tagsToCompile[1])); 85 | closingCurlyRe = new RegExp("\\s*" + escapeRegExp("}" + tagsToCompile[1])) 86 | } 87 | compileTags(tags || mustache.tags); 88 | var scanner = new Scanner(template); 89 | var start, type, value, chr, token, openSection; 90 | while (!scanner.eos()) { 91 | start = scanner.pos; 92 | value = scanner.scanUntil(openingTagRe); 93 | if (value) { 94 | for (var i = 0, valueLength = value.length; i < valueLength; ++i) { 95 | chr = value.charAt(i); 96 | if (isWhitespace(chr)) { 97 | spaces.push(tokens.length) 98 | } else { 99 | nonSpace = true 100 | } 101 | tokens.push(["text", chr, start, start + 1]); 102 | start += 1; 103 | if (chr === "\n") stripSpace() 104 | } 105 | } 106 | if (!scanner.scan(openingTagRe)) break; 107 | hasTag = true; 108 | type = scanner.scan(tagRe) || "name"; 109 | scanner.scan(whiteRe); 110 | if (type === "=") { 111 | value = scanner.scanUntil(equalsRe); 112 | scanner.scan(equalsRe); 113 | scanner.scanUntil(closingTagRe) 114 | } else if (type === "{") { 115 | value = scanner.scanUntil(closingCurlyRe); 116 | scanner.scan(curlyRe); 117 | scanner.scanUntil(closingTagRe); 118 | type = "&" 119 | } else { 120 | value = scanner.scanUntil(closingTagRe) 121 | } if (!scanner.scan(closingTagRe)) throw new Error("Unclosed tag at " + scanner.pos); 122 | token = [type, value, start, scanner.pos]; 123 | tokens.push(token); 124 | if (type === "#" || type === "^") { 125 | sections.push(token) 126 | } else if (type === "/") { 127 | openSection = sections.pop(); 128 | if (!openSection) throw new Error('Unopened section "' + value + '" at ' + start); 129 | if (openSection[1] !== value) throw new Error('Unclosed section "' + openSection[1] + '" at ' + start) 130 | } else if (type === "name" || type === "{" || type === "&") { 131 | nonSpace = true 132 | } else if (type === "=") { 133 | compileTags(value) 134 | } 135 | } 136 | openSection = sections.pop(); 137 | if (openSection) throw new Error('Unclosed section "' + openSection[1] + '" at ' + scanner.pos); 138 | return nestTokens(squashTokens(tokens)) 139 | } 140 | 141 | function squashTokens(tokens) { 142 | var squashedTokens = []; 143 | var token, lastToken; 144 | for (var i = 0, numTokens = tokens.length; i < numTokens; ++i) { 145 | token = tokens[i]; 146 | if (token) { 147 | if (token[0] === "text" && lastToken && lastToken[0] === "text") { 148 | lastToken[1] += token[1]; 149 | lastToken[3] = token[3] 150 | } else { 151 | squashedTokens.push(token); 152 | lastToken = token 153 | } 154 | } 155 | } 156 | return squashedTokens 157 | } 158 | 159 | function nestTokens(tokens) { 160 | var nestedTokens = []; 161 | var collector = nestedTokens; 162 | var sections = []; 163 | var token, section; 164 | for (var i = 0, numTokens = tokens.length; i < numTokens; ++i) { 165 | token = tokens[i]; 166 | switch (token[0]) { 167 | case "#": 168 | case "^": 169 | collector.push(token); 170 | sections.push(token); 171 | collector = token[4] = []; 172 | break; 173 | case "/": 174 | section = sections.pop(); 175 | section[5] = token[2]; 176 | collector = sections.length > 0 ? sections[sections.length - 1][4] : nestedTokens; 177 | break; 178 | default: 179 | collector.push(token) 180 | } 181 | } 182 | return nestedTokens 183 | } 184 | 185 | function Scanner(string) { 186 | this.string = string; 187 | this.tail = string; 188 | this.pos = 0 189 | } 190 | Scanner.prototype.eos = function eos() { 191 | return this.tail === "" 192 | }; 193 | Scanner.prototype.scan = function scan(re) { 194 | var match = this.tail.match(re); 195 | if (!match || match.index !== 0) return ""; 196 | var string = match[0]; 197 | this.tail = this.tail.substring(string.length); 198 | this.pos += string.length; 199 | return string 200 | }; 201 | Scanner.prototype.scanUntil = function scanUntil(re) { 202 | var index = this.tail.search(re), 203 | match; 204 | switch (index) { 205 | case -1: 206 | match = this.tail; 207 | this.tail = ""; 208 | break; 209 | case 0: 210 | match = ""; 211 | break; 212 | default: 213 | match = this.tail.substring(0, index); 214 | this.tail = this.tail.substring(index) 215 | } 216 | this.pos += match.length; 217 | return match 218 | }; 219 | 220 | function Context(view, parentContext) { 221 | this.view = view; 222 | this.cache = { 223 | ".": this.view 224 | }; 225 | this.parent = parentContext 226 | } 227 | Context.prototype.push = function push(view) { 228 | return new Context(view, this) 229 | }; 230 | Context.prototype.lookup = function lookup(name) { 231 | var cache = this.cache; 232 | var value; 233 | if (cache.hasOwnProperty(name)) { 234 | value = cache[name] 235 | } else { 236 | var context = this, 237 | names, index, lookupHit = false; 238 | while (context) { 239 | if (name.indexOf(".") > 0) { 240 | value = context.view; 241 | names = name.split("."); 242 | index = 0; 243 | while (value != null && index < names.length) { 244 | if (index === names.length - 1) lookupHit = hasProperty(value, names[index]); 245 | value = value[names[index++]] 246 | } 247 | } else { 248 | value = context.view[name]; 249 | lookupHit = hasProperty(context.view, name) 250 | } if (lookupHit) break; 251 | context = context.parent 252 | } 253 | cache[name] = value 254 | } if (isFunction(value)) value = value.call(this.view); 255 | return value 256 | }; 257 | 258 | function Writer() { 259 | this.cache = {} 260 | } 261 | Writer.prototype.clearCache = function clearCache() { 262 | this.cache = {} 263 | }; 264 | Writer.prototype.parse = function parse(template, tags) { 265 | var cache = this.cache; 266 | var tokens = cache[template]; 267 | if (tokens == null) tokens = cache[template] = parseTemplate(template, tags); 268 | return tokens 269 | }; 270 | Writer.prototype.render = function render(template, view, partials) { 271 | var tokens = this.parse(template); 272 | var context = view instanceof Context ? view : new Context(view); 273 | return this.renderTokens(tokens, context, partials, template) 274 | }; 275 | Writer.prototype.renderTokens = function renderTokens(tokens, context, partials, originalTemplate) { 276 | var buffer = ""; 277 | var token, symbol, value; 278 | for (var i = 0, numTokens = tokens.length; i < numTokens; ++i) { 279 | value = undefined; 280 | token = tokens[i]; 281 | symbol = token[0]; 282 | if (symbol === "#") value = this.renderSection(token, context, partials, originalTemplate); 283 | else if (symbol === "^") value = this.renderInverted(token, context, partials, originalTemplate); 284 | else if (symbol === ">") value = this.renderPartial(token, context, partials, originalTemplate); 285 | else if (symbol === "&") value = this.unescapedValue(token, context); 286 | else if (symbol === "name") value = this.escapedValue(token, context); 287 | else if (symbol === "text") value = this.rawValue(token); 288 | if (value !== undefined) buffer += value 289 | } 290 | return buffer 291 | }; 292 | Writer.prototype.renderSection = function renderSection(token, context, partials, originalTemplate) { 293 | var self = this; 294 | var buffer = ""; 295 | var value = context.lookup(token[1]); 296 | 297 | function subRender(template) { 298 | return self.render(template, context, partials) 299 | } 300 | if (!value) return; 301 | if (isArray(value)) { 302 | for (var j = 0, valueLength = value.length; j < valueLength; ++j) { 303 | buffer += this.renderTokens(token[4], context.push(value[j]), partials, originalTemplate) 304 | } 305 | } else if (typeof value === "object" || typeof value === "string" || typeof value === "number") { 306 | buffer += this.renderTokens(token[4], context.push(value), partials, originalTemplate) 307 | } else if (isFunction(value)) { 308 | if (typeof originalTemplate !== "string") throw new Error("Cannot use higher-order sections without the original template"); 309 | value = value.call(context.view, originalTemplate.slice(token[3], token[5]), subRender); 310 | if (value != null) buffer += value 311 | } else { 312 | buffer += this.renderTokens(token[4], context, partials, originalTemplate) 313 | } 314 | return buffer 315 | }; 316 | Writer.prototype.renderInverted = function renderInverted(token, context, partials, originalTemplate) { 317 | var value = context.lookup(token[1]); 318 | if (!value || isArray(value) && value.length === 0) return this.renderTokens(token[4], context, partials, originalTemplate) 319 | }; 320 | Writer.prototype.renderPartial = function renderPartial(token, context, partials) { 321 | if (!partials) return; 322 | var value = isFunction(partials) ? partials(token[1]) : partials[token[1]]; 323 | if (value != null) return this.renderTokens(this.parse(value), context, partials, value) 324 | }; 325 | Writer.prototype.unescapedValue = function unescapedValue(token, context) { 326 | var value = context.lookup(token[1]); 327 | if (value != null) return value 328 | }; 329 | Writer.prototype.escapedValue = function escapedValue(token, context) { 330 | var value = context.lookup(token[1]); 331 | if (value != null) return mustache.escape(value) 332 | }; 333 | Writer.prototype.rawValue = function rawValue(token) { 334 | return token[1] 335 | }; 336 | mustache.name = "mustache.js"; 337 | mustache.version = "2.1.3"; 338 | mustache.tags = ["{{", "}}"]; 339 | var defaultWriter = new Writer; 340 | mustache.clearCache = function clearCache() { 341 | return defaultWriter.clearCache() 342 | }; 343 | mustache.parse = function parse(template, tags) { 344 | return defaultWriter.parse(template, tags) 345 | }; 346 | mustache.render = function render(template, view, partials) { 347 | if (typeof template !== "string") { 348 | throw new TypeError('Invalid template! Template should be a "string" ' + 'but "' + typeStr(template) + '" was given as the first ' + "argument for mustache#render(template, view, partials)") 349 | } 350 | return defaultWriter.render(template, view, partials) 351 | }; 352 | mustache.to_html = function to_html(template, view, partials, send) { 353 | var result = mustache.render(template, view, partials); 354 | if (isFunction(send)) { 355 | send(result) 356 | } else { 357 | return result 358 | } 359 | }; 360 | mustache.escape = escapeHtml; 361 | mustache.Scanner = Scanner; 362 | mustache.Context = Context; 363 | mustache.Writer = Writer 364 | }); -------------------------------------------------------------------------------- /examples/json/testList.json: -------------------------------------------------------------------------------- 1 | { 2 | "custom": { 3 | "infoList": [ 4 | { 5 | "guid": "id0", 6 | "title": "tab1标题0", 7 | "date": "17-04-06 03:20:44", 8 | "author": "type1" 9 | }, { 10 | "guid": "id1", 11 | "title": "tab1标题1", 12 | "date": "17-04-06 03:20:44", 13 | "author": "type1" 14 | }, { 15 | "guid": "id2", 16 | "title": "tab1标题2", 17 | "date": "17-04-06 03:20:44", 18 | "author": "type1" 19 | }, { 20 | "guid": "id3", 21 | "title": "tab1标题3", 22 | "date": "17-04-06 03:20:44", 23 | "author": "type1" 24 | }, { 25 | "guid": "id4", 26 | "title": "tab1标题4", 27 | "date": "17-04-06 03:20:44", 28 | "author": "type4" 29 | }, { 30 | "guid": "id5", 31 | "title": "tab1标题5", 32 | "date": "17-04-06 03:20:44", 33 | "author": "type5" 34 | } 35 | ] 36 | }, 37 | "status": { 38 | "code": 200, 39 | "text": "成功", 40 | "url": "" 41 | } 42 | } -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | var concat = require('gulp-concat'); 3 | var uglify = require('gulp-uglify'); 4 | var rename = require('gulp-rename'); 5 | var cleanCSS = require('gulp-clean-css'); 6 | // 串行执行任务 7 | var gulpSequence = require('gulp-sequence'); 8 | // 头部注释 9 | var header = require('gulp-header'); 10 | var pkg = require('./package.json'); 11 | var banner = ['/**', 12 | ' * <%= pkg.name %> - <%= pkg.description %>', 13 | ' * @version v<%= pkg.version %>', 14 | ' * @author <%= pkg.homepage %>', 15 | ' */', 16 | ''].join('\n'); 17 | 18 | 19 | var debugPath = './dist/_debug/'; 20 | var releasePath = './dist/'; 21 | 22 | 23 | // 下拉刷新核心文件合并 24 | gulp.task('core_concat', function() { 25 | return gulp.src(['./src/core/pulltorefresh.iscroll.probe.js', './src/core/pulltorefresh.js', './src/core/pulltorefresh.core.js']) 26 | .pipe(concat('pulltorefresh.core.js')) 27 | .pipe(gulp.dest(debugPath)); 28 | }); 29 | 30 | 31 | // 打包skin-defult 32 | gulp.task('pack_skin_default', ['core_concat'], function() { 33 | return gulp.src([debugPath + 'pulltorefresh.core.js', './src/pulltorefresh.skin.default.js']) 34 | .pipe(concat('pulltorefresh.skin.default.js')) 35 | .pipe(gulp.dest(debugPath)); 36 | }); 37 | 38 | // 打包skin-type1 39 | gulp.task('pack_skin_type1', ['core_concat'], function() { 40 | return gulp.src([debugPath + 'pulltorefresh.core.js', './src/pulltorefresh.skin.type1.js']) 41 | .pipe(concat('pulltorefresh.skin.type1.js')) 42 | .pipe(gulp.dest(debugPath)); 43 | }); 44 | 45 | // 打包skin-type2 46 | gulp.task('pack_skin_type2', ['core_concat'], function() { 47 | return gulp.src([debugPath + 'pulltorefresh.core.js', './src/pulltorefresh.skin.type2.js']) 48 | .pipe(concat('pulltorefresh.skin.type2.js')) 49 | .pipe(gulp.dest(debugPath)); 50 | }); 51 | 52 | // 打包skin-type3 53 | gulp.task('pack_skin_type3', ['core_concat'], function() { 54 | return gulp.src([debugPath + 'pulltorefresh.core.js', './src/pulltorefresh.skin.type3.js']) 55 | .pipe(concat('pulltorefresh.skin.type3.js')) 56 | .pipe(gulp.dest(debugPath)); 57 | }); 58 | 59 | // 打包skin-type4 60 | gulp.task('pack_skin_type4', ['core_concat'], function() { 61 | return gulp.src([debugPath + 'pulltorefresh.core.js', './src/pulltorefresh.skin.type4.js']) 62 | .pipe(concat('pulltorefresh.skin.type4.js')) 63 | .pipe(gulp.dest(debugPath)); 64 | }); 65 | 66 | // 打包skin-type5 67 | gulp.task('pack_skin_type5', ['core_concat'], function() { 68 | return gulp.src([debugPath + 'pulltorefresh.core.js', './src/pulltorefresh.skin.type5.js']) 69 | .pipe(concat('pulltorefresh.skin.type5.js')) 70 | .pipe(gulp.dest(debugPath)); 71 | }); 72 | 73 | // 打包skin-native 74 | gulp.task('pack_skin_native', function() { 75 | return gulp.src(['./src/pulltorefresh.skin.native.js']) 76 | .pipe(concat('pulltorefresh.skin.native.js')) 77 | .pipe(gulp.dest(debugPath)); 78 | }); 79 | 80 | // 打包bizlogic-impl 81 | gulp.task('pack_bizlogic_impl', function() { 82 | return gulp.src(['./src/core/handedata.js', './src/pulltorefresh.bizlogic.impl.js']) 83 | .pipe(concat('pulltorefresh.bizlogic.impl.js')) 84 | .pipe(gulp.dest(debugPath)); 85 | }); 86 | 87 | // 打包bizlogic-impl 88 | gulp.task('pack_bizlogic_impl2', function() { 89 | return gulp.src(['./src/core/handedata.js', './src/pulltorefresh.bizlogic.impl2.js']) 90 | .pipe(concat('pulltorefresh.bizlogic.impl2.js')) 91 | .pipe(gulp.dest(debugPath)); 92 | }); 93 | 94 | 95 | // 打包 css以及静态资源 96 | gulp.task('pack_resources', function() { 97 | return gulp.src(['./src/css/**/*']) 98 | .pipe(gulp.dest(debugPath + 'css/')); 99 | }); 100 | 101 | 102 | // 压缩发布的源文件 103 | gulp.task('js_uglify', function() { 104 | return gulp.src([debugPath + '**/*.js', '!'+ debugPath + '**/*.min.js']) 105 | .pipe(uglify()) 106 | // 压缩时进行异常捕获 107 | .on('error', function(err) { 108 | console.log('line number: %d, message: %s', err.lineNumber, err.message); 109 | this.end(); 110 | }) 111 | // .pipe(rename({ 112 | // suffix: '.min' 113 | // })) 114 | .pipe(header(banner, { pkg : pkg } )) 115 | .pipe(gulp.dest(releasePath)); 116 | }); 117 | 118 | // 压缩发布 css 119 | gulp.task('clean_css', function() { 120 | return gulp.src([debugPath + '**/*.css', '!'+ debugPath + '**/*.min.css']) 121 | .pipe(cleanCSS()) 122 | // .pipe(rename({ 123 | // suffix: '.min' 124 | // })) 125 | .pipe(header(banner, { pkg : pkg } )) 126 | .pipe(gulp.dest(releasePath)); 127 | }); 128 | 129 | // 压缩发布 图片资源,暂时不压缩 130 | gulp.task('resource_uglify', function() { 131 | return gulp.src([debugPath + '**/*', '!'+ debugPath + '**/*.css', '!'+ debugPath + '**/*.js']) 132 | .pipe(gulp.dest(releasePath)); 133 | }); 134 | 135 | gulp.task('pack_debug', ['pack_skin_default', 'pack_skin_type1', 'pack_skin_type2', 'pack_skin_type3', 'pack_skin_type4', 'pack_skin_type5', 'pack_skin_native', 'pack_bizlogic_impl', 'pack_bizlogic_impl2', 'pack_resources']); 136 | 137 | 138 | gulp.task('pack_release', ['js_uglify', 'clean_css', 'resource_uglify']); 139 | 140 | 141 | gulp.task('default', gulpSequence('pack_debug', 'pack_release')); 142 | 143 | // 看守 144 | gulp.task('watch', function() { 145 | 146 | // 看守所有位在 dist/ 目录下的档案,一旦有更动,便进行重整 147 | // gulp.watch([config.src+'/gulpWatch.json']).on('change', function(file) { 148 | // console.log("改动"); 149 | // }); 150 | gulp.watch('./src/**/*', ['default']); 151 | 152 | }); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pulltorefresh-h5-iscroll", 3 | "version": "3.0.0", 4 | "description": "一款基于IScroll5的H5下拉刷新实现,包括多种皮肤的实现", 5 | "author": "dailc", 6 | "license": "MIT", 7 | "devDependencies": { 8 | "gulp": "^3.9.1", 9 | "gulp-clean-css": "^2.0.10", 10 | "gulp-concat": "^2.6.0", 11 | "gulp-rename": "^1.2.2", 12 | "gulp-uglify": "^1.5.1" 13 | }, 14 | "dependencies": { 15 | "gulp-header": "^1.8.8", 16 | "gulp-sequence": "^0.4.6" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/README.md: -------------------------------------------------------------------------------- 1 | ## 基于IScroll的下拉刷新皮肤系列 2 | **最后更新:20170609** 3 | 4 | 5 | ### API说明 6 | **所有的下拉刷新皮肤对外提供的API均保持一致** 7 | 8 | #### `pulltorefresh.skin.×××`的API 9 | 这些API事每一个下拉刷新对象都有的 10 | 11 | ``` 12 | * finished //这是一个属性,用来控制当前上拉加载是否可用 13 | * refresh() //重置状态。譬如上拉加载关闭后需要手动refresh重置finished状态 14 | * pulldownLoading() //主动触发一个下拉刷新的动画(同时会触发下拉回调) 15 | * pullupLoading() //主动触发一个上拉加载的动画(同时会触发上拉回调) 16 | * endPullDownToRefresh() //关闭下拉刷新动画,重置状态 17 | * endPullUpToRefresh(finished) //关闭上拉加载动画,重置状态,如果finished,则不允许在上拉,除非再次refresh() 18 | ``` 19 | 20 | 21 | #### `pulltorefresh.core.js`的API 22 | 这个文件的API主要是用来给具体的皮肤类进行继承与实现。 23 | 24 | ``` 25 | * _initPullToRefreshTipsHook() //在这里初始化生成下拉刷新与上拉加载的提示 26 | * _pullingHook(deltaY,thresholdHeight) //下拉过程中的钩子函数,方便实现一些渐变动画 27 | * _pulldownLoaingAnimationHook //下拉刷新的动画 28 | * _pulldownLoaingAnimationSuccessHook(done,isSuccess) //下拉刷新的成功动画-动画完毕后可能的成功提示,没有的话请直接执行done 29 | * _pulldownLoaingAnimationEndHook //下拉刷新的动画完成后的回调,可以用来重置状态 30 | * _pullupLoaingAnimationHook(isFinished) //上拉加载的动画 31 | * _pullupLoaingAnimationSuccessHook(isFinished) //上拉加载的成功动画-动画完毕后可能的成功提示,或者重置状态 32 | * _scrollEndHook //滑动完毕后的end回调(这个比较少用到) 33 | * _enablePullUpHook //允许pullup后的回调 34 | * _disablePullUpHook //禁止pullup后的回调 35 | ``` 36 | 37 | ### 源码说明 38 | 39 | 40 | #### `pulltorefresh.skin.css` 41 | 是所有自定义下拉刷新皮肤使用的css样式,这里所有的样式一起打包成了一个文件 42 | 43 | #### `pulltorefresh.core.js` 44 | 下拉刷新的核心实现,依赖于`IScroll5`,里面将下拉刷新的核心逻辑都抽取出来了,并规定了一些特定的UI实现API,方便自定义继承实现。 45 | 46 | 这样,可以方便单独去继承这个类实现各色各样不同的皮肤。 47 | 48 | #### `pulltorefresh.skin.×××.js` 49 | 对应的皮肤实现,所有皮肤实现均继承了上述的下拉刷新核心类,因此皮肤类中只关注UI层面的实现。 50 | 51 | * `default`和`type1`皮肤依赖于`mui.css` 52 | * 其它皮肤依赖于`pulltorefresh.skin.css` 53 | 54 | #### `pulltorefresh.skin.native.js` 55 | 这个是一个特殊的皮肤,它重新定义了一个简单的下拉刷新。 56 | 57 | 应用场景是:在混合开发的原生容器内部使用,例如钉钉等环境。在这种环境下,原生已经提供了下拉API了,所以这里面都是基于它的API来实现,同时增加了一个上拉加载。 58 | 59 | 注意,这种皮肤只适用于原生环境,因此在h5环境下会兼容下其它皮肤,参考示例。 60 | 61 | #### `pulltorefresh.bizlogic.impl.js` 62 | 下拉刷新业务层面的封装,上述的皮肤类实现了下拉刷新功能,但是没有对业务场景进行封装的。 63 | 64 | 因此在实际业务是,会有大量重复的代码(如相同的ajax,相同的接口数据处理等等) 65 | 66 | 因此这里有封装了一层,将通用业务写进去了,进一步减少代码。 67 | 68 | 当然了,实际情况下可以去根据不同业务修改这个源文件,重新打包。 69 | 70 | 调用示例: 71 | ``` 72 | new PullToRefreshTools.bizlogic({ 73 | skin: PullToRefreshTools.skin.defaults, 74 | url: 'http://115.29.151.25:8012/request.php', 75 | template: '#list_item', 76 | dataRequest: function(currPage, callback) { 77 | var result = { 78 | action: 'testPullrefreshListDemoV3', 79 | paras: { 80 | currentpageindex: currPage.toString(), 81 | pagesize: 10, 82 | tabType: 'tab1', 83 | // 搜索值,接口里没有实现,这里可以打印代表搜索值已经获取到 84 | searchValue: '' 85 | } 86 | }; 87 | return result; 88 | }, 89 | itemClick: function(e) { 90 | console.log("点击:" + this.id); 91 | } 92 | }); 93 | ``` 94 | 95 | 最终生成的对象开放的API: 96 | 97 | ``` 98 | * refresh() //触发一次上拉加载(一般搜索中用到,搜索完毕后会刷新页面) 99 | * pullToRefreshInstance //属性,这个是原始的下拉刷新对象引用(可以使用上述的下拉刷新api) 100 | * 至于生成业务下拉刷新时需要传入的参数,由于与业务耦合,因此不赘述,详情可以参考源码或示例 101 | ``` 102 | 103 | ### 如何自定义实现皮肤 104 | 参考源码中的skin系列,只需要继承核心类,然后实现对应的UI函数即可。 -------------------------------------------------------------------------------- /src/core/handedata.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 作者: dailc 3 | * 创建时间: 2017-03-28 4 | * 版本: [1.0, 2017/05/26 ] 5 | * 版权: dailc 6 | * 描述: 数据处理的通用方法封装 7 | */ 8 | (function(exports, CommonTools) { 9 | /** 10 | * 通用接口处理 11 | * 通过插拔式增加各种接口的支持 12 | */ 13 | (function() { 14 | // 处理数据的函数池 15 | exports.dataProcessFn = []; 16 | 17 | /** 18 | * @description 统一处理返回数据,返回数据必须符合标准才行,否则会返回错误提示 19 | * @param {JSON} response 接口返回的数据 20 | * @param {Object} options 配置信息,包括 21 | * dataPath 手动指定处理数据的路径,遇到一些其它数据格式可以手动指定 22 | * 可以传入数组,传入数组代表回一次找path,直到找到为止或者一直到最后都没找到 23 | * isDebug 是否是调试模式,调试模式会返回一个debugInfo节点包含着原数据 24 | * 其它:无法处理的数据,会返回对应错误信息 25 | * @return {JSON} 返回的数据,包括多个成功数据,错误提示等等 26 | */ 27 | exports.dataProcess = function(response, options) { 28 | options = options || {}; 29 | 30 | // 永远不要试图修改arguments,请单独备份,否则在严格模式和非严格模式下容易出现错误 31 | var args = [].slice.call(arguments); 32 | var result = { 33 | // code默认为0代表失败,1为成功 34 | code: 0, 35 | // 描述默认为空 36 | message: '', 37 | // 数据默认为空 38 | data: null, 39 | // v7接口中的status字段,放在第一层方便判断 40 | status: 0, 41 | // 一些数据详情,可以协助调试用 42 | debugInfo: { 43 | type: '未知数据格式' 44 | } 45 | }; 46 | 47 | if (options.dataPath == null) { 48 | // 不需要处理 49 | 50 | return response; 51 | } 52 | 53 | if (typeof options.dataPath === 'string') { 54 | options.dataPath = [options.dataPath]; 55 | } 56 | 57 | // 默认为详情 58 | var isDebug = options.isDebug || false, 59 | paths = options.dataPath, 60 | processFns = exports.dataProcessFn, 61 | len = processFns.length, 62 | num = paths.length, 63 | isFound = false; 64 | 65 | if (!response) { 66 | result.message = '接口返回数据为空!'; 67 | return result; 68 | } 69 | // 添加一个result,将返回接口给子函数 70 | args.push(result); 71 | for (var k = 0; !isFound && k < num; k++) { 72 | // 每次动态修改path参数 73 | args[1] = paths[k]; 74 | 75 | for (var i = 0; !isFound && i < len; i++) { 76 | var fn = processFns[i]; 77 | var returnValue = fn.apply(this, args); 78 | 79 | if (returnValue != null) { 80 | // 找到了或者到了最后一个就退出 81 | if (returnValue.code == 1 || k == num - 1) { 82 | isFound = true; 83 | result = returnValue; 84 | break; 85 | } 86 | } 87 | } 88 | } 89 | 90 | if (!isFound) { 91 | // 没有找到数据需要使用默认 92 | // 如果没有数据处理函数或数据格式不符合任何一个函数的要求 93 | result.message = '没有数据处理函数或者接口数据返回格式不符合要求!'; 94 | // 装载数据可以调试 95 | result.debugInfo.data = response; 96 | } 97 | 98 | // 非null代表已经找到格式了,这个是通过约定越好的 99 | if (!isDebug) { 100 | result.debugInfo = undefined; 101 | } 102 | return result; 103 | }; 104 | })(); 105 | (function() { 106 | 107 | /** 108 | * @description 通过指定路径,来获取对应的数据 109 | * 如果不符合数据要求的,请返回null,这样就会进入下一个函数处理了 110 | * @param {JSON} response 接口返回的数据 111 | * @param {String} path 一个自定义路径,以点分割,用来找数据 112 | * @param {JSON} returnValue 返回数据 113 | * 1:返回列表 114 | * 其它:返回详情 115 | * @return {JSON} 返回的数据,包括多个成功数据,错误提示等等 116 | * */ 117 | function handleDataByPathV6(response, path, returnValue) { 118 | if (!(path && response && response.ReturnInfo && response.BusinessInfo)) { 119 | return null; 120 | } 121 | var debugInfo = { 122 | type: 'v6数据格式:' + path 123 | }; 124 | var returnInfo = response.ReturnInfo, 125 | businessInfo = response.BusinessInfo, 126 | userArea = response.UserArea; 127 | 128 | if (returnInfo.Code == '1') { 129 | if (businessInfo.Code == '1') { 130 | returnValue.code = 1; 131 | 132 | var data = CommonTools.getNameSpaceObj(response, path); 133 | 134 | if (data) { 135 | returnValue.data = data; 136 | } else { 137 | returnValue.message = returnValue.message || '指定路径下没有找到数据'; 138 | returnValue.data = null; 139 | // 3代表业务数据错误 140 | debugInfo.errorType = '3'; 141 | } 142 | } else { 143 | // 2代表业务错误 144 | debugInfo.errorType = '2'; 145 | returnValue.code = 0; 146 | returnValue.message = businessInfo.Description || '接口请求错误,后台业务逻辑处理出错!'; 147 | } 148 | } else { 149 | // v6中的程序错误 150 | // 1代表程序错误 151 | debugInfo.errorType = '1'; 152 | returnValue.code = 0; 153 | returnValue.message = returnInfo.Description || '接口请求错误,后台程序处理出错!'; 154 | } 155 | 156 | returnValue.debugInfo = debugInfo; 157 | return returnValue; 158 | } 159 | 160 | exports.dataProcessFn.push(handleDataByPathV6); 161 | })(); 162 | (function() { 163 | 164 | /** 165 | * @description 通过指定路径,来获取对应的数据 166 | * 如果不符合数据要求的,请返回null,这样就会进入下一个函数处理了 167 | * @param {JSON} response 接口返回的数据 168 | * @param {String} path 一个自定义路径,以点分割,用来找数据 169 | * @param {JSON} returnValue 返回数据 170 | * 1:返回列表 171 | * 其它:返回详情 172 | * @return {JSON} 返回的数据,包括多个成功数据,错误提示等等 173 | * */ 174 | function handleDataByPathV7(response, path, returnValue) { 175 | if (!(path && response && response.status && response.custom)) { 176 | return null; 177 | } 178 | var debugInfo = { 179 | type: 'v7数据格式:' + path 180 | }; 181 | var status = response.status; 182 | 183 | // 对应状态码 184 | returnValue.status = status.code || 0; 185 | returnValue.message = status.text; 186 | 187 | if (status.code == '200') { 188 | returnValue.code = 1; 189 | 190 | var data = CommonTools.getNameSpaceObj(response, path); 191 | 192 | if (data) { 193 | 194 | returnValue.data = data; 195 | } else { 196 | returnValue.message = returnValue.message || '指定路径下没有找到数据'; 197 | returnValue.data = null; 198 | // 3代表业务数据错误 199 | debugInfo.errorType = '3'; 200 | } 201 | } else { 202 | // 请求失败的情况暂时使用接口返回的默认提示 203 | returnValue.code = 0; 204 | // 2代表status错误,message默认就已经在节点中 205 | debugInfo.errorType = '2'; 206 | returnValue.message = returnValue.message || 'status状态错误'; 207 | } 208 | 209 | returnValue.debugInfo = debugInfo; 210 | return returnValue; 211 | } 212 | 213 | exports.dataProcessFn.push(handleDataByPathV7); 214 | })(); 215 | 216 | CommonTools.namespace('dataProcess', exports.dataProcess); 217 | })({}, PullToRefreshTools); -------------------------------------------------------------------------------- /src/core/pulltorefresh.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 作者: dailc 3 | * 创建时间: 2017-03-28 4 | * 版本: [1.0, 2017/05/26 ] 5 | * 版权: dailc 6 | * 描述: 这个工具类都是一些最基本的工具函数 7 | */ 8 | "use strict"; 9 | 10 | var PullToRefreshTools = window.PullToRefreshTools || (function(exports, undefined) { 11 | /** 12 | * 通用代码 13 | */ 14 | (function() { 15 | /** 16 | * @description 产生一个 唯一uuid-guid 17 | * @param {Number} len 18 | * @param {Number} radix 基数 19 | * @return {String} 返回一个随机性的唯一uuid 20 | */ 21 | exports.uuid = function(len, radix) { 22 | var chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split(''), 23 | uuid = [], 24 | i; 25 | radix = radix || chars.length; 26 | 27 | if (len) { 28 | for (i = 0; i < len; i++) uuid[i] = chars[0 | Math.random() * radix]; 29 | } else { 30 | var r; 31 | 32 | uuid[8] = uuid[13] = uuid[18] = uuid[23] = '-'; 33 | uuid[14] = '4'; 34 | 35 | for (i = 0; i < 36; i++) { 36 | if (!uuid[i]) { 37 | r = 0 | Math.random() * 16; 38 | uuid[i] = chars[(i == 19) ? (r & 0x3) | 0x8 : r]; 39 | } 40 | } 41 | } 42 | return uuid.join(''); 43 | }; 44 | /** 45 | * 空函数 46 | */ 47 | exports.noop = function() {}; 48 | /** 49 | * extend(simple) 50 | * @param {type} target 51 | * @param {type} source 52 | * @param {type} deep 53 | * @returns {unresolved} 54 | */ 55 | exports.extend = function() { //from jquery2 56 | // from jquery2 57 | var options, name, src, copy, copyIsArray, clone, 58 | target = arguments[0] || {}, 59 | i = 1, 60 | length = arguments.length, 61 | deep = false; 62 | 63 | if (typeof target === "boolean") { 64 | deep = target; 65 | target = arguments[i] || {}; 66 | i++; 67 | } 68 | if (typeof target !== "object" && !exports.isFunction(target)) { 69 | target = {}; 70 | } 71 | if (i === length) { 72 | target = this; 73 | i--; 74 | } 75 | for (; i < length; i++) { 76 | if ((options = arguments[i]) != null) { 77 | for (name in options) { 78 | src = target[name]; 79 | copy = options[name]; 80 | if (target === copy) { 81 | continue; 82 | } 83 | if (deep && copy && (exports.isPlainObject(copy) || (copyIsArray = exports.isArray(copy)))) { 84 | if (copyIsArray) { 85 | copyIsArray = false; 86 | clone = src && exports.isArray(src) ? src : []; 87 | 88 | } else { 89 | clone = src && exports.isPlainObject(src) ? src : {}; 90 | } 91 | 92 | target[name] = exports.extend(deep, clone, copy); 93 | } else if (copy !== undefined) { 94 | target[name] = copy; 95 | } 96 | } 97 | } 98 | } 99 | return target; 100 | }; 101 | /** 102 | * isFunction 103 | */ 104 | exports.isFunction = function(value) { 105 | return exports.type(value) === "function"; 106 | }; 107 | /** 108 | * isPlainObject 109 | */ 110 | exports.isPlainObject = function(obj) { 111 | return exports.isObject(obj) && !exports.isWindow(obj) && Object.getPrototypeOf(obj) === Object.prototype; 112 | }; 113 | exports.isArray = Array.isArray || 114 | function(object) { 115 | return object instanceof Array; 116 | }; 117 | /** 118 | * isWindow(需考虑obj为undefined的情况) 119 | */ 120 | exports.isWindow = function(obj) { 121 | return obj != null && obj === obj.window; 122 | }; 123 | /** 124 | * isObject 125 | */ 126 | exports.isObject = function(obj) { 127 | return exports.type(obj) === "object"; 128 | }; 129 | exports.type = function(obj) { 130 | return obj == null ? String(obj) : class2type[{}.toString.call(obj)] || "object"; 131 | }; 132 | /** 133 | * @description each遍历操作 134 | * @param {type} elements 135 | * @param {type} callback 136 | * @returns {global} 137 | */ 138 | exports.each = function(elements, callback, hasOwnProperty) { 139 | if (!elements) { 140 | return this; 141 | } 142 | if (typeof elements.length === 'number') { 143 | [].every.call(elements, function(el, idx) { 144 | return callback.call(el, idx, el) !== false; 145 | }); 146 | } else { 147 | for (var key in elements) { 148 | if (hasOwnProperty) { 149 | if (elements.hasOwnProperty(key)) { 150 | if (callback.call(elements[key], key, elements[key]) === false) return elements; 151 | } 152 | } else { 153 | if (callback.call(elements[key], key, elements[key]) === false) return elements; 154 | } 155 | } 156 | } 157 | return this; 158 | }; 159 | /** 160 | * @description 选择这段代码用到的太多了,因此抽取封装出来 161 | * @param {Object} element dom元素或者selector 162 | */ 163 | exports.selector = function(element) { 164 | if (typeof element === 'string') { 165 | element = document.querySelector(element); 166 | } 167 | 168 | return element; 169 | }; 170 | var class2type = {}; 171 | exports.each(['Boolean', 'Number', 'String', 'Function', 'Array', 'Date', 'RegExp', 'Object', 'Error'], function(i, name) { 172 | class2type["[object " + name + "]"] = name.toLowerCase(); 173 | }); 174 | (function() { 175 | function detect(ua) { 176 | this.os = {}; 177 | this.os.name = 'browser'; 178 | var funcs = [ 179 | function() { //android 180 | var android = ua.match(/(Android);?[\s\/]+([\d.]+)?/); 181 | if (android) { 182 | this.os.android = true; 183 | this.os.version = android[2]; 184 | this.os.isBadAndroid = !(/Chrome\/\d/.test(window.navigator.appVersion)); 185 | this.os.name += '_' + 'Android'; 186 | this.os.name += '_' + 'mobile'; 187 | } 188 | return this.os.android === true; 189 | }, 190 | function() { //ios 191 | var iphone = ua.match(/(iPhone\sOS)\s([\d_]+)/); 192 | if (iphone) { //iphone 193 | this.os.ios = this.os.iphone = true; 194 | this.os.version = iphone[2].replace(/_/g, '.'); 195 | this.os.name += '_' + 'iphone'; 196 | this.os.name += '_' + 'mobile'; 197 | } else { 198 | var ipad = ua.match(/(iPad).*OS\s([\d_]+)/); 199 | if (ipad) { //ipad 200 | this.os.ios = this.os.ipad = true; 201 | this.os.version = ipad[2].replace(/_/g, '.'); 202 | this.os.name += '_' + 'iOS'; 203 | this.os.name += '_' + 'mobile'; 204 | } 205 | 206 | } 207 | return this.os.ios === true; 208 | } 209 | ]; 210 | [].every.call(funcs, function(func) { 211 | return !func.call(exports); 212 | }); 213 | } 214 | detect.call(exports, navigator.userAgent); 215 | })(); 216 | /** 217 | * @description 判断os系统 ,判断是否是ejs 218 | * ejs.os 219 | * @param {type} 220 | * @returns {undefined} 221 | */ 222 | (function() { 223 | function detect(ua) { 224 | this.os = this.os || {}; 225 | //比如 EpointEJS/6.1.1 也可以/(EpointEJS)\/([\d\.]+)/i 226 | var ejs = ua.match(/EpointEJS/i); 227 | if (ejs) { 228 | this.os.ejs = true; 229 | this.os.name += '_' + 'ejs'; 230 | } 231 | //阿里的钉钉 DingTalk/3.0.0 232 | var dd = ua.match(/DingTalk/i); 233 | if (dd) { 234 | this.os.dd = true; 235 | this.os.name += '_' + 'dd'; 236 | } 237 | } 238 | detect.call(exports, navigator.userAgent); 239 | })(); 240 | 241 | })(); 242 | /** 243 | * @description 模拟Class的基类,以便模拟Class进行继承等 244 | * 仿照mui写的 245 | */ 246 | (function() { 247 | //同时声明多个变量,用,分开要好那么一点点 248 | var initializing = false, 249 | //通过正则检查是否是函数 250 | fnTest = /xyz/.test(function() { 251 | xyz; 252 | }) ? /\b_super\b/ : /.*/; 253 | var Clazz = function() {}; 254 | //很灵活的一种写法,直接重写Class的extend,模拟继承 255 | Clazz.extend = function(prop) { 256 | var _super = this.prototype; 257 | initializing = true; 258 | //可以这样理解:这个prototype将this中的方法和属性全部都复制了一遍 259 | var prototype = new this(); 260 | initializing = false; 261 | for (var name in prop) { 262 | //这一些列操作逻辑并不简单,得清楚运算符优先级 263 | //逻辑与的优先级是高于三元条件运算符的,得注意下 264 | //只有继承的函数存在_super时才会触发(哪怕注释也一样进入) 265 | //所以梳理后其实一系列的操作就是判断是否父对象也有相同对象 266 | //如果有,则对应函数存在_super这个东西 267 | prototype[name] = typeof prop[name] == "function" && 268 | typeof _super[name] == "function" && fnTest.test(prop[name]) ? 269 | (function(name, fn) { 270 | return function() { 271 | var tmp = this._super; 272 | this._super = _super[name]; 273 | var ret = fn.apply(this, arguments); 274 | this._super = tmp; 275 | return ret; 276 | }; 277 | })(name, prop[name]) : 278 | prop[name]; 279 | } 280 | /** 281 | * @description Clss的构造,默认会执行init方法 282 | */ 283 | function Clazz() { 284 | if (!initializing && this.init) { 285 | this.init.apply(this, arguments); 286 | } 287 | } 288 | Clazz.prototype = prototype; 289 | Clazz.prototype.constructor = Clazz; 290 | //callee 的作用是返回当前执行函数的自身 291 | //这里其实就是this.extend,不过严格模式下禁止使用 292 | //Clazz.extend = arguments.callee; 293 | //替代callee 返回本身 294 | Clazz.extend = this.extend; 295 | return Clazz; 296 | }; 297 | exports.Clazz = Clazz; 298 | })(); 299 | 300 | /** 301 | * 方便的生成对象下的命名空间 302 | */ 303 | (function() { 304 | /** 305 | * @description 设置一个Util对象下的命名空间 306 | * @param {String} namespace 307 | * @param {Object} obj 需要赋值的目标对象 308 | */ 309 | exports.namespace = function(namespace, obj) { 310 | var parent = window.PullToRefreshTools; 311 | 312 | if (!namespace) { 313 | return parent; 314 | } 315 | 316 | var namespaceArr = namespace.split('.'), 317 | len = namespaceArr.length; 318 | 319 | for (var i = 0; i < len - 1; i++) { 320 | var tmp = namespaceArr[i]; 321 | // 不存在的话要重新创建对象 322 | parent[tmp] = parent[tmp] || {}; 323 | // parent要向下一级 324 | parent = parent[tmp]; 325 | 326 | } 327 | parent[namespaceArr[len - 1]] = obj; 328 | 329 | return parent[namespaceArr[len - 1]]; 330 | }; 331 | /** 332 | * @description 获取这个模块下对应命名空间的对象 333 | * 如果不存在,则返回null,这个api只要是供内部获取接口数据时调用 334 | * @param {Object} module 335 | * @param {Array} namespace 336 | */ 337 | exports.getNameSpaceObj = function(module, namespace) { 338 | if (!namespace) { 339 | return null; 340 | } 341 | var namespaceArr = namespace.split('.'), 342 | len = namespaceArr.length; 343 | for (var i = 0; i < len; i++) { 344 | module && (module = module[namespaceArr[i]]); 345 | } 346 | return module; 347 | }; 348 | })(); 349 | 350 | /** 351 | * 兼容require 352 | */ 353 | if (typeof module != 'undefined' && module.exports) { 354 | module.exports = exports; 355 | } else if (typeof define == 'function' && (define.amd || define.cmd)) { 356 | define(function() { 357 | return exports; 358 | }); 359 | } 360 | 361 | return exports; 362 | })({}); -------------------------------------------------------------------------------- /src/css/images/img_pulltorefresh_skin2_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dailc/pulltorefresh-h5-iscroll/f221a5e60e6f6c1c955e99f3ea59c1f373ff08ae/src/css/images/img_pulltorefresh_skin2_1.png -------------------------------------------------------------------------------- /src/css/images/img_pulltorefresh_skin2_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dailc/pulltorefresh-h5-iscroll/f221a5e60e6f6c1c955e99f3ea59c1f373ff08ae/src/css/images/img_pulltorefresh_skin2_2.png -------------------------------------------------------------------------------- /src/css/images/img_pulltorefresh_skin2_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dailc/pulltorefresh-h5-iscroll/f221a5e60e6f6c1c955e99f3ea59c1f373ff08ae/src/css/images/img_pulltorefresh_skin2_3.png -------------------------------------------------------------------------------- /src/pulltorefresh.skin.default.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 作者: dailc 3 | * 创建时间: 2017/03/28 4 | * 版本: [1.0, 2017/05/26 ] 5 | * 版权: dailc 6 | * 描述: 皮肤类只会实现UI相关的hook函数 7 | * 默认皮肤default,最简单的下拉刷新 8 | * 依赖mui的css 9 | */ 10 | (function(exports, CommonTools) { 11 | 12 | // 默认的全局参数-主要用来配置下拉刷新提示的一些css class 13 | var NAMESPACE = 'mui-'; 14 | var CLASS_PULL_TOP_POCKET = NAMESPACE + 'pull-top-pocket'; 15 | var CLASS_PULL_BOTTOM_POCKET = NAMESPACE + 'pull-bottom-pocket'; 16 | var CLASS_PULL = NAMESPACE + 'pull'; 17 | var CLASS_PULL_LOADING = NAMESPACE + 'pull-loading'; 18 | var CLASS_PULL_CAPTION = NAMESPACE + 'pull-caption'; 19 | var CLASS_PULL_CAPTION_DOWN = NAMESPACE + 'pull-caption-down'; 20 | var CLASS_PULL_CAPTION_REFRESH = NAMESPACE + 'pull-caption-refresh'; 21 | var CLASS_PULL_CAPTION_NOMORE = NAMESPACE + 'pull-caption-nomore'; 22 | 23 | var CLASS_ICON = NAMESPACE + 'icon'; 24 | var CLASS_SPINNER = NAMESPACE + 'spinner'; 25 | var CLASS_ICON_PULLDOWN = NAMESPACE + 'icon-pulldown'; 26 | var CLASS_ICON_SUCCESS = NAMESPACE + 'icon-checkmarkempty'; 27 | var CLASS_ICON_ERROR = NAMESPACE + 'icon-info'; 28 | 29 | var CLASS_BLOCK = NAMESPACE + 'block'; 30 | var CLASS_HIDDEN = NAMESPACE + 'hidden'; 31 | var CLASS_VISIBILITY = NAMESPACE + 'visibility'; 32 | 33 | var CLASS_LOADING_UP = CLASS_PULL_LOADING + ' ' + CLASS_ICON + ' ' + CLASS_ICON_PULLDOWN; 34 | var CLASS_LOADING_DOWN = CLASS_PULL_LOADING + ' ' + CLASS_ICON + ' ' + CLASS_ICON_PULLDOWN; 35 | var CLASS_LOADING = CLASS_PULL_LOADING + ' ' + CLASS_ICON + ' ' + CLASS_SPINNER; 36 | 37 | var CLASS_LOADING_SUCCESS = CLASS_PULL_LOADING + ' ' + CLASS_ICON + ' ' + CLASS_ICON_SUCCESS; 38 | 39 | var CLASS_LOADING_ERROR = CLASS_PULL_LOADING + ' ' + CLASS_ICON + ' ' + CLASS_ICON_ERROR; 40 | var pocketHtml = ['
', '
', '
{contentrefresh}
', '
'].join(''); 41 | 42 | 43 | /** 44 | * 创建一个Class对象 45 | * 只需要关注默认的UI实现即可 46 | * UI只需要实现需要实现的函数 47 | */ 48 | var PullToRefresh = CommonTools.core.extend({ 49 | 50 | /** 51 | * @description 生成下拉刷新提示,这个需要被具体实现 52 | * 这个默认实现就直接在一个函数里面同时生成下拉和上拉提示了 53 | */ 54 | _initPullToRefreshTipsHook: function(enablePullDown, enablePullUp) { 55 | this._initPocket(); 56 | if(!enablePullUp) { 57 | this.bottomPocket && this.bottomPocket.classList.add(CLASS_HIDDEN); 58 | } 59 | if(!enablePullDown) { 60 | this.topPocket && this.topPocket.classList.add(CLASS_HIDDEN); 61 | } 62 | }, 63 | /** 64 | * @description 初始化下拉刷新 65 | */ 66 | _initPulldownRefreshState: function() { 67 | this.pullPocket = this.topPocket; 68 | this.pullPocket.classList.add(CLASS_BLOCK); 69 | this.pullPocket.classList.add(CLASS_VISIBILITY); 70 | this.pullCaption = this.topCaption; 71 | this.pullLoading = this.topLoading; 72 | }, 73 | /** 74 | * @description 初始化上拉加载 75 | */ 76 | _initPullupRefreshState: function() { 77 | this.pullPocket = this.bottomPocket; 78 | this.pullPocket.classList.add(CLASS_BLOCK); 79 | this.pullPocket.classList.add(CLASS_VISIBILITY); 80 | this.pullCaption = this.bottomCaption; 81 | this.pullLoading = this.bottomLoading; 82 | }, 83 | /** 84 | * @description 下拉过程中的钩子函数 85 | * @param {Number} deltaY 86 | * @param {Number} thresholdHeight 对应的高度阈值 87 | */ 88 | _pullingHook: function(deltaY, thresholdHeight) { 89 | // 高度阈值 90 | if(deltaY >= thresholdHeight) { 91 | this._setCaption(true, this.options.down.contentover); 92 | } else if(deltaY < thresholdHeight) { 93 | this._setCaption(true, this.options.down.contentdown); 94 | } 95 | }, 96 | /** 97 | * @description 下拉刷新的成功动画,每次确保触发一次 98 | */ 99 | _pulldownLoaingAnimationHook: function() { 100 | this._setCaption(true, this.options.down.contentrefresh); 101 | }, 102 | /** 103 | * @description 下拉刷新的成功动画-动画完毕后可能的成功提示,每次确保触发一次 104 | * 比如在成功里面提示加载了多少条数据,如果不需要可以传null,会直接走到end事件里 105 | * @param {Function} done 这个可以提前结束动画-如果不想要的话 106 | * @param {Boolean} isSuccess 是否请求成功 107 | */ 108 | _pulldownLoaingAnimationSuccessHook: function(done, isSuccess) { 109 | if(this.options.down.isSuccessTips) { 110 | this._setCaption(true, isSuccess ? this.options.down.contentrefreshsuccess : this.options.down.contentrefresherror); 111 | } else { 112 | // 否则直接没有成功提示 113 | done(); 114 | } 115 | 116 | }, 117 | /** 118 | * @description 下拉刷新的动画完成后的回调,可以用来重置状态 119 | */ 120 | _pulldownLoaingAnimationEndHook: function() { 121 | this._setCaption(true, this.options.down.contentdown, true); 122 | this.topPocket.classList.remove(CLASS_VISIBILITY); 123 | }, 124 | /** 125 | * @description 上拉加载的成功动画,每次确保触发一次 126 | */ 127 | _pullupLoaingAnimationHook: function(isFinished) { 128 | if(this.options.up) { 129 | this._setCaption(false, this.options.up.contentrefresh); 130 | } 131 | 132 | }, 133 | /** 134 | * @description 上拉加载的成功动画-动画完毕后可能的成功提示,每次确保触发一次 135 | */ 136 | _pullupLoaingAnimationSuccessHook: function(isFinished) { 137 | if(this.options.up) { 138 | if(isFinished) { 139 | this._setCaption(false, this.options.up.contentnomore); 140 | } else { 141 | this._setCaption(false, this.options.up.contentdown); 142 | } 143 | //this.bottomPocket.classList.remove(CLASS_VISIBILITY); 144 | } 145 | 146 | }, 147 | /** 148 | * @description _disablePullUpHook 149 | */ 150 | _disablePullUpHook: function() { 151 | this.bottomPocket.className = 'mui-pull-bottom-pocket' + ' ' + CLASS_HIDDEN; 152 | }, 153 | /** 154 | * @description disablePullUpHook 155 | */ 156 | _enablePullUpHook: function() { 157 | if(!this.options.up) { 158 | return; 159 | } 160 | this.bottomPocket.classList.remove(CLASS_HIDDEN); 161 | this._setCaption(false, this.options.up.contentdown); 162 | }, 163 | /** 164 | * @description 创建上拉提示或下拉提示 165 | * @param {Object} clazz 166 | * @param {Object} options 167 | * @param {Object} iconClass 168 | */ 169 | _createPocket: function(clazz, options, iconClass) { 170 | var pocket = document.createElement('div'); 171 | pocket.className = clazz; 172 | pocket.innerHTML = pocketHtml.replace('{contentrefresh}', options.contentinit).replace('{icon}', iconClass); 173 | return pocket; 174 | }, 175 | /** 176 | * @description 初始化下拉刷新和上拉加载提示 177 | */ 178 | _initPocket: function() { 179 | var options = this.options; 180 | if(options.down && options.down.hasOwnProperty('callback')) { 181 | this.topPocket = this.wrapper.querySelector('.' + CLASS_PULL_TOP_POCKET); 182 | if(!this.topPocket) { 183 | this.topPocket = this._createPocket(CLASS_PULL_TOP_POCKET, options.down, CLASS_LOADING_DOWN); 184 | this.wrapper.insertBefore(this.topPocket, this.wrapper.firstChild); 185 | } 186 | this.topLoading = this.topPocket.querySelector('.' + CLASS_PULL_LOADING); 187 | this.topCaption = this.topPocket.querySelector('.' + CLASS_PULL_CAPTION); 188 | } 189 | if(options.up && options.up.hasOwnProperty('callback')) { 190 | this.bottomPocket = this.scrollWrap.querySelector('.' + CLASS_PULL_BOTTOM_POCKET); 191 | if(!this.bottomPocket) { 192 | this.bottomPocket = this._createPocket(CLASS_PULL_BOTTOM_POCKET, options.up, CLASS_LOADING); 193 | this.scrollWrap.appendChild(this.bottomPocket); 194 | } 195 | this.bottomLoading = this.bottomPocket.querySelector('.' + CLASS_PULL_LOADING); 196 | this.bottomCaption = this.bottomPocket.querySelector('.' + CLASS_PULL_CAPTION); 197 | } 198 | 199 | }, 200 | 201 | /** 202 | * @description 设置提示的class 203 | * @param {Object} isPulldown 204 | * @param {Object} caption 205 | * @param {Object} title 206 | */ 207 | _setCaptionClass: function(isPulldown, caption, title) { 208 | if(!this.options.up) { 209 | return; 210 | } 211 | if(!isPulldown) { 212 | 213 | switch(title) { 214 | case this.options.up.contentdown: 215 | caption.className = CLASS_PULL_CAPTION + ' ' + CLASS_PULL_CAPTION_DOWN; 216 | break; 217 | case this.options.up.contentrefresh: 218 | caption.className = CLASS_PULL_CAPTION + ' ' + CLASS_PULL_CAPTION_REFRESH 219 | break; 220 | case this.options.up.contentnomore: 221 | caption.className = CLASS_PULL_CAPTION + ' ' + CLASS_PULL_CAPTION_NOMORE; 222 | break; 223 | } 224 | } 225 | }, 226 | /** 227 | * @description 设置caption 228 | * @param {Object} isPulldown 229 | * @param {Object} title 230 | * @param {Object} reset 231 | */ 232 | _setCaption: function(isPulldown, title, reset) { 233 | if(this.loading) { 234 | return; 235 | } 236 | if(isPulldown) { 237 | this._initPulldownRefreshState(); 238 | } else { 239 | this._initPullupRefreshState(); 240 | } 241 | var options = this.options; 242 | var pocket = this.pullPocket; 243 | var caption = this.pullCaption; 244 | var loading = this.pullLoading; 245 | var isPulldown = this.pulldown; 246 | var self = this; 247 | if(pocket) { 248 | if(reset) { 249 | setTimeout(function() { 250 | caption.innerHTML = self.lastTitle = title; 251 | if(isPulldown) { 252 | loading.className = CLASS_LOADING_DOWN; 253 | } else { 254 | self._setCaptionClass(false, caption, title); 255 | loading.className = CLASS_LOADING; 256 | } 257 | loading.style.webkitAnimation = ""; 258 | loading.style.webkitTransition = ""; 259 | loading.style.webkitTransform = ""; 260 | }, 100); 261 | } else { 262 | if(title !== this.lastTitle) { 263 | caption.innerHTML = title; 264 | if(isPulldown) { 265 | if(title === options.down.contentrefresh) { 266 | loading.className = CLASS_LOADING; 267 | loading.style.webkitAnimation = "spinner-spin 1s step-end infinite"; 268 | } else if(title === options.down.contentover) { 269 | loading.className = CLASS_LOADING_UP; 270 | loading.style.webkitTransition = "-webkit-transform 0.3s ease-in"; 271 | loading.style.webkitTransform = "rotate(180deg)"; 272 | } else if(title === options.down.contentdown) { 273 | loading.className = CLASS_LOADING_DOWN; 274 | loading.style.webkitTransition = "-webkit-transform 0.3s ease-in"; 275 | loading.style.webkitTransform = "rotate(0deg)"; 276 | } else if(title === options.down.contentrefreshsuccess) { 277 | //隐藏loading先 278 | loading.className = CLASS_LOADING_SUCCESS; 279 | loading.style.webkitTransition = "-webkit-transform 0.3s ease-in"; 280 | loading.style.webkitTransform = "scale(1.2,1.2)"; 281 | loading.style.webkitAnimation = "none"; 282 | //优先显示tips 283 | caption.innerHTML = self.successTips || title; 284 | } else if(title === options.down.contentrefresherror) { 285 | loading.className = CLASS_LOADING_ERROR; 286 | loading.style.webkitTransition = "-webkit-transform 0.3s ease-in"; 287 | loading.style.webkitTransform = "scale(1.2,1.2)"; 288 | loading.style.webkitAnimation = "none"; 289 | } 290 | } else { 291 | if(options.up) { 292 | if(title === options.up.contentrefresh) { 293 | loading.className = CLASS_LOADING + ' ' + CLASS_VISIBILITY; 294 | } else { 295 | loading.className = CLASS_LOADING + ' ' + CLASS_HIDDEN; 296 | } 297 | self._setCaptionClass(false, caption, title); 298 | } 299 | 300 | } 301 | this.lastTitle = title; 302 | } 303 | } 304 | 305 | } 306 | }, 307 | 308 | }); 309 | 310 | /** 311 | * @description 初始化下拉刷新组件,init是兼容工厂的调用方式 312 | * @param {JSON} options 传入的参数 313 | * @return 返回的是一个下拉刷新对象 314 | */ 315 | PullToRefresh.init = function(options) { 316 | return new PullToRefresh(options); 317 | }; 318 | 319 | 320 | CommonTools.namespace('skin.defaults', PullToRefresh); 321 | 322 | })({}, PullToRefreshTools); -------------------------------------------------------------------------------- /src/pulltorefresh.skin.native.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 作者: dailc 3 | * 创建时间: 2017/03/28 4 | * 版本: [1.0, 2017/05/26 ] 5 | * 版权: dailc 6 | * 描述: 这个皮肤是ejs或钉钉下的下拉刷新,使用了他们自带提供的原生下拉刷新,以及自定义生成上拉加载 7 | * 注意:分别在ejs下或者钉钉下依赖对应的库文件 8 | */ 9 | (function(exports, CommonTools) { 10 | 11 | var isSupportTouch = "ontouchend" in document ? true : false; 12 | //默认的下拉刷新每一个页面只支持一个,所以是单例模式 13 | var instance; 14 | //这里的isPullDown是指从上往下拉 15 | //pullup是指从下往上拉 16 | var isTouch = false, 17 | isPullDown = false; 18 | //定义一个global 19 | var global = {}; 20 | /** 21 | * 默认的设置参数 22 | */ 23 | var defaultSetting = { 24 | //下拉有关 25 | down: { 26 | callback: CommonTools.noop 27 | }, 28 | //上拉有关 29 | up: { 30 | //是否自动上拉加载-初始化是是否自动 31 | auto: false, 32 | //是否隐藏那个加载更多动画,达到默认加载效果 33 | show: true, 34 | contentrefresh: '正在加载...', 35 | callback: CommonTools.noop 36 | }, 37 | //注意,传给Mui时可以传 #id形式或者是 原生dom对象 38 | element: '#pullrefresh' 39 | }; 40 | var pulluploadingTips = '
{{contentrefresh}}
'; 41 | /** 42 | * @description 兼容ejs情况下的下拉刷新 43 | * 去除多余dom 44 | * @param {HTMLElement} elem 对应的dom 45 | */ 46 | function compatibleNative(elem) { 47 | //计划改变,ejs下拉刷新不使用scroll,否则不好计算什么时候可以滑动,而是直接去除这个dom 48 | // mui(elem).scroll({ 49 | // deceleration: 0.0005 //flick 减速系数,系数越大,滚动速度越慢,滚动距离越小,默认值0.0006 50 | // }); 51 | if (typeof elem === 'string') { 52 | elem = document.querySelector(elem); 53 | } 54 | if (CommonTools.os.ejs || CommonTools.os.dd) { 55 | //去除dom,ejs下拉刷新不需要scroll 56 | var pullRefreshDom = elem; 57 | pullRefreshDom.classList.remove('mui-scroll-wrapper'); 58 | var scrollDom = pullRefreshDom.querySelector('.mui-scroll'); 59 | if (scrollDom) { 60 | //pullRefreshDom.innerHTML = scrollDom.innerHTML; 61 | scrollDom.classList.remove('mui-scroll'); 62 | } 63 | } else { 64 | 65 | } 66 | 67 | } 68 | 69 | /** 70 | * @description 将string字符串转为html对象,默认创一个div填充 71 | * @param {String} strHtml 目标字符串 72 | * @return {HTMLElement} 返回处理好后的html对象,如果字符串非法,返回null 73 | */ 74 | function pareseStringToHtml(strHtml) { 75 | if (strHtml == null || typeof(strHtml) != "string") { 76 | return null; 77 | } 78 | //创一个灵活的div 79 | var i, a = document.createElement("div"); 80 | var b = document.createDocumentFragment(); 81 | a.innerHTML = strHtml; 82 | while (i = a.firstChild) b.appendChild(i); 83 | return b; 84 | } 85 | /** 86 | * @description 浏览器视口的高度 87 | * @return {Number} 返回具体高度 88 | */ 89 | function getWinHeight() { 90 | var windowHeight = 0; 91 | if (document.compatMode == "CSS1Compat") { 92 | windowHeight = document.documentElement.clientHeight; 93 | } else { 94 | windowHeight = document.body.clientHeight; 95 | } 96 | return windowHeight; 97 | } 98 | /** 99 | * @description 获取滚动条在Y轴上的滚动距离 100 | * @return {Number} 返回具体距离 101 | */ 102 | function getScrollTop() { 103 | var scrollTop = 0, 104 | bodyScrollTop = 0, 105 | documentScrollTop = 0; 106 | if (document.body) { 107 | bodyScrollTop = document.body.scrollTop || 0; 108 | } 109 | if (document.documentElement) { 110 | documentScrollTop = document.documentElement.scrollTop || 0; 111 | } 112 | scrollTop = (bodyScrollTop > documentScrollTop) ? bodyScrollTop : documentScrollTop; 113 | return scrollTop; 114 | } 115 | /** 116 | * @description 获取文档的总高度 117 | * @return {Number} 返回具体高度 118 | */ 119 | function getScrollHeight() { 120 | var scrollHeight = 0, 121 | bodyScrollHeight = 0, 122 | documentScrollHeight = 0; 123 | if (document.body) { 124 | bodyScrollHeight = document.body.scrollHeight; 125 | } 126 | 127 | if (document.documentElement) { 128 | documentScrollHeight = document.documentElement.scrollHeight; 129 | } 130 | scrollHeight = (bodyScrollHeight - documentScrollHeight > 0) ? bodyScrollHeight : documentScrollHeight; 131 | return scrollHeight; 132 | } 133 | /** 134 | * @constructor 构造函数 135 | * @description 定义下拉刷新构造函数 136 | * @param {HTMLElement} elem 137 | * @param {JSON} options 138 | */ 139 | function PullRefresh(options) { 140 | options = CommonTools.extend(true, {}, defaultSetting, options); 141 | 142 | var $this = this; 143 | var elem = options.container; 144 | $this.loadingUp = false; 145 | $this.finished = false; 146 | $this.options = options; 147 | if (typeof elem === 'string') { 148 | elem = document.querySelector(elem); 149 | } 150 | $this.elem = elem; 151 | 152 | //增加class 153 | $this.elem.classList.add('pulltorefresh-native-type'); 154 | setPullDownFunc(); 155 | 156 | if ($this.options.down) { 157 | if (CommonTools.os.dd) { 158 | //钉钉的下拉刷新 159 | //开启下拉刷新 160 | dd.ui.pullToRefresh.enable({ 161 | onSuccess: function() { 162 | //alert('下拉刷新成功,立刻收起'); 163 | $this.options.down.callback && $this.options.down.callback(); 164 | }, 165 | onFail: function() { 166 | //alert('下拉刷新失败'); 167 | dd.ui.pullToRefresh.stop(); 168 | } 169 | }); 170 | } else { 171 | //ejs的下拉刷新 172 | //开启下拉刷新 173 | if (ejs.nativeUI) { 174 | ejs.nativeUI.pullToRefresh.enable(function() { 175 | $this.options.down.callback && $this.options.down.callback(); 176 | }); 177 | } else { 178 | ejs.ui.pullToRefresh.enable({ 179 | success: function() { 180 | $this.options.down.callback && $this.options.down.callback(); 181 | }, 182 | error: function() { 183 | //alert('下拉刷新失败'); 184 | ejs.ui.pullToRefresh.stop(); 185 | } 186 | }); 187 | } 188 | 189 | } 190 | } 191 | 192 | //兼容原生环境下的处理 193 | compatibleNative($this.elem || '#pullrefresh'); 194 | } 195 | /** 196 | * @description 主动上拉加载更多 197 | */ 198 | PullRefresh.prototype.pullupLoading = function() { 199 | if (instance.options.up) { 200 | //加载更多 201 | loadMoreAnimation(true); 202 | //触发上拉回调 203 | instance.options.up.callback && instance.options.up.callback(); 204 | } 205 | }; 206 | PullRefresh.prototype.endPullUpToRefresh = function(isNoMore) { 207 | if (isNoMore) { 208 | this.finished = true; 209 | } 210 | if (instance.options.up) { 211 | //去除加载更多动画 212 | loadMoreAnimation(false); 213 | } 214 | }; 215 | PullRefresh.prototype.endPullDownToRefresh = function() { 216 | //关闭下拉刷新 217 | if (CommonTools.os.dd) { 218 | dd.ui.pullToRefresh.stop(); 219 | } else if (CommonTools.os.ejs) { 220 | if (ejs.nativeUI) { 221 | ejs.nativeUI.pullToRefresh.stop(); 222 | } else { 223 | ejs.ui.pullToRefresh.stop(); 224 | } 225 | } 226 | 227 | }; 228 | PullRefresh.prototype.refresh = function(refresh) { 229 | this.finished = false; 230 | }; 231 | /** 232 | * @description 设置下拉刷新相关 233 | */ 234 | function setPullDownFunc() { 235 | var x = 0, 236 | y = 0, 237 | scrollTop = 0; 238 | var b = document.body; 239 | //监听touch时间,模拟下拉 240 | var touchStartEvt = isSupportTouch ? 'touchstart' : 'mousedown'; 241 | b.addEventListener(touchStartEvt, function(evt) { 242 | //console.log('touchstart'); 243 | var touch; 244 | if (isSupportTouch) { 245 | touch = evt.touches[0]; //获取第一个触点 246 | } else { 247 | touch = evt; 248 | } 249 | x = Number(touch.pageX); //页面触点X坐标 250 | y = Number(touch.pageY); //页面触点Y坐标 251 | scrollTop = b.scrollTop; 252 | isTouch = true; 253 | //console.log('x = ' + x); 254 | //console.log('y = ' + y); 255 | //console.log('scrollTop = ' + scrollTop); 256 | }); 257 | var touchEndEvt = isSupportTouch ? 'touchend' : 'mouseup'; 258 | b.addEventListener(touchEndEvt, function(evt) { 259 | //console.log('touchend'); 260 | isTouch = false; 261 | isPullDown = false; 262 | }); 263 | var touchMoveEvt = isSupportTouch ? 'touchmove' : 'mousemove'; 264 | b.addEventListener(touchMoveEvt, function(evt) { 265 | //console.log('touchmove'); 266 | var touch; 267 | if (isSupportTouch) { 268 | touch = evt.touches[0]; //获取第一个触点 269 | } else { 270 | touch = evt; 271 | } 272 | var mx = Number(touch.pageX); //页面触点X坐标 273 | var my = Number(touch.pageY); //页面触点Y坐标 274 | 275 | //console.log("isTouch = " + isTouch) 276 | //console.log("y-my = " + (y-my)) 277 | //console.log("b.scrollTop = " + b.scrollTop); 278 | if (isTouch) { 279 | if (my - y > 30) { 280 | if (b.scrollTop == 0) { 281 | if (!isPullDown) { 282 | console.log("PullDown"); 283 | isPullDown = true; 284 | } 285 | } 286 | } 287 | //alert('scrollTop:'+scrollTop+',b.scrollTop:'+b.scrollTop+',y:'+y+',my:'+my); 288 | if (y - my > 100) { 289 | 290 | if (scrollTop == b.scrollTop) { 291 | if (!instance.loadingUp) { 292 | //console.log('my = ' + my); 293 | //console.log('y = ' + y); 294 | //console.log('scrollTop = ' + scrollTop); 295 | //console.log('b.scrollTop = ' + b.scrollTop); 296 | //console.log("pullup,finish:"+instance.finished); 297 | if (!instance.finished) { 298 | if (instance.options.up) { 299 | //加载更多 300 | loadMoreAnimation(true); 301 | //触发上拉回调 302 | instance.options.up.callback && instance.options.up.callback(); 303 | } 304 | } 305 | 306 | } 307 | } 308 | } 309 | 310 | } 311 | }); 312 | 313 | //ios下的补救措施 314 | var scrollFunc = function() { 315 | var y = getScrollTop(); 316 | var slider = document.getElementById('sliderSegmentedControl'); 317 | 318 | if ((y + getWinHeight()) === getScrollHeight()) { 319 | if (!instance.loadingUp) { 320 | if (!instance.finished) { 321 | if (instance.options.up) { 322 | //加载更多 323 | loadMoreAnimation(true); 324 | //触发上拉回调 325 | instance.options.up.callback && instance.options.up.callback(); 326 | } 327 | } 328 | } 329 | 330 | } 331 | }; 332 | document.onscroll = scrollFunc; 333 | } 334 | /** 335 | * @description 控制加载更多 336 | * @param {Boolean} more 337 | */ 338 | function loadMoreAnimation(more) { 339 | 340 | var dom = instance.elem; 341 | if (!dom) { 342 | return; 343 | } 344 | 345 | if (more) { 346 | if (!instance.loadingUp) { 347 | //显示loading 348 | var content = instance.options.up.contentrefresh || '正在加载...'; 349 | pulluploadingTips = pulluploadingTips.replace('{{contentrefresh}}', content); 350 | dom.appendChild(pareseStringToHtml(pulluploadingTips)); 351 | instance.loadingUp = true; 352 | } 353 | } else { 354 | if (instance.loadingUp) { 355 | //隐藏loading 356 | var loadingDom = dom.querySelector('.mui-pull-bottom-tips'); 357 | loadingDom && loadingDom.parentNode.removeChild(loadingDom); 358 | instance.loadingUp = false; 359 | } 360 | } 361 | } 362 | 363 | function initPullToRefresh(options) { 364 | if(instance) { 365 | return instance; 366 | } 367 | instance = new PullRefresh(options); 368 | 369 | if (options.up && options.up.auto) { 370 | // 如果设置了auto,则自动上拉一次 371 | instance.pullupLoading(); 372 | } 373 | 374 | return instance; 375 | } 376 | 377 | CommonTools.namespace('skin.natives', initPullToRefresh); 378 | 379 | })({}, PullToRefreshTools); -------------------------------------------------------------------------------- /src/pulltorefresh.skin.type1.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 作者: dailc 3 | * 创建时间: 2017/03/28 4 | * 版本: [1.0, 2017/05/26 ] 5 | * 版权: dailc 6 | * 描述: 皮肤类只会实现UI相关的hook函数 7 | * 默认皮肤,皮肤 type1 为了简化代码已经后续方便就没有再复用default了 8 | * 依赖mui的css 9 | */ 10 | (function(exports, CommonTools) { 11 | 12 | // 默认的全局参数-主要用来配置下拉刷新提示的一些css class 13 | var NAMESPACE = 'mui-'; 14 | var CLASS_PULL_TOP_POCKET = NAMESPACE + 'pull-top-pocket'; 15 | var CLASS_PULL_BOTTOM_POCKET = NAMESPACE + 'pull-bottom-pocket'; 16 | var CLASS_PULL = NAMESPACE + 'pull'; 17 | var CLASS_PULL_LOADING = NAMESPACE + 'pull-loading'; 18 | var CLASS_PULL_CAPTION = NAMESPACE + 'pull-caption'; 19 | var CLASS_PULL_CAPTION_DOWN = NAMESPACE + 'pull-caption-down'; 20 | var CLASS_PULL_CAPTION_REFRESH = NAMESPACE + 'pull-caption-refresh'; 21 | var CLASS_PULL_CAPTION_NOMORE = NAMESPACE + 'pull-caption-nomore'; 22 | 23 | var CLASS_ICON = NAMESPACE + 'icon'; 24 | var CLASS_SPINNER = NAMESPACE + 'spinner'; 25 | var CLASS_ICON_PULLDOWN = NAMESPACE + 'icon-pulldown'; 26 | var CLASS_ICON_SUCCESS = NAMESPACE + 'icon-checkmarkempty'; 27 | var CLASS_ICON_ERROR = NAMESPACE + 'icon-info'; 28 | 29 | var CLASS_BLOCK = NAMESPACE + 'block'; 30 | var CLASS_HIDDEN = NAMESPACE + 'hidden'; 31 | var CLASS_VISIBILITY = NAMESPACE + 'visibility'; 32 | 33 | var CLASS_LOADING_UP = CLASS_PULL_LOADING + ' ' + CLASS_ICON + ' ' + CLASS_ICON_PULLDOWN; 34 | var CLASS_LOADING_DOWN = CLASS_PULL_LOADING + ' ' + CLASS_ICON + ' ' + CLASS_ICON_PULLDOWN; 35 | var CLASS_LOADING = CLASS_PULL_LOADING + ' ' + CLASS_ICON + ' ' + CLASS_SPINNER; 36 | 37 | var CLASS_LOADING_SUCCESS = CLASS_PULL_LOADING + ' ' + CLASS_ICON + ' ' + CLASS_ICON_SUCCESS; 38 | var CLASS_LOADING_ERROR = CLASS_PULL_LOADING + ' ' + CLASS_ICON + ' ' + CLASS_ICON_ERROR; 39 | var pocketHtml = ['
', '
', '
{contentrefresh}
', '
'].join(''); 40 | 41 | 42 | var PullToRefresh = CommonTools.core.extend({ 43 | 44 | /** 45 | * @description 生成下拉刷新提示,这个需要被具体实现 46 | * 这个默认实现就直接在一个函数里面同时生成下拉和上拉提示了 47 | */ 48 | _initPullToRefreshTipsHook: function(enablePullDown, enablePullUp) { 49 | this._initPocket(); 50 | if(!enablePullUp) { 51 | this.bottomPocket && this.bottomPocket.classList.add(CLASS_HIDDEN); 52 | } 53 | if(!enablePullDown) { 54 | this.topPocket && this.topPocket.classList.add(CLASS_HIDDEN); 55 | } 56 | }, 57 | /** 58 | * @description 初始化下拉刷新 59 | */ 60 | _initPulldownRefreshState: function() { 61 | this.pullPocket = this.topPocket; 62 | this.pullPocket.classList.add(CLASS_BLOCK); 63 | this.pullPocket.classList.add(CLASS_VISIBILITY); 64 | this.pullCaption = this.topCaption; 65 | this.pullLoading = this.topLoading; 66 | 67 | }, 68 | /** 69 | * @description 初始化上拉加载 70 | */ 71 | _initPullupRefreshState: function() { 72 | this.pullPocket = this.bottomPocket; 73 | this.pullPocket.classList.add(CLASS_BLOCK); 74 | this.pullPocket.classList.add(CLASS_VISIBILITY); 75 | this.pullCaption = this.bottomCaption; 76 | this.pullLoading = this.bottomLoading; 77 | }, 78 | /** 79 | * @description 下拉过程中的钩子函数 80 | * @param {Number} deltaY 81 | * @param {Number} thresholdHeight 对应的高度阈值 82 | */ 83 | _pullingHook: function(deltaY, thresholdHeight) { 84 | // 高度阈值 85 | if(deltaY >= thresholdHeight) { 86 | this._setCaption(true, this.options.down.contentover); 87 | } else if(deltaY < thresholdHeight) { 88 | this._setCaption(true, this.options.down.contentdown); 89 | } 90 | }, 91 | /** 92 | * @description 下拉刷新的成功动画,每次确保触发一次 93 | */ 94 | _pulldownLoaingAnimationHook: function() { 95 | this._setCaption(true, this.options.down.contentrefresh); 96 | }, 97 | /** 98 | * @description 下拉刷新的成功动画-动画完毕后可能的成功提示,每次确保触发一次 99 | * 比如在成功里面提示加载了多少条数据,如果不需要可以传null,会直接走到end事件里 100 | * @param {Function} done 这个可以提前结束动画-如果不想要的话 101 | * @param {Boolean} isSuccess 是否请求成功 102 | */ 103 | _pulldownLoaingAnimationSuccessHook: function(done, isSuccess) { 104 | if(this.options.down.isSuccessTips) { 105 | this._setCaption(true, isSuccess ? this.options.down.contentrefreshsuccess : this.options.down.contentrefresherror); 106 | } else { 107 | // 否则直接没有成功提示 108 | done(); 109 | } 110 | 111 | }, 112 | /** 113 | * @description 下拉刷新的动画完成后的回调,可以用来重置状态 114 | */ 115 | _pulldownLoaingAnimationEndHook: function() { 116 | this._setCaption(true, this.options.down.contentdown, true); 117 | this.topPocket.classList.remove(CLASS_VISIBILITY); 118 | }, 119 | /** 120 | * @description 上拉加载的成功动画,每次确保触发一次 121 | */ 122 | _pullupLoaingAnimationHook: function(isFinished) { 123 | this._setCaption(false, this.options.up.contentrefresh); 124 | }, 125 | /** 126 | * @description 上拉加载的成功动画-动画完毕后可能的成功提示,每次确保触发一次 127 | */ 128 | _pullupLoaingAnimationSuccessHook: function(isFinished) { 129 | if(isFinished) { 130 | this._setCaption(false, this.options.up.contentnomore); 131 | } else { 132 | this._setCaption(false, this.options.up.contentdown); 133 | } 134 | // this.bottomPocket.classList.remove(CLASS_VISIBILITY); 135 | }, 136 | /** 137 | * @description _disablePullUpHook 138 | */ 139 | _disablePullUpHook: function() { 140 | this.bottomPocket.className = 'mui-pull-bottom-pocket' + ' ' + CLASS_HIDDEN; 141 | }, 142 | /** 143 | * @description disablePullUpHook 144 | */ 145 | _enablePullUpHook: function() { 146 | this.bottomPocket.classList.remove(CLASS_HIDDEN); 147 | this._setCaption(false, this.options.up.contentdown); 148 | }, 149 | /** 150 | * @description 创建上拉提示或下拉提示 151 | * @param {Object} clazz 152 | * @param {Object} options 153 | * @param {Object} iconClass 154 | */ 155 | _createPocket: function(clazz, options, iconClass) { 156 | var pocket = document.createElement('div'); 157 | pocket.className = clazz; 158 | pocket.innerHTML = pocketHtml.replace('{contentrefresh}', options.contentdown).replace('{icon}', iconClass); 159 | return pocket; 160 | }, 161 | /** 162 | * @description 初始化下拉刷新和上拉加载提示 163 | */ 164 | _initPocket: function() { 165 | var options = this.options; 166 | if(options.down && options.down.hasOwnProperty('callback')) { 167 | this.topPocket = this.wrapper.querySelector('.' + CLASS_PULL_TOP_POCKET); 168 | if(!this.topPocket) { 169 | this.topPocket = this._createPocket(CLASS_PULL_TOP_POCKET, options.down, CLASS_LOADING_DOWN); 170 | // this.wrapper.insertBefore(this.topPocket, this.wrapper.firstChild); 171 | this.scrollWrap.insertBefore(this.topPocket, this.scrollWrap.firstChild); 172 | } 173 | this.topLoading = this.topPocket.querySelector('.' + CLASS_PULL_LOADING); 174 | this.topCaption = this.topPocket.querySelector('.' + CLASS_PULL_CAPTION); 175 | // 这里为了方便,就不再单独引入样式文件了,而是直接通过style改写mui的样式 176 | // 将absulute改写为 relative visibility改为visible; 177 | this.topPocket.style.position = 'relative'; 178 | 179 | } 180 | if(options.up && options.up.hasOwnProperty('callback')) { 181 | this.bottomPocket = this.scrollWrap.querySelector('.' + CLASS_PULL_BOTTOM_POCKET); 182 | if(!this.bottomPocket) { 183 | this.bottomPocket = this._createPocket(CLASS_PULL_BOTTOM_POCKET, options.up, CLASS_LOADING); 184 | this.scrollWrap.appendChild(this.bottomPocket); 185 | } 186 | this.bottomLoading = this.bottomPocket.querySelector('.' + CLASS_PULL_LOADING); 187 | this.bottomCaption = this.bottomPocket.querySelector('.' + CLASS_PULL_CAPTION); 188 | } 189 | 190 | // 需要滑动到offset位置 191 | // 这个如果不设置,下拉的提示就会位置不正确 192 | // 需要设一个定时,否则可能计算失误,这里在返回到offset前就先隐藏了 193 | var self = this; 194 | setTimeout(function() { 195 | // 暂时写死一个,用offset有时会有失误 196 | // self.topPocket.offsetHeight||0 197 | self.topPocket && self._setOffsetY(50, function() { 198 | self.topPocket.style.visibility = 'visible'; 199 | self.bottomPocket && (self.bottomPocket.style.visibility = 'visible'); 200 | }); 201 | }, 0); 202 | }, 203 | 204 | /** 205 | * @description 设置提示的class 206 | * @param {Object} isPulldown 207 | * @param {Object} caption 208 | * @param {Object} title 209 | */ 210 | _setCaptionClass: function(isPulldown, caption, title) { 211 | if(!this.options.up) { 212 | return; 213 | } 214 | if(!isPulldown) { 215 | 216 | switch(title) { 217 | case this.options.up.contentdown: 218 | caption.className = CLASS_PULL_CAPTION + ' ' + CLASS_PULL_CAPTION_DOWN; 219 | break; 220 | case this.options.up.contentrefresh: 221 | caption.className = CLASS_PULL_CAPTION + ' ' + CLASS_PULL_CAPTION_REFRESH 222 | break; 223 | case this.options.up.contentnomore: 224 | caption.className = CLASS_PULL_CAPTION + ' ' + CLASS_PULL_CAPTION_NOMORE; 225 | break; 226 | } 227 | } 228 | }, 229 | /** 230 | * @description 设置caption 231 | * @param {Object} isPulldown 232 | * @param {Object} title 233 | * @param {Object} reset 234 | */ 235 | _setCaption: function(isPulldown, title, reset) { 236 | if(this.loading) { 237 | return; 238 | } 239 | if(isPulldown) { 240 | this._initPulldownRefreshState(); 241 | } else { 242 | this._initPullupRefreshState(); 243 | } 244 | var options = this.options; 245 | var pocket = this.pullPocket; 246 | var caption = this.pullCaption; 247 | var loading = this.pullLoading; 248 | var isPulldown = this.pulldown; 249 | var self = this; 250 | if(pocket) { 251 | if(reset) { 252 | setTimeout(function() { 253 | caption.innerHTML = self.lastTitle = title; 254 | if(isPulldown) { 255 | loading.className = CLASS_LOADING_DOWN; 256 | } else { 257 | self._setCaptionClass(false, caption, title); 258 | loading.className = CLASS_LOADING; 259 | } 260 | loading.style.webkitAnimation = ""; 261 | loading.style.webkitTransition = ""; 262 | loading.style.webkitTransform = ""; 263 | }, 100); 264 | } else { 265 | if(title !== this.lastTitle) { 266 | caption.innerHTML = title; 267 | if(isPulldown) { 268 | if(title === options.down.contentrefresh) { 269 | loading.className = CLASS_LOADING; 270 | loading.style.webkitAnimation = "spinner-spin 1s step-end infinite"; 271 | } else if(title === options.down.contentover) { 272 | loading.className = CLASS_LOADING_UP; 273 | loading.style.webkitTransition = "-webkit-transform 0.3s ease-in"; 274 | loading.style.webkitTransform = "rotate(180deg)"; 275 | } else if(title === options.down.contentdown) { 276 | loading.className = CLASS_LOADING_DOWN; 277 | loading.style.webkitTransition = "-webkit-transform 0.3s ease-in"; 278 | loading.style.webkitTransform = "rotate(0deg)"; 279 | } else if(title === options.down.contentrefreshsuccess) { 280 | // 隐藏loading先 281 | loading.className = CLASS_LOADING_SUCCESS; 282 | loading.style.webkitTransition = "-webkit-transform 0.3s ease-in"; 283 | loading.style.webkitTransform = "scale(1.2,1.2)"; 284 | loading.style.webkitAnimation = "none"; 285 | // 优先显示tips 286 | caption.innerHTML = self.successTips || title; 287 | } else if(title === options.down.contentrefresherror) { 288 | loading.className = CLASS_LOADING_ERROR; 289 | loading.style.webkitTransition = "-webkit-transform 0.3s ease-in"; 290 | loading.style.webkitTransform = "scale(1.2,1.2)"; 291 | loading.style.webkitAnimation = "none"; 292 | } 293 | } else { 294 | if(options.up) { 295 | if(title === options.up.contentrefresh) { 296 | loading.className = CLASS_LOADING + ' ' + CLASS_VISIBILITY; 297 | } else { 298 | loading.className = CLASS_LOADING + ' ' + CLASS_HIDDEN; 299 | } 300 | self._setCaptionClass(false, caption, title); 301 | } 302 | 303 | } 304 | this.lastTitle = title; 305 | } 306 | } 307 | 308 | } 309 | }, 310 | 311 | }); 312 | 313 | /** 314 | * @description 初始化下拉刷新组件 315 | * @param {JSON} options 传入的参数 316 | * @return 返回的是一个下拉刷新对象 317 | */ 318 | PullToRefresh.init = function(options) { 319 | return new PullToRefresh(options); 320 | }; 321 | 322 | CommonTools.namespace('skin.type1', PullToRefresh); 323 | 324 | })({}, PullToRefreshTools); -------------------------------------------------------------------------------- /src/pulltorefresh.skin.type2.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 作者: dailc 3 | * 创建时间: 2017/03/28 4 | * 版本: [1.0, 2017/05/26 ] 5 | * 版权: dailc 6 | * 描述: 皮肤类只会实现UI相关的hook函数 7 | * 皮肤 type2 ,不再基于mui的css,而是有自己的资源引用 8 | * 依赖 pulltorefresh.skin.css 9 | */ 10 | (function(exports, CommonTools) { 11 | 12 | /** 13 | * 全局参数 14 | */ 15 | var CLASS_HIDDEN = 'hidden'; 16 | 17 | var PullToRefresh = CommonTools.core.extend({ 18 | 19 | /** 20 | * @description 生成下拉刷新提示,这个需要被具体实现 21 | * 这个默认实现就直接在一个函数里面同时生成下拉和上拉提示了 22 | */ 23 | _initPullToRefreshTipsHook: function(enablePullDown, enablePullUp) { 24 | this._initPocket(); 25 | this._checkHidden(enablePullDown, enablePullUp); 26 | }, 27 | 28 | _checkHidden: function(enablePullDown, enablePullUp) { 29 | if(!enablePullUp) { 30 | this.bottomPocket && this.bottomPocket.classList.add(CLASS_HIDDEN); 31 | } 32 | if(!enablePullDown) { 33 | this.topPocket && this.topPocket.classList.add(CLASS_HIDDEN); 34 | } 35 | }, 36 | 37 | /** 38 | * @description 下拉过程中的钩子函数 39 | * @param {Number} deltaY 40 | * @param {Number} thresholdHeight 对应的高度阈值 41 | */ 42 | _pullingHook: function(deltaY, thresholdHeight) { 43 | // 高度阈值 44 | if(deltaY >= thresholdHeight) { 45 | this._setCaption(true, this.options.down.contentover); 46 | } else if(deltaY < thresholdHeight) { 47 | this._setCaption(true, this.options.down.contentdown); 48 | } 49 | }, 50 | /** 51 | * @description 下拉刷新的成功动画,每次确保触发一次 52 | */ 53 | _pulldownLoaingAnimationHook: function() { 54 | this._setCaption(true, this.options.down.contentrefresh); 55 | }, 56 | /** 57 | * @description 下拉刷新的成功动画-动画完毕后可能的成功提示,每次确保触发一次 58 | * 比如在成功里面提示加载了多少条数据,如果不需要可以传null,会直接走到end事件里 59 | * @param {Function} done 这个可以提前结束动画-如果不想要的话 60 | * @param {Boolean} isSuccess 是否请求成功 61 | */ 62 | _pulldownLoaingAnimationSuccessHook: function(done, isSuccess) { 63 | if(this.options.down.isSuccessTips) { 64 | this._setCaption(true, isSuccess ? this.options.down.contentrefreshsuccess : this.options.down.contentrefresherror); 65 | } else { 66 | // 否则直接没有成功提示 67 | done(); 68 | } 69 | 70 | }, 71 | /** 72 | * @description 下拉刷新的动画完成后的回调,可以用来重置状态 73 | */ 74 | _pulldownLoaingAnimationEndHook: function() { 75 | this._setCaption(true, this.options.down.contentdown, true); 76 | 77 | }, 78 | /** 79 | * @description 上拉加载的成功动画,每次确保触发一次 80 | */ 81 | _pullupLoaingAnimationHook: function(isFinished) { 82 | this._setCaption(false, this.options.up.contentrefresh); 83 | }, 84 | /** 85 | * @description 上拉加载的成功动画-动画完毕后可能的成功提示,每次确保触发一次 86 | */ 87 | _pullupLoaingAnimationSuccessHook: function(isFinished) { 88 | if(isFinished) { 89 | this._setCaption(false, this.options.up.contentnomore); 90 | } else { 91 | this._setCaption(false, this.options.up.contentdown); 92 | } 93 | // this.bottomPocket.classList.remove(CLASS_VISIBILITY); 94 | }, 95 | /** 96 | * @description _disablePullUpHook 97 | */ 98 | _disablePullUpHook: function() { 99 | this.bottomPocket.className = 'pull-bottom-pocket' + ' ' + CLASS_HIDDEN; 100 | }, 101 | /** 102 | * @description disablePullUpHook 103 | */ 104 | _enablePullUpHook: function() { 105 | this.bottomPocket.classList.remove(CLASS_HIDDEN); 106 | this._setCaption(false, this.options.up.contentdown); 107 | }, 108 | /** 109 | * @description 创建下拉提示 110 | */ 111 | _createTopPocket: function() { 112 | var pocket = document.createElement('div'); 113 | pocket.style.visibility = 'hidden'; 114 | pocket.className = 'pull-top-pocket'; 115 | pocket.innerHTML = '
' + this.options.down.contentdown + '
'; 116 | return pocket; 117 | }, 118 | /** 119 | * @description 创建上拉提示 120 | */ 121 | _createBottomPocket: function() { 122 | var pocket = document.createElement('div'); 123 | pocket.style.visibility = 'hidden'; 124 | pocket.className = 'pull-bottom-pocket'; 125 | pocket.innerHTML = '
' + this.options.up.contentdown + '
'; 126 | return pocket; 127 | }, 128 | /** 129 | * @description 初始化下拉刷新和上拉加载提示 130 | */ 131 | _initPocket: function() { 132 | // 先改变wrap的皮肤 133 | this.wrapper.classList.add('pulltorefresh-type2'); 134 | var options = this.options; 135 | if(options.down && options.down.hasOwnProperty('callback')) { 136 | if(!this.topPocket) { 137 | this.topPocket = this._createTopPocket(); 138 | 139 | this.scrollWrap.insertBefore(this.topPocket, this.scrollWrap.firstChild); 140 | } 141 | } 142 | if(options.up && options.up.hasOwnProperty('callback')) { 143 | if(!this.bottomPocket) { 144 | this.bottomPocket = this._createBottomPocket(); 145 | this.scrollWrap.appendChild(this.bottomPocket); 146 | } 147 | } 148 | // 需要滑动到offset位置 149 | // 这个如果不设置,下拉的提示就会位置不正确 150 | // 需要设一个定时,否则可能计算失误,这里在返回到offset前就先隐藏了 151 | var self = this; 152 | setTimeout(function() { 153 | // 暂时写死一个,用offset有时会有失误 154 | // self.topPocket.offsetHeight||0 155 | self.topPocket && self._setOffsetY(74, function() { 156 | self.topPocket.style.visibility = 'visible'; 157 | self.bottomPocket && (self.bottomPocket.style.visibility = 'visible'); 158 | }); 159 | }, 0); 160 | }, 161 | 162 | /** 163 | * @description 设置caption 164 | * @param {Object} isPulldown 165 | * @param {Object} title 166 | * @param {Object} reset 167 | */ 168 | _setCaption: function(isPulldown, title, reset) { 169 | if(this.loading) { 170 | return; 171 | } 172 | var pocket; 173 | if(isPulldown) { 174 | pocket = this.topPocket; 175 | 176 | } else { 177 | pocket = this.bottomPocket; 178 | } 179 | var label = pocket.querySelector('.pull-caption'); 180 | var options = this.options; 181 | var self = this; 182 | if(pocket) { 183 | if(reset) { 184 | setTimeout(function() { 185 | pocket.className = 'pull-top-pocket '; 186 | label.innerHTML = options.down.contentdown; 187 | }, 100); 188 | } else { 189 | if(title !== this.lastTitle) { 190 | label.innerHTML = title; 191 | if(isPulldown) { 192 | if(title === options.down.contentrefresh) { 193 | pocket.className = 'pull-top-pocket loading'; 194 | 195 | } else if(title === options.down.contentover) { 196 | pocket.className = 'pull-top-pocket flip'; 197 | 198 | } else if(title === options.down.contentdown) { 199 | pocket.className = 'pull-top-pocket '; 200 | } else if(title === options.down.contentrefreshsuccess) { 201 | // 优先显示tips 202 | label.innerHTML = self.successTips || title; 203 | pocket.className = 'pull-top-pocket success'; 204 | } else if(title === options.down.contentrefresherror) { 205 | pocket.className = 'pull-top-pocket error'; 206 | } 207 | } else { 208 | if(options.up) { 209 | if(title === options.up.contentrefresh) { 210 | pocket.classList.remove('nomore'); 211 | pocket.classList.add('loading'); 212 | } else { 213 | pocket.classList.remove('loading'); 214 | 215 | if(title === options.up.contentnomore) { 216 | pocket.classList.add('nomore'); 217 | } else { 218 | pocket.classList.remove('nomore'); 219 | } 220 | 221 | } 222 | 223 | } 224 | 225 | } 226 | this.lastTitle = title; 227 | } 228 | } 229 | 230 | } 231 | }, 232 | 233 | }); 234 | 235 | /** 236 | * @description 初始化下拉刷新组件 237 | * @param {JSON} options 传入的参数 238 | * @return 返回的是一个下拉刷新对象 239 | */ 240 | PullToRefresh.init = function(options) { 241 | return new PullToRefresh(options); 242 | }; 243 | 244 | 245 | CommonTools.namespace('skin.type2', PullToRefresh); 246 | 247 | })({}, PullToRefreshTools); -------------------------------------------------------------------------------- /src/pulltorefresh.skin.type5.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 作者: dailc 3 | * 创建时间: 2017/08/14 4 | * 版本: [1.0, 2017/08/14 ] 5 | * 版权: dailc 6 | * 描述: 皮肤类只会实现UI相关的hook函数 7 | * 皮肤 type5 8 | * 仿微信小程序效果 9 | * 10 | */ 11 | (function(CommonTools) { 12 | 13 | /** 14 | * 全局参数 15 | */ 16 | var CLASS_HIDDEN = 'hidden'; 17 | 18 | var PullToRefresh = CommonTools.core.extend({ 19 | 20 | /** 21 | * @description 生成下拉刷新提示,这个需要被具体实现 22 | * 这个默认实现就直接在一个函数里面同时生成下拉和上拉提示了 23 | */ 24 | _initPullToRefreshTipsHook: function(enablePullDown, enablePullUp) { 25 | this._initPocket(); 26 | this._checkHidden(enablePullDown, enablePullUp); 27 | }, 28 | 29 | _checkHidden: function(enablePullDown, enablePullUp) { 30 | if (!enablePullUp) { 31 | this.bottomPocket && this.bottomPocket.classList.add(CLASS_HIDDEN); 32 | } 33 | if (!enablePullDown) { 34 | this.topPocket && this.topPocket.classList.add(CLASS_HIDDEN); 35 | } 36 | }, 37 | 38 | /** 39 | * @description 下拉过程中的钩子函数 40 | * @param {Number} deltaY 41 | * @param {Number} thresholdHeight 对应的高度阈值 42 | */ 43 | _pullingHook: function(deltaY, thresholdHeight) { 44 | if ((this.options.down && this.options.down.cssAnimation)) { 45 | // 高度阈值,暂时不做其他事情 46 | if (deltaY <= thresholdHeight) { 47 | this.topLoading.style.webkitTransform = 'translateY(' + (-50 + deltaY / thresholdHeight * 50) + 'px)'; 48 | this.topLoading.style.transform = 'translateY(' + (-50 + deltaY / thresholdHeight * 50) + 'px)'; 49 | } else { 50 | this.topLoading.style.webkitTransform = 'translateY(0)'; 51 | this.topLoading.style.transform = 'translateY(0)'; 52 | } 53 | } 54 | 55 | }, 56 | /** 57 | * @description 下拉刷新的成功动画,每次确保触发一次 58 | */ 59 | _pulldownLoaingAnimationHook: function() { 60 | // 添加loading class 61 | this.topPocket.classList.add('loading'); 62 | }, 63 | /** 64 | * @description 下拉刷新的成功动画-动画完毕后可能的成功提示,每次确保触发一次 65 | * 比如在成功里面提示加载了多少条数据,如果不需要可以传null,会直接走到end事件里 66 | * @param {Function} done 这个可以提前结束动画-如果不想要的话 67 | * @param {Boolean} isSuccess 是否请求成功 68 | */ 69 | _pulldownLoaingAnimationSuccessHook: function(done, isSuccess) { 70 | // 此皮肤没有成功提示 71 | done(); 72 | }, 73 | /** 74 | * @description 下拉刷新的动画完成后的回调,可以用来重置状态 75 | */ 76 | _pulldownLoaingAnimationEndHook: function() { 77 | // 移除loading class 78 | this.topPocket.classList.remove('loading'); 79 | if (this.options.down && this.options.down.cssAnimation) { 80 | this.topLoading.style.webkitTransform = 'translateY(-50px)'; 81 | this.topLoading.style.transform = 'translateY(-50px)'; 82 | } 83 | 84 | }, 85 | /** 86 | * @description 上拉加载的成功动画,每次确保触发一次 87 | */ 88 | _pullupLoaingAnimationHook: function(isFinished) { 89 | this._setCaption(false, this.options.up.contentrefresh); 90 | }, 91 | /** 92 | * @description 上拉加载的成功动画-动画完毕后可能的成功提示,每次确保触发一次 93 | */ 94 | _pullupLoaingAnimationSuccessHook: function(isFinished) { 95 | if (isFinished) { 96 | this._setCaption(false, this.options.up.contentnomore); 97 | } else { 98 | this._setCaption(false, this.options.up.contentdown); 99 | } 100 | // this.bottomPocket.classList.remove(CLASS_VISIBILITY); 101 | }, 102 | /** 103 | * @description _disablePullUpHook 104 | */ 105 | _disablePullUpHook: function() { 106 | this.bottomPocket.className = 'pull-bottom-pocket' + ' ' + CLASS_HIDDEN; 107 | }, 108 | /** 109 | * @description disablePullUpHook 110 | */ 111 | _enablePullUpHook: function() { 112 | this.bottomPocket.classList.remove(CLASS_HIDDEN); 113 | this._setCaption(false, this.options.up.contentdown); 114 | }, 115 | /** 116 | * @description 创建下拉提示 117 | */ 118 | _createTopPocket: function() { 119 | var pocket = document.createElement('div'); 120 | //pocket.style.visibility = 'hidden'; 121 | pocket.className = 'pull-top-pocket'; 122 | pocket.innerHTML = '
'; 123 | return pocket; 124 | }, 125 | /** 126 | * @description 创建上拉提示 127 | */ 128 | _createBottomPocket: function() { 129 | var pocket = document.createElement('div'); 130 | //pocket.style.visibility = 'hidden'; 131 | pocket.className = 'pull-bottom-pocket'; 132 | pocket.innerHTML = '
' + this.options.up.contentdown + '
'; 133 | return pocket; 134 | }, 135 | /** 136 | * @description 初始化下拉刷新和上拉加载提示 137 | */ 138 | _initPocket: function() { 139 | // 改变配置 140 | this.options.down.cssAnimation = true; 141 | // 先改变wrap的皮肤 142 | this.wrapper.classList.add('pulltorefresh-type5'); 143 | var options = this.options; 144 | if (options.down && options.down.hasOwnProperty('callback')) { 145 | if (!this.topPocket) { 146 | this.topPocket = this._createTopPocket(); 147 | 148 | // 插到wrap中 149 | this.wrapper.insertBefore(this.topPocket, this.wrapper.firstChild); 150 | } 151 | 152 | this.topLoading = this.topPocket.querySelector('.pull-block'); 153 | 154 | if (this.options.down && this.options.down.cssAnimation) { 155 | // 单独渲染图层,优化动画 156 | this.topPocket.style.webkitTransform = 'translateZ(0)'; 157 | this.topPocket.style.transform = 'translateZ(0)'; 158 | this.topLoading.style.webkitTransform = 'translateY(-50px)'; 159 | this.topLoading.style.transform = 'translateY(-50px)'; 160 | } 161 | 162 | } 163 | if (options.up && options.up.hasOwnProperty('callback')) { 164 | if (!this.bottomPocket) { 165 | this.bottomPocket = this._createBottomPocket(); 166 | this.scrollWrap.appendChild(this.bottomPocket); 167 | } 168 | } 169 | }, 170 | 171 | /** 172 | * @description 设置caption 173 | * @param {Object} isPulldown 174 | * @param {Object} title 175 | * @param {Object} reset 176 | */ 177 | _setCaption: function(isPulldown, title, reset) { 178 | if (this.loading) { 179 | return; 180 | } 181 | var pocket; 182 | if (isPulldown) { 183 | // 本皮肤上拉没有文字提示 184 | return; 185 | 186 | } else { 187 | pocket = this.bottomPocket; 188 | } 189 | var label = pocket.querySelector('.pull-caption'); 190 | var options = this.options; 191 | var self = this; 192 | if (pocket) { 193 | if (title !== this.lastTitle) { 194 | label.innerHTML = title; 195 | if (options.up) { 196 | if (title === options.up.contentrefresh) { 197 | pocket.classList.remove('nomore'); 198 | pocket.classList.add('loading'); 199 | } else { 200 | pocket.classList.remove('loading'); 201 | 202 | if (title === options.up.contentnomore) { 203 | pocket.classList.add('nomore'); 204 | } else { 205 | pocket.classList.remove('nomore'); 206 | } 207 | 208 | } 209 | 210 | } 211 | this.lastTitle = title; 212 | } 213 | } 214 | }, 215 | 216 | }); 217 | 218 | CommonTools.namespace('skin.type5', PullToRefresh); 219 | 220 | })(PullToRefreshTools); -------------------------------------------------------------------------------- /staticresource/img/effect1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dailc/pulltorefresh-h5-iscroll/f221a5e60e6f6c1c955e99f3ea59c1f373ff08ae/staticresource/img/effect1.gif -------------------------------------------------------------------------------- /staticresource/img/effect2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dailc/pulltorefresh-h5-iscroll/f221a5e60e6f6c1c955e99f3ea59c1f373ff08ae/staticresource/img/effect2.gif -------------------------------------------------------------------------------- /staticresource/img/effect3.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dailc/pulltorefresh-h5-iscroll/f221a5e60e6f6c1c955e99f3ea59c1f373ff08ae/staticresource/img/effect3.gif -------------------------------------------------------------------------------- /staticresource/img/effect4.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dailc/pulltorefresh-h5-iscroll/f221a5e60e6f6c1c955e99f3ea59c1f373ff08ae/staticresource/img/effect4.gif -------------------------------------------------------------------------------- /staticresource/img/effect5.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dailc/pulltorefresh-h5-iscroll/f221a5e60e6f6c1c955e99f3ea59c1f373ff08ae/staticresource/img/effect5.gif -------------------------------------------------------------------------------- /staticresource/img/effect6.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dailc/pulltorefresh-h5-iscroll/f221a5e60e6f6c1c955e99f3ea59c1f373ff08ae/staticresource/img/effect6.gif --------------------------------------------------------------------------------