JavaScript touch / 上拉加载

实现上来加载的关键其实就是获取文档高度,视口高度,然后通过判断用户在滑动到文档底部时发送数据请求,将新接收的数据填充到文档的尾部
首先需要我们了解的一些概念

  • clientHeight,clientWidth
  • offsetHeight,offsetWidth
  • scrollHeight,scrollWidth
  • scrollTop

这三个的兼容性时比较繁琐的,不同浏览器的处理方式都不相同,我以chrome的为准来大概说一下
client是各个浏览器处理都比较统一的,都是指元素的可视区域的宽/高,包含padding值,但不包含滚动条的宽/高
offset返回元素可视区域的宽/高,包含padding值,包含滚动条的宽/高
scroll返回文档的实际宽/高,包含元素的padding值


JavaScript touch / 上拉加载_第1张图片
QQ截图20171205151647.png

而scrollTop获取的则是滚动条滚动的距离,可以理解问当前位置距离文档顶部的距离


JavaScript touch / 上拉加载_第2张图片
QQ截图20171205152901.png

scrollTop的兼容性也比较恶心,有兴趣的可以看一下他们整理的兼容性图表
JavaScript touch / 上拉加载_第3张图片
192310310816099.jpg

需要用的时候做一下兼容性处理即可
scrollTop = document.documentElement.scrollTop || window.pageYOffset || document.body.scrollTop;

下面是一个上拉加载下拉刷新的插件

dropload

;(function ($) {
    'use strict';
    var win = window;
    var doc = document;
    var $win = $(win);
    var $doc = $(doc);
    $.fn.dropload = function (options) {
        return new MyDropLoad(this, options);
    };
    var MyDropLoad = function (element, options) {
        var me = this;
        me.$element = element;
        // 上方是否插入DOM
        me.upInsertDOM = false;
        // loading状态
        me.loading = false;
        // 是否锁定
        me.isLockUp = false;
        me.isLockDown = false;
        // 是否有数据
        me.isData = true;
        me._scrollTop = 0;
        me._threshold = 0;
        me.init(options);
    };

    // 初始化
    MyDropLoad.prototype.init = function (options) {
        var me = this;
        //设置opts 将用户定义和默认值合并,如果用户已设定按照用户设定,如果用户未设定,按照默认值 类似 name=options.name|| ‘tom’
        me.opts = $.extend(true, {}, {
            // 滑动区域
            scrollArea: me.$element,
            // 上方DOM
            domUp: {
                domClass: 'dropload-up',
                domRefresh: '
↓下拉刷新
', domUpdate: '
↑释放更新
', domLoad: '
加载中...
' }, // 下方DOM domDown: { domClass: 'dropload-down', domRefresh: '
↑上拉加载更多
', domLoad: '
加载中...
', domNoData: '
暂无数据
' }, // 自动加载 autoLoad: true, // 拉动距离 distance: 50, // 提前加载距离 threshold: '', // 上方function loadUpFn: '', // 下方function loadDownFn: '' }, options); // 判断loadDownFn是否赋值了事件,如果有说明用户设定了上拉加载,预先在下面插入加载时的DOM if (me.opts.loadDownFn != '') { me.$element.append('
' + me.opts.domDown.domRefresh + '
'); //获取插入的DOM me.$domDown = $('.' + me.opts.domDown.domClass); } //设定提前加载的距离 //判断是否存在底部加载的DOM,并且当前没有设定提前加载的距离 //计算提前加载距离 //否则按照之前设定的加载距离加载 if (!!me.$domDown && me.opts.threshold === '') { // 默认滑到加载区2/3处时加载 me._threshold = Math.floor(me.$domDown.height() * 1 / 3); } else { me._threshold = me.opts.threshold; } // 判断滚动区域 //判定用户指定的scrollArea是否是win if (me.opts.scrollArea == win) { me.$scrollArea = $win; // 获取文档高度 me._scrollContentHeight = $doc.height(); // 获取win显示区高度 —— 这里有坑 不同浏览器对clientHeight的兼容性不同 me._scrollWindowHeight = doc.documentElement.clientHeight; } else { me.$scrollArea = me.opts.scrollArea; me._scrollContentHeight = me.$element[0].scrollHeight; me._scrollWindowHeight = me.$element.height(); } //判断如果文档高度小于窗口高度,那么添加数据填充窗口 fnAutoLoad(me); // 窗口调整 $win.on('resize', function () { clearTimeout(me.timer); me.timer = setTimeout(function () { if (me.opts.scrollArea == win) { // 重新获取win显示区高度 me._scrollWindowHeight = win.innerHeight; } else { me._scrollWindowHeight = me.$element.height(); } //判断如果文档高度小于窗口高度,那么添加数据填充窗口 fnAutoLoad(me); }, 150); }); // 绑定触摸触发touch视图art事件 me.$element.on('touchstart', function (e) { //菊花图不存 if (!me.loading) { //fnTouches 对e.touchs做兼容处理 fnTouches(e); //执行touchstart事件 fnTouchstart(e, me); } }); me.$element.on('touchmove', function (e) { if (!me.loading) { fnTouches(e, me); fnTouchmove(e, me); } }); me.$element.on('touchend', function () { if (!me.loading) { fnTouchend(me); } }); // 加载下方 me.$scrollArea.on('scroll', function () { me._scrollTop = me.$scrollArea.scrollTop(); // 滚动页面触发加载数据 if (me.opts.loadDownFn != '' && !me.loading && !me.isLockDown && (me._scrollContentHeight - me._threshold) <= (me._scrollWindowHeight + me._scrollTop)) { loadDown(me); } }); }; // touches 兼容 function fnTouches(e) { if (!e.touches) { //e.originaEvent 是jq在对构造一个jq对象后jq创建的e对象对原始的e对象的引用 //根据jq源码,jq对象下的e.pageX和e.pageY和原生的是一样的,但是额外增加了对IE的兼容 e.touches = e.originalEvent.touches; } } // touchstart function fnTouchstart(e, me) { // 记住触摸时的scrolltop值 me._startY = e.touches[0].pageY; // 触摸点距离顶部的距离 me.touchScrollTop = me.$scrollArea.scrollTop(); } // touchmove function fnTouchmove(e, me) { //pageY 相对于文档左上角y的距离,也可以说是实际运动的距离 me._curY = e.touches[0].pageY; me._moveY = me._curY - me._startY; //动态计算 判断滑动方向来设定当前状态是 down/up if (me._moveY > 0) { //下拉 me.direction = 'down'; } else if (me._moveY < 0) { //上拉 me.direction = 'up'; } var _absMoveY = Math.abs(me._moveY); //如果当前用户设定了loadUpFn 当前滑动方向为down,window.scrollTop()<0 不是锁定状态 //上拉加载操作 //加载上方 if (me.opts.loadUpFn != '' && me.touchScrollTop <= 0 && me.direction == 'down' && !me.isLockUp) { e.preventDefault(); me.$domUp = $('.' + me.opts.domUp.domClass); // 如果加载区没有DOM if (!me.upInsertDOM) { me.$element.prepend('
'); me.upInsertDOM = true; } fnTransition(me.$domUp, 0); //如果拉动距离小于设定的拉动距离 // 下拉 if (_absMoveY <= me.opts.distance) { me._offsetY = _absMoveY; // todo:move时会不断清空、增加dom,有可能影响性能,下同 me.$domUp.html(me.opts.domUp.domRefresh); // 指定距离 < 下拉距离 < 指定距离*2 } else if (_absMoveY > me.opts.distance && _absMoveY <= me.opts.distance * 2) { me._offsetY = me.opts.distance + (_absMoveY - me.opts.distance) * 0.5; me.$domUp.html(me.opts.domUp.domUpdate); // 下拉距离 > 指定距离*2 } else { me._offsetY = me.opts.distance + me.opts.distance * 0.5 + (_absMoveY - me.opts.distance * 2) * 0.2; } me.$domUp.css({'height': me._offsetY}); } } // touchend function fnTouchend(me) { var _absMoveY = Math.abs(me._moveY); // 存在loadUpFn 下拉 当前状态 'down' 不是锁定状态 if (me.opts.loadUpFn != '' && me.touchScrollTop <= 0 && me.direction == 'down' && !me.isLockUp) { //动画 fnTransition(me.$domUp, 300); //拉动超过设定的距离 if (_absMoveY > me.opts.distance) { me.$domUp.css({'height': me.$domUp.children().height()}); me.$domUp.html(me.opts.domUp.domLoad); me.loading = true; //执行loadUpFn函数 me.opts.loadUpFn(me); } else { //在transtion动画结束时 me.$domUp.css({'height': '0'}).on('webkitTransitionEnd mozTransitionEnd transitionend', function () { //将目前是否显示上方DOM状态设置为false me.upInsertDOM = false; //删除目前显示的DOM $(this).remove(); }); } //重置 me._moveY = 0; } } // 如果文档高度不大于窗口高度,数据较少,自动加载下方数据 function fnAutoLoad(me) { if (me.opts.loadDownFn != '' && me.opts.autoLoad) { if ((me._scrollContentHeight - me._threshold) <= me._scrollWindowHeight) { loadDown(me); } } } // 重新获取文档高度 function fnRecoverContentHeight(me) { if (me.opts.scrollArea == win) { me._scrollContentHeight = $doc.height(); } else { me._scrollContentHeight = me.$element[0].scrollHeight; } } // 加载下方 function loadDown(me) { me.direction = 'up'; me.$domDown.html(me.opts.domDown.domLoad); me.loading = true; me.opts.loadDownFn(me); } // 锁定 MyDropLoad.prototype.lock = function (direction) { var me = this; // 如果不指定方向 if (direction === undefined) { // 如果操作方向向上 if (me.direction == 'up') { me.isLockDown = true; // 如果操作方向向下 } else if (me.direction == 'down') { me.isLockUp = true; } else { me.isLockUp = true; me.isLockDown = true; } // 如果指定锁上方 } else if (direction == 'up') { me.isLockUp = true; // 如果指定锁下方 } else if (direction == 'down') { me.isLockDown = true; // 为了解决DEMO5中tab效果bug,因为滑动到下面,再滑上去点tab,direction=down,所以有bug me.direction = 'up'; } }; // 解锁 MyDropLoad.prototype.unlock = function () { var me = this; // 简单粗暴解锁 me.isLockUp = false; me.isLockDown = false; // 为了解决DEMO5中tab效果bug,因为滑动到下面,再滑上去点tab,direction=down,所以有bug me.direction = 'up'; }; // 无数据 MyDropLoad.prototype.noData = function (flag) { var me = this; if (flag === undefined || flag == true) { me.isData = false; } else if (flag == false) { me.isData = true; } }; // 重置 MyDropLoad.prototype.resetload = function () { var me = this; if (me.direction == 'down' && me.upInsertDOM) { me.$domUp.css({'height': '0'}).on('webkitTransitionEnd mozTransitionEnd transitionend', function () { me.loading = false; me.upInsertDOM = false; $(this).remove(); fnRecoverContentHeight(me); }); } else if (me.direction == 'up') { me.loading = false; // 如果有数据 if (me.isData) { // 加载区修改样式 me.$domDown.html(me.opts.domDown.domRefresh); fnRecoverContentHeight(me); fnAutoLoad(me); } else { // 如果没数据 me.$domDown.html(me.opts.domDown.domNoData); } } }; // css过渡 function fnTransition(dom, num) { dom.css({ '-webkit-transition': 'all ' + num + 'ms', 'transition': 'all ' + num + 'ms' }); } })(window.Zepto || window.jQuery); /* * 方法核心 loadDown() * * 在touch事件结束时执行fnTouchend函数 * 判断me.opts.loadUpFn != '' && me.touchScrollTop <= 0 && me.direction == 'down' && !me.isLockUp * 成立那么执行下拉刷新 * * 监听scroll事件 * touch同时会触发scroll事件 * (me._scrollContentHeight - me._threshold) <= (me._scrollWindowHeight + me._scrollTop) * 通过判断当前window的scrollTop + 视口的高度 是否大于文档高度 - 添加的提示信息的高度 来判断是否应该触发下拉加载 * */
JavaScript touch / 上拉加载_第4张图片
QQ截图20171130183648.png

你可能感兴趣的:(JavaScript touch / 上拉加载)