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实现下拉刷新/上拉加载插件,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对我们网站的支持!

(0)

相关推荐

  • js仿手机页面文件下拉刷新效果

    最后弄出了一个简单的下拉刷新页面的形式,还不算太复杂 要在仿真器下才能看到效果,比如chrome的里边(或者用手机浏览器查看,但测试发现有些浏览器有问题,目前手机猎豹是没问题的) 主要就是: 下拉-->提示松开刷新-->松开后-->开始刷新-->刷新成功后还原 html,css部分 style type="text/css"> #slideDown{margin-top: 0;width: 100%;} #slideDown1,#slideDown2{wi

  • iscroll.js的上拉下拉刷新时无法回弹的解决方法

    使用过iscroll.js的上拉下拉刷新效果的朋友应该都碰到过这个问题:在iOS的浏览器中,上拉或下拉刷新时,当手指划出屏幕后,页面无法弹回.很多人因为解决不了这个问题,干脆就那样不解决了,还有的直接就不用HTML了,使用原生代替HTML页面. 相信很多朋友也有自己的解决办法,只是没写出来,所以网上都搜不到解决方案.在很多QQ群里面也有很多人在问该怎么解决这个问题,所以我写这篇文章记录一下我的解决方案,希望对一些朋友有所帮助. 上拉下拉刷新的主要代码: myScroll = new iScrol

  • Listloading.js移动端上拉下拉刷新组件

    listloading是一个移动端的上拉.下拉加载更多的组件.主要依赖于iscroll.js v5.1.2基础上开发的组件,基础库可以使用jquery.js或者zepto.js操作dom节点,目前我是使用了zepto.js作为基础库操作dom,以jquery插件的形式存在.如果不想以插件方式使用,则只需要把listloading直接移植你需要的库里面就ok啦.listloading主要针对移动端而生,在使用浏览器自带滚动,用户体验很不友好,与Android和ios差别甚远,所以选择iscroll

  • APP中javascript+css3实现下拉刷新效果

    原生app里的数据列表都会使用下拉刷新的效果,在webapp里可以采用iscroll.swiper等插件或框架实现,那么如何自己编码实现类似的效果呢,下面介绍使用原生js+css3实现的简单效果. html布局 <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-

  • 纯javascript实现简单下拉刷新功能

    代码很简单,实现的功能却很实用,直接奉上代码 CSS: 复制代码 代码如下: <meta charset="utf-8" /> <title>Pull to Refresh</title> <meta name="viewport" content="width=device-width,height=device-height,inital-scale=1.0,maximum-scale=1.0,user-sca

  • AngularJS上拉加载问题解决方法

    项目中始终存在一个问题:当在搜索栏输入关键词后(见图1),按照既定的业务逻辑应该是服务端接收到请求后,首先返回查询的前7条数据,待客户端出现上拉加载时,继续查找另外7条数据.但实际情形是不确定的,在服务端控制台(见图2)可看到begno一直到了126,也就是相当于客户端往服务端请求了127次,这是一个令人无法忍受的结果. 图1 客户端搜索栏 图2 服务端控制台 可以断定是客户端的业务逻辑出现了问题.返回到客户端,查看源码逻辑,修改如下: 控制器 $scope.medsearchMore = fu

  • 基于iscroll.js实现下拉刷新和上拉加载效果

    现在已经不是纯Android独霸天下的时代了,H5嵌入Android的Hybrid混合开发是大势所趋.今天给大家带来的就是移动端中常见的"上拉刷新,下拉加载"特效,这个特效将会基于H5来实现. 先看下运行效果: 是不是有点小小的'鸡冻' ,它就是由我们今天要介绍的主人公'iscroll.js'实现的,接下来我以最最简便的方式教给大家~~ 实现步骤 一.准备好iscroll.js库 到官网下载即可: https://github.com/cubiq/iscroll 二.搭建页面结构 &l

  • 基于iScroll实现下拉刷新和上滑加载效果

    本文实例为大家分享了iScroll下拉刷新上滑加载展示的具体代码,供大家参考,具体内容如下 html代码: <!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <meta name="viewport" content="width=devi

  • JS+CSS实现下拉刷新/上拉加载插件

    闲来无事,写了一个当下比较常见的下拉刷新/上拉加载的jquery插件,代码记录在这里,有兴趣将代码写成插件与npm包可以留言. 体验地址:http://owenliang.github.io/pullToRefresh/ 项目地址:https://github.com/owenliang/pullToRefresh 实现注意: 利用transition做动画时,优先使用transform:translate取代top,后者动画流畅度存在问题. 各移动浏览器对手势触摸的处理不同(简单罗列如下),但

  • vue实现网络图片瀑布流 + 下拉刷新 + 上拉加载更多(步骤详解)

    一.思路分析和效果图 用vue来实现一个瀑布流效果,加载网络图片,同时有下拉刷新和上拉加载更多功能效果.然后针对这几个效果的实现,捋下思路: 根据加载数据的顺序,依次追加标签展示效果: 选择哪种方式实现瀑布流,这里选择绝对定位方式: 关键问题:由于每张图片的宽高不一样,而瀑布流中要求所有图片的宽度一致,高度随宽度等比缩放.而且由于图片的加载是异步延迟.在不知道图片高度的情况下,每个图片所在的item盒子不好绝对定位.因此在渲染页面前先获取所有图片的高度,是解决问题的关键点!这里选择用JS中的Im

  • React Native 自定义下拉刷新上拉加载的列表的示例

    在移动端开发中列表页是非常常见的页面,在React Native中我们一般使用FlatList或SectionList组件实现这些列表视图.通常列表页都会有大量的数据需要加载显示,这时候就用到了分页加载,因此对于列表组件来说,实现下拉刷新和上拉加载在很多情况下是必不可少的. 本篇文章基于FlatList封装一个支持下拉刷新和上拉加载的RefreshListView,对原始的FlatList进行封装之后,再调用上拉和下拉刷新就十分方便了. 下拉刷新的实现十分简单,这里我们沿用FlatList本身的

  • Android ListView下拉刷新上拉自动加载更多DEMO示例

    代码下载地址已经更新.因为代码很久没更新,已经很落伍了,建议大家使用RecyclerView实现. 参考项目: https://github.com/bingoogolapple/BGARefreshLayout-Android https://github.com/baoyongzhang/android-PullRefreshLayout 下拉刷新,Android中非常普遍的功能.为了方便便重写的ListView来实现下拉刷新,同时添加了上拉自动加载更多的功能.设计最初是参考开源中国的And

  • Android 仿硅谷新闻下拉刷新/上拉加载更多

    1.添加加载更多布局 1_初始化和隐藏代码 在RefreshListView构造方法中调用 private void initFooterView(Context context) { View footerView = View.inflate(context, R.layout.refresh_listview_footer, null); //隐藏代码 footerView.measure(0, 0); int footerViewHeight = footerView.getMeasur

  • android RecyclerView侧滑菜单,滑动删除,长按拖拽,下拉刷新上拉加载

    本文介绍的库中的侧滑效果借鉴自SwipeMenu,并对SipwMenu的源码做了修改与Bug修复,然后才开发出的SwipeRecyclerView. 需要说明的是,本库没有对RecyclerView做大的修改,只是ItemView的封装.看起来是对RecyclerView的修改,其实仅仅是为RecyclerView添加了使用的方法API而已. 本库已经更新了三个版本了,会一直维护下去,根据小伙伴的要求,以后也会添加一些其它功能. SwipeRecyclerView将完美解决这些问题: 以下功能全

  • react-native ListView下拉刷新上拉加载实现代码

    本文介绍了react-native ListView下拉刷新上拉加载实现.分享给大家,具体如下: 先看效果图 下拉刷新 React Native提供了一个组件可以实现下拉刷新方法RefreshControl 使用方法 <ListView refreshControl={ <RefreshControl refreshing={this.state.refreshing} onRefresh={this._onRefresh.bind(this)} /> } //... </List

  • Android下拉刷新上拉加载控件(适用于所有View)

    前面写过一篇关于下拉刷新控件的文章下拉刷新控件终结者:PullToRefreshLayout,后来看到好多人还有上拉加载更多的需求,于是就在前面下拉刷新控件的基础上进行了改进,加了上拉加载的功能.不仅如此,我已经把它改成了对所有View都通用!可以随心所欲使用这两个功能~~ 我做了一个大集合的demo,实现了ListView.GridView.ExpandableListView.ScrollView.WebView.ImageView.TextView的下拉刷新和上拉加载.后面会提供demo的

  • RecyclerView下拉刷新上拉加载

    一 .前言 最近实在太忙,一个多礼拜没有更新文章了,于是今晚加班加点把demo写出来,现在都12点了才开始写文章. 1.我们的目标 把RecyclerView下拉刷新上拉加载更多加入到我们的开发者头条APP中. 2.效果图 3.实现步骤 找一个带上拉刷新下载加载更多的RecyclerView开源库,我们要站在巨人的肩膀上 下载下来自己先运行下demo,然后看看是不是我们需要的功能,觉得不错就把module依赖进来,整合主项目. 整合进来了之后,我们肯定需要进行修改,例如我这边就有滑动冲突,有多个

随机推荐