H5 下拉加载更多(模拟微信聊天记录)

前言

前段时间用H5实现一个实时聊天的功能。发现很难实现像微信聊天记录一样下拉加载更多记录。市面上大部分的 Web 项目实现的效果都是上拉加载,下拉刷新。下拉加载更多很少见,下拉在加载数据方面与上拉是一样的,但是如何做到像微信聊天记录一样,下拉之后还是保留在原有的位置就需要思考一下了。
H5 下拉加载更多(模拟微信聊天记录)

下拉与上拉区别

下拉与上拉在加载数据上面基本一致,但是因为方向不同,所以也存在以下不同之处。

  1. 上拉是将数据加载到原数据后面,所以用的是数组拼接 concat 方法。而下拉是将数据添加到原数据前面,所以需要使用 unshift 方法。
  2. 上拉是滑动到底部时触发,下拉是滑动到顶部时触发,因此判断 scrollTop 为 0 时触发,触发完之后需要将 scrollTop 重置一下。
  3. 为了模拟微信聊天记录加载方式,下拉之后需要滑动到原先的位置。通过 scrollTo 方法实现,scrollTo(x,y) 接收两个参数,x 为页面 X 轴,y 为页面 Y 轴,这里显然是需要修改 Y 轴的距离。x 为 0 即可。而 y 值就是第一次的 scrollHeight 滑动高度,因为每次加载的数据都是一样的,所以使用 scrollHeight 作为上次滑动位置。
  4. 代码中有很多定时器的使用,主要是为了防止用户频繁滑动(节流)以及在列表渲染之后再去获取 DOM 属性。

实现思路

  1. 封装一个加载数据的方法,首先加载初始数据;
  2. 获取列表的 scrollHeight,将记录定位到最底部,也就是最新的记录在可视区域的最下面。
  3. 监听滑动,当滑动到最顶部时,加载一屏数据,同时滑动定位到上次初始的位置。

完整代码如下

<!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>
        .container {
            width: 300px;
            height: 300px;
            overflow: auto;
            border: 1px solid;
            margin: 100px auto;
        }

        .item {
            height: 29px;
            line-height: 30px;
            text-align: center;
            border-bottom: 1px solid #aaa;
        }
    </style>
</head>
<body>
<div id="app">
    <div class="container" ref="container">
        <div class="item">{{loadText+"第"+pageNum+"页"}}</div>
        <div v-for="(item, index) in list" :key="index" class="item">{{item}}</div>
    </div>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
    new Vue({
        el: '#app',
        data: {
            scrollHeight: 0,
            list: [],
            loadText:"加载中...",
            pageSize:20,
            pageNum:1,
        },
        mounted() {
            this.initData();
            const container = this.$refs.container;
            //这里的定时是为了列表首次渲染后获取scrollHeight并滑动到底部。
            setTimeout(() => {
                this.scrollHeight = container.scrollHeight;
                container.scrollTo(0, this.scrollHeight);
            }, 10);
            container.addEventListener('scroll', (e) => {
                //这里的2秒钟定时是为了避免滑动频繁,节流
                setTimeout(() => {
                    if(this.list.length>=90){
                        this.loadText = "加载完成";
                        return;
                    }
                    //滑到顶部时触发下次数据加载
                    if (e.target.scrollTop == 0) {
                        //将scrollTop置为10以便下次滑到顶部
                        e.target.scrollTop = 10;
                        //加载数据
                        this.initData();
                        //这里的定时是为了在列表渲染之后才使用scrollTo。
                        setTimeout(() => {
                            e.target.scrollTo(0, this.scrollHeight - 30);//-30是为了露出最新加载的一行数据
                        }, 100);
                    }
                }, 2000);
            });
        },
        methods:{
            //初始数据
            initData() {
                for (var i = 20; i > 0; i--) {
                    this.list.unshift(i)
                }
                this.pageNum++;
            }
        }
    })
</script>
</body>
</html>
上一篇:iframe标签的使用


下一篇:js获取浏览器和屏幕的各种宽度高度