h5下拉刷新-pullRefresh v1.1

1,360 阅读2分钟

前言


接上期文章h5下拉刷新实现传统下拉场景之后,又去原生APP所遇场景并不仅仅是全屏范围的下拉刷新,有更多的场景则是部分元素下拉刷新,如列表的下拉刷新操作,so 我又来了,针对这种场景,优化一下我们的 pullRefresh,开始吧

效果展示


其一为局部元素列表下拉刷新效果
其二为页面整体下拉刷新效果

列表下拉刷新 全屏下拉刷新

整体代码


话不多说放代码

/**
 * @param {Object} options
 * @使用方法
 *     const pullObj = pullRefresh(options);
 *     pullObj.init(); 初始化
 */

const parseTime = (timestamp) => {
    const a = new Date(timestamp);
    const h = a.getHours();
    const s = a.getMinutes();
    const double = t => t > 9 ? t : "0" + t;
    return double(h) + ":" + double(s);
}
const loadingIcon = "loading.png";
const defaultOptions = {
    parEl: document.body, // 父元素(不动的大盒子)
    tarEl: "app", // 目标元素(滑动的子盒子)
    pullCallback: () => { // 有效下拉回调,通常在内部获取数据并调用 finish() 方法
        window.location.reload();
    }
};
export function pullRefresh(_options) {
    const options = {
        ...defaultOptions,
        ..._options
    };
    let lastUpdateTime = new Date().getTime();
    let { parEl, tarEl, pullCallback } = options;
    if(typeof parEl === "string") {
        parEl = document.getElementById(parEl);
    }
    if(typeof tarEl === "string") {
        tarEl = document.getElementById(tarEl);
    }
    const appStyleCache = tarEl.style;
    const tarElPosition = appStyleCache.position;
    const parTopDistance = parEl.getBoundingClientRect().top;
    if(!tarElPosition) {
        tarEl.style["position"] = "relative";
        appStyleCache["position"] = "relative";
    }
    const pullTextEl = document.createElement("p");

    pullTextEl.style = "width: 100%; text-align: center; position: absolute; z-index: 0; left: 0; top: -50px; font-size: 12px; line-height: 16px; color: #666; display: none;";
    const loadImg = document.createElement("i");
    loadImg.style = `display: inline-block; width: 15px; height: 15px; margin-right: 5px; background: url('${loadingIcon}'); background-size: 100% 100%; vertical-align: middle; animation: looprotate 2s linear infinite;`;
    const loadText = document.createElement("span");
    loadImg.style.opacity = 0;
    // 是否执行有效下拉回调标志位
    let refreshStatus = false;
    const isQBrowser = () => { // qq系浏览器
        const ua = navigator.userAgent.toLowerCase();
        return !!ua.match(/mqqbrowser|qzone|qqbrowser/i);
    };
    const finish = () => {
        if(isQBrowser()) return;
        lastUpdateTime = new Date().getTime();
        loadImg.style.opacity = 0;
        loadText.innerText = `刷新成功 \n最后更新:${parseTime(lastUpdateTime)}`;
        setTimeout(() => {
            tarEl.style["transition"] = "transform 0.2s";
            tarEl.style["transform"] = "translate(0, 0)";
            setTimeout(() => {
                pullTextEl.style["display"] = "none";
                // tarEl.style = appStyleCache;
            }, 400);
        }, 500);
    };
    const init = () => {
        if(isQBrowser()) return;
        tarEl.appendChild(pullTextEl);
        pullTextEl.appendChild(loadImg);
        pullTextEl.appendChild(loadText);
        let startP, moveLen;
        // 下拉处理
        const pullHandler = (moveLen) => {
            // 下拉元素距离视口顶部距离
            const tarTopDistance = tarEl.getBoundingClientRect().top;
            // 下拉元素距离父级顶部距离
            const toParTopDistance = tarTopDistance - parTopDistance;
            // 有效下拉才做处理
            if(toParTopDistance >= 0) {
                if(pullTextEl.style["display"] === "none") {
                    // 有效下拉时才展示提示文案
                    pullTextEl.style["display"] = "block";
                }
                // 下拉效果
                if(moveLen > 0 && moveLen < 50){
                    tarEl.style["transform"] = "translate(0, " + moveLen + "px)";
                } else if(moveLen >= 50 && moveLen < 100) { // 到刷新标志,下拉阻尼增大
                    const _moveLen = 50 + (moveLen - 50) * 0.6;
                    tarEl.style["transform"] = "translate(0, " + _moveLen + "px)";
                } else if(moveLen >= 100) { // 下拉超过 100,下拉阻尼再次增大
                    const _moveLen = (50 + ( 100 - 50)*0.6) + (moveLen - 100) * 0.2;
                    tarEl.style["transform"] = "translate(0, " + _moveLen + "px)";
                }
                // 下拉触发
                if(toParTopDistance < 55) {
                    loadText.innerText = `下拉可以刷新... \n最后更新:${parseTime(lastUpdateTime)}`;
                    refreshStatus = false;
                } else {
                    loadText.innerText = `松开立即刷新... \n最后更新:${parseTime(lastUpdateTime)}`;
                    refreshStatus = true;
                }
            }
        };
        tarEl.addEventListener("touchstart", (e) => {
            startP = e.touches[0].pageY;
            tarEl.style["transition"] = "transform 0s";
        });
        tarEl.addEventListener("touchmove", (e) => {
            moveLen = e.touches[0].pageY - startP;
            pullHandler(moveLen);
        });
        tarEl.addEventListener("touchend", () => {
            // 下拉元素距离视口顶部距离
            const tarTopDistance = tarEl.getBoundingClientRect().top;
            // 下拉元素距离父级顶部距离
            const toParTopDistance = tarTopDistance - parTopDistance;
            if(toParTopDistance > 0) { // 当有效下拉发生后动画归位,重置样式
                if(refreshStatus) {
                    loadImg.style.opacity = 1;
                    loadText.innerText = `正在刷新数据中... \n最后更新:${parseTime(lastUpdateTime)}`;
                    pullCallback && pullCallback();
                    tarEl.style["transition"] = "transform 0.4s";
                    tarEl.style["transform"] = "translate(0, 55px)";
                } else {
                    tarEl.style["transition"] = "transform 0.4s";
                    tarEl.style["transform"] = "translate(0, 0)";
                    setTimeout(() => {
                        pullTextEl.style["display"] = "none";
                        tarEl.style = appStyleCache;
                    }, 400);
                }
            } else { // 未发生有效下拉的直接重置样式
                pullTextEl.style["display"] = "none";
                tarEl.style = appStyleCache;
            }
        });
    };
    return {
        init,
        finish
    };
}

如何使用


本次优化更新了入参方式,将接受一个option对象

const pullRefreshObj = pullRefresh({
    parEl: "pullParEl", // 作用元素父元素
    tarEl: "pullTarEl", // 目标作用元素
    pullCallback: async () => { // 产生有效下拉后释放触发回调方法
        await yourHandler();
        pullRefreshObj.finish();
    }
});
pullRefreshObj.init(); // 初始化

结语


没错,继上次的初版下拉刷新,我们又成功俘获了用户的芳心,体验上去了,还愁留不住用户吗? 致敬每一位前端coder;Respect;

666.jpeg