h5下拉刷新实现

4,699 阅读3分钟

前言


混合开发是我们经常使用的开发模式,快节奏的时代,app的发版流程缓慢为h5带来了更多的可能,带在体验上来说,h5还是有很长的路要走。那么今天我们就来看一个场景,下拉刷新操作,在app里面可以说是很常见的功能,但在h5中经常被忽略,那么今天就手写一个h5下拉刷新,让h5体验距离app更近一步。
git仓库

操作


用户下拉 |---->> 无效下拉 ---->> 自动回弹
用户下拉 |---->> 有效下拉 ---->> 触发回调方法(刷接口/刷页面)---->> 自动回弹

实现


主要通过监听touch事件获取手指滑动轨迹,并实时改变 translate 达到页面滑动效果,增加阻尼概念让下拉操作更加真实,结束时判断是否有效下拉来执行对应方法

/**
 * @param {Function}pullCallback
 * 下拉回调,不传默认为 reload();
 * 有效下拉:指页面被下拉至文档顶部离开视口顶部
 * 暂不支持 微信等 q系浏览器
 * @使用方法
 *     const pullObj = pullRefresh(() => {
 *         // 刷数据接口
 *         // 刷新完成之后调用 pullObj.finish(); 顶部刷新区归位
 *     });
 *     pullObj.init(); 初始化
 */

export function pullRefresh(pullCallback) {
    const body = document.body;
    const app = document.getElementById('app');
    const app_style_cache = app.style;
    const pullTextEl = document.createElement('p');

    pullTextEl.style = "width: 100%; text-align: center; position: fixed; z-index: -1; left: 0; top: 0; font-size: 12px; line-height: 40px; color: #999; display: none;";
    const loadText = document.createElement('span');
    // 是否执行有效下拉回调标志位
    let refreshStatus = false;
    const isQBrowser = () => { // qq系浏览器
        const ua = navigator.userAgent.toLowerCase();
        return !!ua.match(/mqqbrowser|qzone|qqbrowser/i);
    };
    const finish = () => {
        if(isQBrowser()) return;
        loadText.innerText = '刷新成功';
        setTimeout(() => {
            app.style['transition'] = 'transform 0.2s';
            app.style['transform'] = 'translate(0, 0)';
            setTimeout(() => {
                pullTextEl.style['display'] = 'none';
                app.style = app_style_cache;
            }, 400);
        }, 500);
    };
    const init = () => {
        if(isQBrowser()) return;
        body.appendChild(pullTextEl);
        pullTextEl.appendChild(loadText);
        let startP, moveLen;
        // 默认有效下拉回调
        const _pullCallback = () => {
            window.location.reload();
        };
        // 下拉处理
        const _pullHandler = (moveLen) => {
            // 下拉元素距离视口顶部距离
            const top_distance = app.getBoundingClientRect().top;
            // 有效下拉才做处理
            if(top_distance >= 0) {
                if(pullTextEl.style['display'] === 'none') {
                    // 有效下拉时才展示提示文案
                    pullTextEl.style['display'] = 'block';
                }
                // 下拉效果
                if(moveLen > 0 && moveLen < 50){
                    app.style['transform'] = 'translate(0, ' + moveLen + 'px)';
                } else if(moveLen >= 50 && moveLen < 100) { // 到刷新标志,下拉阻尼增大
                    const _moveLen = 50 + (moveLen - 50) * 0.6;
                    app.style['transform'] = 'translate(0, ' + _moveLen + 'px)';
                } else if(moveLen >= 100) { // 下拉超过 100,下拉阻尼再次增大
                    const _moveLen = 80 + (moveLen - 100) * 0.2;
                    app.style['transform'] = 'translate(0, ' + _moveLen + 'px)';
                }
                // 下拉触发
                if(top_distance < 50) {
                    loadText.innerText = '下拉即可刷新...';
                    refreshStatus = false;
                } else {
                    loadText.innerText = '松开立即刷新...';
                    refreshStatus = true;
                }
            }
        };
        app.addEventListener('touchstart', (e) => {
            startP = e.touches[0].pageY;
            app.style['transition'] = 'transform 0s';
        });
        app.addEventListener('touchmove', (e) => {
            moveLen = e.touches[0].pageY - startP;
            _pullHandler(moveLen);
        });
        app.addEventListener('touchend', (e) => {
            // 下拉元素距离视口顶部距离
            const top_distance = app.getBoundingClientRect().top;
            if(top_distance > 0) { // 当有效下拉发生后动画归位,重置样式
                if(refreshStatus) {
                    loadText.innerText = '数据加载中...';
                    pullCallback ? pullCallback() : _pullCallback();
                    app.style['transition'] = 'transform 0.4s';
                    app.style['transform'] = 'translate(0, 40px)';
                } else {
                    app.style['transition'] = 'transform 0.4s';
                    app.style['transform'] = 'translate(0, 0)';
                    setTimeout(() => {
                        pullTextEl.style['display'] = 'none';
                        app.style = app_style_cache;
                    }, 400);
                }
            } else { // 未发生有效下拉的直接重置样式
                pullTextEl.style['display'] = 'none';
                app.style = app_style_cache;
            }
        })
    };
    return {
        init,
        finish
    }
}

如何使用


es6 下直接 import 引用 pullRefresh_es6.js 文件即可
其他情况可引用 pullRefresh.js 或 pullRefresh.min.js 文件,直接使用 window 全局变量 pullRefresh,或通过 require 方式引用上述文件

具体使用:  
配置  
var pullObj = pullRefresh(function() {  
    // 下拉回调处理 your code  
    handler();  
    // 结束  
    pullObj.finish();  
});  
初始化  
pullObj.init();  

结语


用户体验是每一位前端人都应该注意的,我们是距离用户最近的一层,每一个细节,每一次交互,都会影响着用户对我们自己产品的态度,想要得到用户,就得给有’看似扭捏,实则拿捏‘的实力和态度
致敬每一位前端coder;Respect;

666.jpeg