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:;
right:;
margin:auto;
width: 50px;
height: 50px;
z-index:;
opacity:;
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:;
bottom:;
left:;
right:;
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实现的下拉刷新/上拉加载插件的更多相关文章
- Android 下拉刷新上啦加载SmartRefreshLayout + RecyclerView
在弄android刷新的时候,可算是耗费了一番功夫,最后发觉有现成的控件,并且非常好用,这里记录一下. 原文是 https://blog.csdn.net/huangxin112/article/de ...
- SwipeRefreshLayout实现下拉刷新上滑加载
1. 效果图 2.RefreshLayout.java package myapplication.com.myapplication; import android.content.Context; ...
- 移动端下拉刷新上拉加载-mescroll.js插件
最近无意间看到有这么一个上拉刷新下拉加载的插件 -- mescroll.js,个人感觉挺好用的,官网地址是:http://www.mescroll.com 然后我就看了一下文档,简单的写了一个小dem ...
- vue10行代码实现上拉翻页加载更多数据,纯手写js实现下拉刷新上拉翻页不引用任何第三方插件
vue10行代码实现上拉翻页加载更多数据,纯手写js实现下拉刷新上拉翻页不引用任何第三方插件/库 一提到移动端的下拉刷新上拉翻页,你可能就会想到iScroll插件,没错iScroll是一个高性能,资源 ...
- js 前端实现下拉刷新 上拉加载
效果 css html,body{ height:100%; // 其他界面未设置html 无法监听scroll } /* 下拉刷新 */ .refresh-loading { transition: ...
- 基于SwiperJs的H5/移动端下拉刷新上拉加载更多的效果
最早时,公司的H5项目中曾用过点击一个"加载更多"的DOM元素来实现分页的功能,后来又用过网上有人写的一个上拉加载更多的插件,那个插件是页面将要滚动到底部时就自动请求数据并插入到页 ...
- 基于SwiperJs的H5/移动端下拉刷新上拉加载更多
最早时,公司的H5项目中曾用过点击一个"加载更多"的DOM元素来实现分页的功能,后来又用过网上有人写的一个上拉加载更多的插件,那个插件是页面将要滚动到底部时就自动请求数据并插入到页 ...
- vue mint-ui 框架下拉刷新上拉加载组件的使用
安装 npm i mint-ui -S 然后在main.js中引入 import MintUI from 'mint-ui' import 'mint-ui/lib/style.css' Vue.us ...
- mui下拉刷新上拉加载
新外卖商家端主页订单大厅页面 使用mui双webview,实现下拉刷新上拉加载 主页面: order_index.html <!doctype html> <html> < ...
随机推荐
- php小测试,难点与分享
B/S(网页程序) 网页结构,依托游览器 C/S(客户端程序) 单引号和双引号包含字符串的区别: 双引号里面可以解析变量,比如: $a=555; echo "你好{$a}"; 输出 ...
- java判断字符串String中是否存在中文
public class IsContainChinese { public static boolean isContainChinese (String str){ boolean flag=tr ...
- Java安装
java特点:跨平台.简单.面向对像编译后解释再运行安装JDK 环境变量的配置:JAVA_HOME:JDK的根目录====C:\Program Files\Java\jdk1.7.0_25Path:J ...
- californium 框架设计分析
Californium 源码分析 1. Californium 项目简介 Californium 是一款基于Java实现的Coap技术框架,该项目实现了Coap协议的各种请求响应定义,支持CON/NO ...
- PHP 用session与gd库实现简单验证码生成与验证的类
验证码是为了防止机器灌水给网站带来污染以及增加服务器负担而出现的.目前大大小小的网站都有验证码.今天自己实现了一个简单的验证码类.说简单是因为没有加一些干扰的弧线等等,只是将文字旋转了一下.当然,因为 ...
- VS2008编译汇编程序的问题
在VS2008 pro里已经集成了MASM,经过相关设置后,编译出了程序,但是执行后,说少msvcr90.dll39944然后自己写了个39945 那个testprintf是执行正常的.虽然两个都有引 ...
- BZOJ [HAOI2011]防线修建(动态凸包)
听说有一种很高端的东西叫动态凸包维护dp就像学一下,不过介于本人还不会动态凸包就去学了下,还是挺神奇的说,维护上下凸包的写法虽然打得有点多不过也只是维护复制黏贴的事情而已罢了. 先说下动态凸包怎么写吧 ...
- Android开发9:网络访问&Web服务开发
前言 啦啦啦各位小伙伴们好~ 一起进入我们今天的主题.今天我们将和大家学习网络访问和Web服务开发的相关知识,一起学习熟练使用 HttpURLConnection 访问 WebService,熟悉使用 ...
- C语言程序的三种基本结构
1.程序结构:在C语言程序中,一共有三种程序结构:顺序结构.选择结构(分支结构).循环结构: 顺序结构:从头到尾一句接着一句的执行下来,直到执行完最后一句: 选择结构:到某个节点后,会根据一次判断的结 ...
- 2017-2-18 net 输入输出语句
控制台程序的创建,输出,输入语句,定义变量,变量赋值,值覆盖,值拼接,值打印两种数据类型,整形类型转换 知识点: 1.输出语句 Console.WriteLine("");光标换行 ...