dropload.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313
  1. /**
  2. * dropload
  3. * 西门(http://ons.me/526.html)
  4. * 0.9.1(161205)
  5. */
  6. ;(function($){
  7. 'use strict';
  8. var win = window;
  9. var doc = document;
  10. var $win = $(win);
  11. var $doc = $(doc);
  12. $.fn.dropload = function(options){
  13. return new MyDropLoad(this, options);
  14. };
  15. var MyDropLoad = function(element, options){
  16. var me = this;
  17. me.$element = element;
  18. // 上方是否插入DOM
  19. me.upInsertDOM = false;
  20. // loading状态
  21. me.loading = false;
  22. // 是否锁定
  23. me.isLockUp = false;
  24. me.isLockDown = false;
  25. // 是否有数据
  26. me.isData = true;
  27. me._scrollTop = 0;
  28. me._threshold = 0;
  29. me.init(options);
  30. };
  31. // 初始化
  32. MyDropLoad.prototype.init = function(options){
  33. var me = this;
  34. me.opts = $.extend(true, {}, {
  35. scrollArea : me.$element, // 滑动区域
  36. domUp : { // 上方DOM
  37. domClass : 'dropload-up',
  38. domRefresh : '<div class="dropload-refresh">↓下拉刷新</div>',
  39. domUpdate : '<div class="dropload-update">↑释放更新</div>',
  40. domLoad : '<div class="dropload-load"><span class="loading"></span>加载中...</div>'
  41. },
  42. domDown : { // 下方DOM
  43. domClass : 'dropload-down',
  44. domRefresh : '<div class="dropload-refresh">↑上拉加载更多</div>',
  45. domLoad : '<div class="dropload-load"><span class="loading"></span>加载中...</div>',
  46. domNoData : '<div class="dropload-noData">暂无数据</div>'
  47. },
  48. autoLoad : true, // 自动加载
  49. distance : 50, // 拉动距离
  50. threshold : '', // 提前加载距离
  51. loadUpFn : '', // 上方function
  52. loadDownFn : '' // 下方function
  53. }, options);
  54. // 如果加载下方,事先在下方插入DOM
  55. if(me.opts.loadDownFn != ''){
  56. me.$element.append('<div class="'+me.opts.domDown.domClass+'">'+me.opts.domDown.domRefresh+'</div>');
  57. me.$domDown = $('.'+me.opts.domDown.domClass);
  58. }
  59. // 计算提前加载距离
  60. if(!!me.$domDown && me.opts.threshold === ''){
  61. // 默认滑到加载区2/3处时加载
  62. me._threshold = Math.floor(me.$domDown.height()*1/3);
  63. }else{
  64. me._threshold = me.opts.threshold;
  65. }
  66. // 判断滚动区域
  67. if(me.opts.scrollArea == win){
  68. me.$scrollArea = $win;
  69. // 获取文档高度
  70. me._scrollContentHeight = $doc.height();
  71. // 获取win显示区高度 —— 这里有坑
  72. me._scrollWindowHeight = doc.documentElement.clientHeight;
  73. }else{
  74. me.$scrollArea = me.opts.scrollArea;
  75. me._scrollContentHeight = me.$element[0].scrollHeight;
  76. me._scrollWindowHeight = me.$element.height();
  77. }
  78. fnAutoLoad(me);
  79. // 窗口调整
  80. $win.on('resize',function(){
  81. clearTimeout(me.timer);
  82. me.timer = setTimeout(function(){
  83. if(me.opts.scrollArea == win){
  84. // 重新获取win显示区高度
  85. me._scrollWindowHeight = win.innerHeight;
  86. }else{
  87. me._scrollWindowHeight = me.$element.height();
  88. }
  89. fnAutoLoad(me);
  90. },150);
  91. });
  92. // 绑定触摸
  93. me.$element.on('touchstart',function(e){
  94. if(!me.loading){
  95. fnTouches(e);
  96. fnTouchstart(e, me);
  97. }
  98. });
  99. me.$element.on('touchmove',function(e){
  100. if(!me.loading){
  101. fnTouches(e, me);
  102. fnTouchmove(e, me);
  103. }
  104. });
  105. me.$element.on('touchend',function(){
  106. if(!me.loading){
  107. fnTouchend(me);
  108. }
  109. });
  110. // 加载下方
  111. me.$scrollArea.on('scroll',function(){
  112. me._scrollTop = me.$scrollArea.scrollTop();
  113. // 滚动页面触发加载数据
  114. if(me.opts.loadDownFn != '' && !me.loading && !me.isLockDown && (me._scrollContentHeight - me._threshold) <= (me._scrollWindowHeight + me._scrollTop)){
  115. loadDown(me);
  116. }
  117. });
  118. };
  119. // touches
  120. function fnTouches(e){
  121. if(!e.touches){
  122. e.touches = e.originalEvent.touches;
  123. }
  124. }
  125. // touchstart
  126. function fnTouchstart(e, me){
  127. me._startY = e.touches[0].pageY;
  128. // 记住触摸时的scrolltop值
  129. me.touchScrollTop = me.$scrollArea.scrollTop();
  130. }
  131. // touchmove
  132. function fnTouchmove(e, me){
  133. me._curY = e.touches[0].pageY;
  134. me._moveY = me._curY - me._startY;
  135. if(me._moveY > 0){
  136. me.direction = 'down';
  137. }else if(me._moveY < 0){
  138. me.direction = 'up';
  139. }
  140. var _absMoveY = Math.abs(me._moveY);
  141. // 加载上方
  142. if(me.opts.loadUpFn != '' && me.touchScrollTop <= 0 && me.direction == 'down' && !me.isLockUp){
  143. e.preventDefault();
  144. me.$domUp = $('.'+me.opts.domUp.domClass);
  145. // 如果加载区没有DOM
  146. if(!me.upInsertDOM){
  147. me.$element.prepend('<div class="'+me.opts.domUp.domClass+'"></div>');
  148. me.upInsertDOM = true;
  149. }
  150. fnTransition(me.$domUp,0);
  151. // 下拉
  152. if(_absMoveY <= me.opts.distance){
  153. me._offsetY = _absMoveY;
  154. // todo:move时会不断清空、增加dom,有可能影响性能,下同
  155. me.$domUp.html(me.opts.domUp.domRefresh);
  156. // 指定距离 < 下拉距离 < 指定距离*2
  157. }else if(_absMoveY > me.opts.distance && _absMoveY <= me.opts.distance*2){
  158. me._offsetY = me.opts.distance+(_absMoveY-me.opts.distance)*0.5;
  159. me.$domUp.html(me.opts.domUp.domUpdate);
  160. // 下拉距离 > 指定距离*2
  161. }else{
  162. me._offsetY = me.opts.distance+me.opts.distance*0.5+(_absMoveY-me.opts.distance*2)*0.2;
  163. }
  164. me.$domUp.css({'height': me._offsetY});
  165. }
  166. }
  167. // touchend
  168. function fnTouchend(me){
  169. var _absMoveY = Math.abs(me._moveY);
  170. if(me.opts.loadUpFn != '' && me.touchScrollTop <= 0 && me.direction == 'down' && !me.isLockUp){
  171. fnTransition(me.$domUp,300);
  172. if(_absMoveY > me.opts.distance){
  173. me.$domUp.css({'height':me.$domUp.children().height()});
  174. me.$domUp.html(me.opts.domUp.domLoad);
  175. me.loading = true;
  176. me.opts.loadUpFn(me);
  177. }else{
  178. me.$domUp.css({'height':'0'}).on('webkitTransitionEnd mozTransitionEnd transitionend',function(){
  179. me.upInsertDOM = false;
  180. $(this).remove();
  181. });
  182. }
  183. me._moveY = 0;
  184. }
  185. }
  186. // 如果文档高度不大于窗口高度,数据较少,自动加载下方数据
  187. function fnAutoLoad(me){
  188. if(me.opts.loadDownFn != '' && me.opts.autoLoad){
  189. if((me._scrollContentHeight - me._threshold) <= me._scrollWindowHeight){
  190. loadDown(me);
  191. }
  192. }
  193. }
  194. // 重新获取文档高度
  195. function fnRecoverContentHeight(me){
  196. if(me.opts.scrollArea == win){
  197. me._scrollContentHeight = $doc.height();
  198. }else{
  199. me._scrollContentHeight = me.$element[0].scrollHeight;
  200. }
  201. }
  202. // 加载下方
  203. function loadDown(me){
  204. me.direction = 'up';
  205. me.$domDown.html(me.opts.domDown.domLoad);
  206. me.loading = true;
  207. me.opts.loadDownFn(me);
  208. }
  209. // 锁定
  210. MyDropLoad.prototype.lock = function(direction){
  211. var me = this;
  212. // 如果不指定方向
  213. if(direction === undefined){
  214. // 如果操作方向向上
  215. if(me.direction == 'up'){
  216. me.isLockDown = true;
  217. // 如果操作方向向下
  218. }else if(me.direction == 'down'){
  219. me.isLockUp = true;
  220. }else{
  221. me.isLockUp = true;
  222. me.isLockDown = true;
  223. }
  224. // 如果指定锁上方
  225. }else if(direction == 'up'){
  226. me.isLockUp = true;
  227. // 如果指定锁下方
  228. }else if(direction == 'down'){
  229. me.isLockDown = true;
  230. // 为了解决DEMO5中tab效果bug,因为滑动到下面,再滑上去点tab,direction=down,所以有bug
  231. me.direction = 'up';
  232. }
  233. };
  234. // 解锁
  235. MyDropLoad.prototype.unlock = function(){
  236. var me = this;
  237. // 简单粗暴解锁
  238. me.isLockUp = false;
  239. me.isLockDown = false;
  240. // 为了解决DEMO5中tab效果bug,因为滑动到下面,再滑上去点tab,direction=down,所以有bug
  241. me.direction = 'up';
  242. };
  243. // 无数据
  244. MyDropLoad.prototype.noData = function(flag){
  245. var me = this;
  246. if(flag === undefined || flag == true){
  247. me.isData = false;
  248. }else if(flag == false){
  249. me.isData = true;
  250. }
  251. };
  252. // 重置
  253. MyDropLoad.prototype.resetload = function(){
  254. var me = this;
  255. if(me.direction == 'down' && me.upInsertDOM){
  256. me.$domUp.css({'height':'0'}).on('webkitTransitionEnd mozTransitionEnd transitionend',function(){
  257. me.loading = false;
  258. me.upInsertDOM = false;
  259. $(this).remove();
  260. fnRecoverContentHeight(me);
  261. });
  262. }else if(me.direction == 'up'){
  263. me.loading = false;
  264. // 如果有数据
  265. if(me.isData){
  266. // 加载区修改样式
  267. me.$domDown.html(me.opts.domDown.domRefresh);
  268. fnRecoverContentHeight(me);
  269. fnAutoLoad(me);
  270. }else{
  271. // 如果没数据
  272. me.$domDown.html(me.opts.domDown.domNoData);
  273. }
  274. }
  275. };
  276. // css过渡
  277. function fnTransition(dom,num){
  278. dom.css({
  279. '-webkit-transition':'all '+num+'ms',
  280. 'transition':'all '+num+'ms'
  281. });
  282. }
  283. })(window.Zepto || window.jQuery);