当前位置: 移动技术网 > IT编程>开发语言>JavaScript > JS+CSS实现下拉刷新/上拉加载插件

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

2019年03月20日  | 移动技术网IT编程  | 我要评论
闲来无事,写了一个当下比较常见的下拉刷新/上拉加载的jquery插件,代码记录在这里,有兴趣将代码写成插件与npm包可以留言。 体验地址:http://owenliang

闲来无事,写了一个当下比较常见的下拉刷新/上拉加载的jquery插件,代码记录在这里,有兴趣将代码写成插件与npm包可以留言。

体验地址:

项目地址: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实现下拉刷新/上拉加载插件,希望对大家有所帮助

如您对本文有疑问或者有任何想说的,请 点击进行留言回复,万千网友为您解惑!

相关文章:

验证码:
移动技术网