前端页面如何实现下拉刷新

页面的滚动区域本来是用的插件vue-scroller,但是由于在一些低端安卓机中页面略有卡动,可能是GPU不给力造成的,这里尝试用原生的overflow: auto; 来测试一下滚动效果,发现效果不错,但是在ios设备中就发现了了滚动没有惯性很死板的体验,这里可以在滚动容器中写一行css属性-webkit-overflow-scrolling: touch;,这样就有原生的滚动体验啦,接下来就差一个下拉刷新的效果了,没有找到现成的轮子,这里就自己开撸

如何实现

  1. 当容器的scrollTop为0的时候,使用transform: translateY来模拟
  2. 检测下拉的高度当达到某一固定值的时候,释放手指,调用回调函数实现下拉刷新

着手实现

既然是下拉我们肯定需要监听touchstarttouchmovetouchend三个dom事件

touchstart

el.addEventListener('touchstart', e => {
    if (el.scrollTop !== 0) {
        return
    }
    beginPagY = e.touches[0].pageY
    e.preventDefault()
})

用于记录手指点按屏幕时候的位置,为了后续translateY的值计算做准备

touchstartmove

el.addEventListener('touchmove', e => {
    if (el.scrollTop !== 0) {
        return
    }
    const pageY = e.touches[0].pageY
    const distance = currentPos = pageY - beginPagY
    if (distance < 0 || distance > maxTranslateY) {
        // 上拉的和超过最大限定高度时候不做任何处理
        return;
    }
    if (distance > 60) {
        iconEl.classList.add('active')
    } else {
        iconEl.classList.remove('active')
    }
    e.preventDefault()
    el.style.transform = `translateY(${distance}px)`
})

touchmove主要是根据手指下拉的距离不断的修改container的translate的值来达到效果, 同时记录当前下拉的distance,用于松手是判断是否触发下拉刷新的效果; 同时在模拟下拉效果的时候要阻止系统默认事件e.preventDefault()

touchend

let clear = () => {
    this.isShowLoading = false
    el.style.transform = `translateY(0)`
    setTimeout(() => {
        el.style.transition = ``
    }, 200)
}
el.addEventListener('touchend', () => {
    el.style.transition = `.2s`
    if (currentPos >= 60) {
        this.isShowLoading = true
        el.style.transform = `translateY(30px)`
        callback && callback(() => {
            clear()
        })
        return
    }
    clear()
})

touchend 主要是用来松手时是否执行下拉刷新的效果, 当currentPos达到预先设定的值的时候就触发回调函数,这里设置transition来增加动画效果

最终效果

所有代码

<!doctype html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style>
        * {
            margin: 0;
            padding: 0;
        }
        #app {
            height: 100vh;
            overflow: hidden;
            display: flex;
            flex-direction: column;
        }
        header {
            width: 100%;
            height: 44px;
            background: teal;
            z-index: 10;
        }
        main {
            height: calc(100% - 44px);
            overflow: auto;
            -webkit-overflow-scrolling: touch;
            flex: 1;
        }
        .item {
            font-size: 12px;
            line-height: 30px;
            color: #aaa;
        }
        .item + .item {
            border-top: 1px solid;
        }
    </style>
    <style>
        .css-icon {
            display: inline-block;
            height: 1em; width: 1em;
            font-size: 20px;
            box-sizing: border-box;
            text-indent: -9999px;
            vertical-align: middle;
            position: relative;
        }
        .css-icon::before,
        .css-icon::after {
            content: '';
            box-sizing: inherit;
            position: absolute;
            left: 50%; top: 50%;
            -ms-transform: translate(-50%, -50%);
            transform: translate(-50%, -50%);
        }


        .icon-upward::before {
            height: .65em; width: .65em;
            border-style: solid;
            border-width: 2px 0 0 2px;
            -ms-transform: translate(-50%, -50%) rotate(45deg);
            transform: translate(-50%, -50%) rotate(45deg);
        }
        .icon-upward::after {
            height: .8em;
            border-left: 2px solid;
            top: 55%;
        }

        .icon-upward.active {
            transform: rotate(180deg);
            transition: transform .3s;
        }


        .pull-to-refresh-layer {
            height: 60px;
            margin-top: -60px;
            font-size: 12px;
            text-align: center;
            color: #aaa;
            line-height: 30px;
        }
    </style>
</head>
<body>
<div id="app">
    <header></header>
    <main>
        <div ref="container">
            <div class="pull-to-refresh-layer">
                <div v-show="!isShowLoading">
                    <i ref="icon" class="css-icon icon-upward"></i>
                    <p>下拉刷新</p>
                </div>
                <div v-show="isShowLoading" style="padding-top: 30px;">
                    假装是个loading图标
                </div>
            </div>
            <div class="item" v-for="item in list" :key="item">{{item}}</div>
        </div>
    </main>
</div>
<script src="./vue.js"></script>
<script>
    new Vue({
        el: '#app',
        data: {
            isShowLoading: false
        },
        computed: {
            list() {
                return new Array(100).fill(0).map((item, index) => index)
            }
        },
        mounted() {
            this.pullRefresh(this.$refs.container, (done) => {
                setTimeout(() => {
                    done()
                }, 1000)
            })
        },
        methods: {
            pullRefresh(el, callback) {
                let beginPagY = 0
                let currentPos
                const maxTranslateY = 150
                const iconEl = this.$refs.icon
                el.addEventListener('touchstart', e => {
                    if (el.scrollTop !== 0) {
                        return
                    }
                    beginPagY = e.touches[0].pageY
                    e.preventDefault()
                })
                el.addEventListener('touchmove', e => {
                    if (el.scrollTop !== 0) {
                        return
                    }
                    const pageY = e.touches[0].pageY
                    const distance = currentPos = pageY - beginPagY
                    if (distance < 0 || distance > maxTranslateY) {
                        // 上拉的时候不做任何处理
                        return;
                    }
                    if (distance > 60) {
                        iconEl.classList.add('active')
                        console.log(iconEl.classList);
                    } else {
                        iconEl.classList.remove('active')
                    }
                    e.preventDefault()
                    el.style.transform = `translateY(${distance}px)`
                })
                let clear = () => {
                    this.isShowLoading = false
                    el.style.transform = `translateY(0)`
                    setTimeout(() => {
                        el.style.transition = ``
                    }, 200)
                }
                el.addEventListener('touchend', () => {
                    el.style.transition = `.2s`
                    if (currentPos >= 60) {
                        this.isShowLoading = true
                        el.style.transform = `translateY(30px)`
                        callback && callback(() => {
                            clear()
                        })
                        return
                    }
                    clear()
                })
            }
        }
    })
</script>
</body>
</html>

posted @ 2019-10-12 17:18  请叫我宋某某  阅读(11811)  评论(0编辑  收藏  举报