JS+CSS实现下拉刷新/上拉加载插件
闲来无事,写了一个当下比较常见的下拉刷新/上拉加载的jquery插件,代码记录在这里,有兴趣将代码写成插件与npm包可以留言。
体验地址:http://owenliang.github.io/pulltorefresh/
项目地址:https://github.com/owenliang/pulltorefresh
实现注意:
利用transition做动画时,优先使用transform:translate取代top,后者动画流畅度存在问题。
各移动浏览器对手势触摸的处理不同(简单罗列如下),但是下面的应对方案又会导致部分浏览器的overflow:scroll失效,总之难以兼容:
微信浏览器下拉自带回弹动画:可以禁止document的touchmove事件默认处理行为。
谷歌浏览器下拉自带刷新功能:利用属性touch-action: none可以禁掉。
针对上述问题,我的建议是滚动一律用iscroll5插件模拟实现(非overflow:scroll),然后利用上面的方法禁掉浏览器的默认touchmove行为。
transition如果有多个属性,那么transitionend回调会为每个属性回调一次,因此遇到其中任意一个回调就应该把css和transitionend回调都删除掉。
浏览器在执行js代码时没有机会重绘ui,因此在使用transition的时候一定要注意把修改动画终止css的代码通过settimeout延迟一会执行。
贴代码上首页,欢迎留言交流,需一位有兴趣有时间的朋友合作,主要做2件事:
1)插件改为npm包。
2)基于pulltorefresh库,开发类似"今日头条"的左右滑动ui。
pulltorefresh.js:
/** * 为指定的容器添加滚动条,支持下拉刷新与上拉加载功能 * @param container 需要滚动的容器,要求设置css: position!=static,height= * @param option 配置项,详见下方defaultoption说明 * @return 返回对象用于操控此区域,当前暴露了iscroll的refresh函数,当你在插件之外向滚动区域增加/删除内容后应该主动调用一次 * @description * * 2017-03-29 * 1)支持上拉加载 * 2017-03-30 * 1)改为jquery静态函数插件 * 2)支持关闭下拉刷新或上拉加载 */ $.installpulltorefresh = function (container, option) { // 起始触摸位置 var touchstarty = 0; // 起始图标位置 var pullstarty = 0; // 当前的触摸事件 var touchevent = null; // 当前的刷新事件 var refreshevent = null; // 当前图标位置 var cury = -55; // 当前的加载事件 var loadevent = null; // 默认参数 var defaultoption = { // 刷新相关 norefresh: false, // 关闭下拉刷新特性 pausebound: 40, // 触发刷新的位置(也是图标loading暂停的位置) lowerbound: 80, // 最大下拉到多少px loadimg: "load.png", // loading图片 pullimg: "pull.png", // 下拉图片 onrefresh: function (refreshdone) { // 刷新数据回调 settimeout(function() { // 默认不做任何事 refreshdone(); }, 0); }, // 加载相关 noload: false, // 关闭上拉加载特性 bottomheight: 1, // 距离滚动条底部多少px发起刷新 onload: function (loaddone) { settimeout(function() { loaddone(); }, 0); }, }; var finaloption = $.extend(true, defaultoption, option); // 创建iscroll5滚动区域 var iscroll = new iscroll(container, { bounce: false, }); // 关闭上拉加载特性 if (!finaloption.noload) { // 监听滚动结束事件,用于上拉加载 iscroll.on('scrollend', function () { // 有滚动条的情况下,才允许上拉加载 if (iscroll.maxscrolly < 0) { // maxscrolly<0表明出现了滚动条 var bottomdistance = (iscroll.maxscrolly - iscroll.y) * -1; // 距离底部足够近,触发加载 if (bottomdistance <= finaloption.bottomheight) { // 当前没有刷新和加载事件正在执行 if (!loadevent && !refreshevent) { loadevent = {}; // 生成新的加载事件 finaloption.onload(function (error, msg) { loadevent = null; // 清理当前的加载事件 // 延迟重绘滚动条 settimeout(function () { iscroll.refresh(); }, 0); }); } } } }); } // 关闭下拉刷新特性 if (!finaloption.norefresh) { // 紧邻滚动区域,容纳刷新图标 var pullcontainer = $('<div class="pullcontainer"></div>') // 创建小图标 var pulltorefresh = $('<div class="pulltorefresh"><img src="' + finaloption.pullimg + '"></div>'); // 保留小图标的快捷方式 var pullimg = pulltorefresh.find("img"); // 小图标加入到容器 pullcontainer.append(pulltorefresh); // 小图标容器添加到滚动区域之前 $(container).before(pullcontainer); // 预加载loadimg $('<img src="' + finaloption.loadimg + '">'); // 设置transform的函数 function csstransform(node, content) { node.css({ '-webkit-transform' : content, '-moz-transform' : content, '-ms-transform' : content, '-o-transform' : content, 'transform' : content, }); } // 调整小图标位置,角度,透明度 function gotowards(translatey, rotate, opcaticy) { // 更新当前小图标的位置,获取css(transform)比较麻烦,所以每次变更时自己保存 cury = translatey; // 旋转图标(根据抵达lowerbound的比例旋转,最大转1圈) if (rotate === undefined) { rotate = (cury / finaloption.lowerbound) * 360; } // 透明度根据抵达pausebound的比例计算 if (opcaticy === undefined) { opcaticy = (cury / finaloption.pausebound) * 1; if (opcaticy > 1) { opcaticy = 1; } } // 改变位置和旋转角度 csstransform(pulltorefresh, "translatey(" + translatey + "px) translatez(0)" + "rotatez(" + rotate + "deg)"); // 改变透明度 pulltorefresh.css("opacity", opcaticy); } // 开启回弹动画 function trystartbacktrantop() { // 启动回弹动画 pulltorefresh.addclass("backtrantop"); // 判断是否触发刷新 if (cury >= finaloption.pausebound) { gotowards(finaloption.pausebound); // 回弹动画结束发起刷新 pulltorefresh.on('transitionend webkittransitionend otransitionend', function (event) { // 由于transitionend会对每个属性回调一次,所以只处理其中一个 if (event.originalevent.propertyname == "transform") { // 暂停动画 pulltorefresh.removeclass("backtrantop"); pulltorefresh.unbind(); // 透明度重置为1 gotowards(finaloption.pausebound, undefined, 1); // 切换图片为loading图 pullimg.attr("src", finaloption.loadimg); // 因为anamition会覆盖transform的原因,使用top临时定位元素 pulltorefresh.addclass("loadinganimation"); pulltorefresh.css("top", finaloption.pausebound + "px"); // 回调刷新数据,最终应将refreshevent传回校验 finaloption.onrefresh(function (error, msg) { // 用户回调时dom通常已经更新, 需要通知iscroll调整(官方建议延迟执行,涉及到浏览器重绘问题) settimeout(function () { iscroll.refresh(); }, 0); // 重置角度,切换为pull图 gotowards(finaloption.pausebound); // 取消animation,重置top pulltorefresh.removeclass("loadinganimation"); pulltorefresh.css("top", ""); // 延迟过渡动画100毫秒,给浏览器重绘的机会 settimeout(function () { // 切换为pull图 pullimg.attr("src", finaloption.pullimg); // 恢复动画 pulltorefresh.addclass("backtrantop"); // 刷新完成 refreshevent = null; // 弹回顶部 gotowards(-55); }, 100); }); } }); } else { gotowards(-55); // 弹回顶部 refreshevent = null; // 未达成刷新触发条件 } } // 父容器注册下拉事件 $(container).on("touchstart", function (event) { // 新的触摸事件 touchevent = {}; // 有一个刷新事件正在进行 if (refreshevent) { return; } // 只有滚动轴位置接近顶部, 才可以生成新的刷新事件 if (iscroll.y < -1 * finaloption.lowerbound) { return; } // 一个新的刷新事件 refreshevent = touchevent; touchstarty = event.originalevent.changedtouches[0].clienty; pullstarty = cury; // 如果存在,则关闭回弹动画与相关监听 pulltorefresh.removeclass("backtrantop"); pulltorefresh.unbind(); // 切换为pull图 pullimg.attr("src", finaloption.pullimg); }).on("touchmove", function (event) { // 在刷新未完成前触摸,将被忽略 if (touchevent != refreshevent) { return; } var touchcury = event.originalevent.changedtouches[0].clienty; var touchdistance = touchcury - touchstarty; // 本次移动的距离 var curpully = pullstarty + touchdistance; // 计算图标应该移动到的位置 // 向下不能拉出范围 if (curpully > finaloption.lowerbound) { curpully = finaloption.lowerbound; } // 向上不能拉出范围 if (curpully <= -55) { curpully = -55; } // 更新图标的位置 gotowards(curpully); }).on("touchend", function (event) { // 在刷新未完成前触摸,将被忽略 if (touchevent != refreshevent) { return; } // 尝试启动回弹动画 trystartbacktrantop(); }); } // 初始化iscroll settimeout(function() { iscroll.refresh(); }, 0); // 返回操作此区域的工具对象 return { // 用户如果在下拉刷新之外修改了滚动区域的内容,需要主动调用refresh refresh: function() { // 延迟以便配合浏览器重绘 settimeout(function() { iscroll.refresh(); }, 0); }, // 触发下拉刷新 triggerpull: function() { // 正在刷新或者禁止刷新 if (refreshevent || finaloption.norefresh) { return false; } // 暂停可能正在进行的最终阶段回弹动画 pulltorefresh.removeclass("backtrantop"); // 小图标移动到lowerbound位置 gotowards(finaloption.lowerbound); // 创建新的刷新事件,占坑可以阻止在settimeout之前的触摸引起刷新 refreshevent = {}; // 延迟到浏览器重绘 settimeout(function() { trystartbacktrantop(); }, 100); }, }; }; contact github api training shop blog about © 2017 github, inc. terms privacy security status help
pulltorefresh.css:
.pulltorefresh { position:absolute; left:0; right:0; margin:auto; width: 50px; height: 50px; z-index: 10; opacity: 1; transform:translatey(-55px) translatez(0) rotatez(0deg); -ms-transform:translatey(-55px) translatez(0) rotatez(0deg); /* ie 9 */ -moz-transform:translatey(-55px) translatez(0) rotatez(0deg); /* firefox */ -webkit-transform:translatey(-55px) translatez(0) rotatez(0deg); /* safari 和 chrome */ -o-transform:translatey(-55px) translatez(0) rotatez(0deg); /* opera */ } .backtrantop { transition: transform 0.8s ease, opacity 0.8s ease; -moz-transition: transform 0.8s ease, opacity 0.8s ease; /* firefox 4 */ -webkit-transition: transform 0.8s ease, opacity 0.8s ease; /* safari 和 chrome */ -o-transition: transform 0.8s ease, opacity 0.8s ease; /* opera */ } .pullcontainer { position:relative; } .pulltorefresh img { display:block; width: 40px; height: 40px; /* 让img居中在.pulltorefresh中 */ position: absolute; top: 0; bottom: 0; left:0; right:0; margin:auto; } /* loading旋转动画 */ .loadinganimation { animation: loadingframe 1s infinite; -moz-animation: loadingframe 1s infinite; /* firefox */ -webkit-animation: loadingframe 1s infinite; /* safari 和 chrome */ -o-animation: loadingframe 1s infinite; /* opera */ } @keyframes loadingframe { from { transform: rotatez(360deg); } to { transform: rotatez(0deg); } } @-moz-keyframes loadingframe /* firefox */ { from { transform: rotatez(360deg); } to { transform: rotatez(0deg); } } @-webkit-keyframes loadingframe /* safari 和 chrome */ { from { transform: rotatez(360deg); } to { transform: rotatez(0deg); } } @-o-keyframes loadingframe /* opera */ { from { transform: rotatez(360deg); } to { transform: rotatez(0deg); } }
以上所述是小编给大家介绍的js+css实现下拉刷新/上拉加载插件,希望对大家有所帮助
上一篇: JS正则获取HTML元素的方法