vue无限滚动列表

775 阅读5分钟

在大屏开发过程中,无限滚动列表是一个常用的需求,效果如下:

1709698720453.gif 在vue2和vue3的开发过程中会有一些区别:

  1. 如果是是在用vue3进行开发,那么则需要安装 vue3-seamless-scroll,比如当前的node版本是16.20.2, vue版本是3.2.47, vue3-seamless-scroll版本是2.0.1,直接上代码:
import { Vue3SeamlessScroll } from 'vue3-seamless-scroll'
<div class="handle-ontime-list">
    <div class="handle-ontime-list-header">
      <div class="handle-ontime-list-header-item" style="width: 7.2917vw">处理时间</div>
      <div class="handle-ontime-list-header-item" style="width: 27.9167vw">内容</div>
      <div class="handle-ontime-list-header-item" style="width: 5.5208vw">操作</div>
    </div>
    <vue3-seamless-scroll
      :step="0.6"
      :wheel="true"
      :hover="true"
      direction="up"
      count="infinite"
      :list="handledList"
      :limitScrollNum="6"
      class="handle-ontime-list-body"
    >
      <div
        v-for="(li, i) in handledList"
        :key="i"
        :class="'handle-ontime-list-row ' + (i >= 0 ? 'with-border' : '')"
        @click="openDetail(li)"
      >
        <div class="handle-ontime-list-item" style="width: 7.2917vw">
          {{ li.timeStr }}
        </div>
        <div class="handle-ontime-list-item" style="width: 27.9167vw">
          {{ li.detail }}
        </div>
        <div class="handle-ontime-list-item" style="width: 5.5208vw">
          {{ '查看详情' }}
        </div>
      </div>
    </vue3-seamless-scroll>
  </div>
  <style scoped lang="scss">
    .handle-ontime-list{
      width: 40.4688vw;
      height: 20.3vw;
      margin-left: 1.25vw;
      border-radius: 0.4167vw;
      background: rgba(0, 157, 255, 0.1);
      backdrop-filter: blur(2.6vw);
      box-shadow: inset 0px 0px 0.52vw 0px #009dff;
    }
    .handle-ontime-list-header{
      width: 100%;
      height: 2.0833vw;
      display: flex;
      justify-content: space-between;
      background: rgba(0, 157, 255, 0.5);
      border-top-left-radius: 0.4167vw;
      border-top-right-radius: 0.4167vw;
    }
    .handle-ontime-list-header-item{
      height: 1.9688vw;
      line-height: 1.9688vw;
      overflow: hidden;
      font-family: Source Han Sans CN;
      font-size: 0.9375vw;
      font-weight: bold;
      color: #ffffff;
    }
    .handle-ontime-list-body{
      width: 100%;
      height: 17.7083vw;
      overflow: hidden;
    }
    .handle-ontime-list-row{
      height: 1.9688vw;
      display: flex;
      justify-content: space-between;
      color: rgba(255, 255, 255, 1);
      cursor: pointer;
    }
    .handle-ontime-list-row:hover {
      background: linear-gradient(
        270deg,
        rgba(0, 255, 221, 0) 0%,
        rgba(0, 255, 221, 0.5) 47%,
        rgba(0, 255, 221, 0) 100%
      );
    }
    .with-border {
      border-top: 1px solid rgba(67, 152, 255, 0.2);
    }
    .handle-ontime-list-item {
      height: 1.9688vw;
      line-height: 1.9688vw;
      overflow: hidden;
      font-family: Source Han Sans CN;
      font-size: 0.8333vw;
      font-weight: 500;
      letter-spacing: 0;
    }
</style>

这个组件可以实现列表的自动无限循环滚动,鼠标悬浮停止滚动,手动滑动滚轮时可滚动。

  1. 如果是用vue2进行开发,对应的vue-seamless-scroll组件会有一个缺陷,就是无法手动滚动,这个问题在vue3版本的vue3-seamless-scroll组件中得到了解决,所以如果是在vue2开发无限滚动时需要手动滚动的功能时,就不能用vue-seamless-scroll了,此处推荐 "@david-j/vue-j-scroll": "^1.2.7",这个组件,使用时需要全局引入:
// main.js
import VueJScroll from '@david-j/vue-j-scroll'
Vue.use(VueJScroll)

// 组件直接用
<div class="schedule_list">
    <div class="schedule_list_title">
        <div style="flex: 0 0 20%">姓名</div>
        <div style="flex: 0 0 20%"></div>
        <div style="flex: 0 0 20%"></div>
        <div style="flex: 0 0 20%">床位号</div>
        <div style="flex: 0 0 20%">时间段</div>
    </div>
    <vue-j-scroll
        v-show="!noData"
        class="list-style"
        :data="scheduleList"
        :steep="0.5"
        scroll-direction="top"
        :is-roller="true"
        :roller-scroll-distance="50"
    >
        <div
            v-for="(item, index) in scheduleList"
            :key="'t' + index"
            class="list-item"
        >
            <div style="flex: 0 0 20%">{{ item.name }}</div>
            <div style="flex: 0 0 20%">{{ item.gradeName }}</div>
            <div style="flex: 0 0 20%">{{ item.className }}</div>
            <div style="flex: 0 0 20%" class="bigscreen_num">{{ item.bedName }}</div>
            <div style="flex: 0 0 20%" class="bigscreen_num">{{ item.time }}</div>
        </div>
    </vue-j-scroll>
</div>
<style lang="scss">
    .schedule_list{
        width: 95%;
        height: 9.8958vw;
        background: url('../../assets/screenimages/bold/bottombox.png') no-repeat;
        background-size: 100% 100%;
        font-size: 0.6771vw;
        .schedule_list_title {
            width: 100%;
            height: 2.2917vw;
            background: url('../../assets/screenimages/duletitleback.png') no-repeat;
            background-size: 100% 100%;
            display: flex;
            line-height: 2.2917vw;
            font-size: 0.8333vw;
            font-weight: 700;
            div{
                text-align: center;
            }
        }
    }
    .list-style{
        width: 100%;
        height: calc(100% - 2.5vw);
        overflow: hidden;
    }
    .list-item{
        display: flex;
        padding: 0.625vw 0.9375vw;
        font-size: 0.7292vw;
        div{
            text-align: center;
        }
        &:hover{
            background: radial-gradient(rgba(57, 127, 130, 1), rgba(57, 127, 130, 0));
        }
    }
</style>
  1. 自己写一个原生的无限滚动列表:
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>滚动列表</title>
</head>

<body>
    <div id="app">
        <p>{{message}}</p>
        <div class="schedule">
            <div class="schedule_list">
                <div class="schedule_list_title">
                    <div style="flex: 0 0 20%">姓名</div>
                    <div style="flex: 0 0 20%">排</div>
                    <div style="flex: 0 0 20%">班</div>
                    <div style="flex: 0 0 20%">床位号</div>
                    <div style="flex: 0 0 20%">时间段</div>
                </div>
    
                <div
                    ref="scheduleScroll"
                    class="schedule_list_data"
                    @mouseout="setScrollState(true)"
                    @mouseover="setScrollState(false)"
                >
                    <div v-for="(item, index) in scheduleList" :key="index" class="schedule_list_item">
                        <div style="flex: 0 0 20%">{{ item.name }}</div>
                        <div style="flex: 0 0 20%">{{ item.gradeName }}</div>
                        <div style="flex: 0 0 20%">{{ item.className }}</div>
                        <div style="flex: 0 0 20%" class="bigscreen_num">{{ item.bedName }}</div>
                        <div style="flex: 0 0 20%" class="bigscreen_num">{{ item.time }}</div>
                    </div>
                </div>
            </div>
        </div>
    </div>
</body>
<script>
    var app = new Vue({
        el: "#app",
        data: {
            message: "滚动列表",
            scheduleList: [
                {
                    name: "张三",
                    gradeName: "一年级",
                    className: "一班",
                    bedName: "101",
                    time: "10:00-12:00"
                },
                {
                    name: "李四",
                    gradeName: "一年级",
                    className: "一班",
                    bedName: "102",
                    time: "10:00-12:00"
                },
                {
                    name: "王五",
                    gradeName: "一年级",
                    className: "一班",
                    bedName: "103",
                    time: "10:00-12:00"
                },
                {
                    name: "赵六",
                    gradeName: "一年级",
                    className: "一班",
                    bedName: "104",
                    time: "10:00-12:00"
                },
                {
                    name: "孙七",
                    gradeName: "一年级",
                    className: "一班",
                    bedName: "105",
                    time: "10:00-12:00"
                },
                {
                    name: "周八",
                    gradeName: "一年级",
                    className: "一班",
                    bedName: "106",
                    time: "10:00-12:00"
                },
                {
                    name: "吴九",
                    gradeName: "一年级",
                    className: "一班",
                    bedName: "107",
                    time: "10:00-12:00"
                },
                {
                    name: "郑十",
                    gradeName: "一年级",
                    className: "一班",
                    bedName: "108",
                    time: "10:00-12:00"
                },
                {
                    name: "王十一",
                    gradeName: "一年级",
                    className: "一班",
                    bedName: "109",
                    time: "10:00-12:00"
                },
                {
                    name: "赵十二",
                    gradeName: "一年级",
                    className: "一班",
                    bedName: "110",
                    time: "10:00-12:00"
                },
                {
                    name: "孙十三",
                    gradeName: "一年级",
                    className: "一班",
                    bedName: "111",
                    time: "10:00-12:00"
                },
                {
                    name: "周十四",
                    gradeName: "一年级",
                    className: "一班",
                    bedName: "112",
                    time: "10:00-12:00"
                },
                {
                    name: "吴十五",
                    gradeName: "一年级",
                    className: "一班",
                    bedName: "113",
                    time: "10:00-12:00"
                },
            ],
            scrollSetInterval: null, // 滚动定时器
            scrollSetIntervalCurTop: 0, // 记录当前滚动的位置
        },
        methods: {
            setScrollState(val) {
                const node = this.$refs.scheduleScroll
                if (val) {
                    if (!this.scrollSetInterval) {
                    this.scrollSetInterval = setInterval(() => {
                        this.scrollSetIntervalCurTop += 2
                        node.scroll(0, this.scrollSetIntervalCurTop)
                        // 滑动到底部时自动滚回头部
                        if ((this.scrollSetIntervalCurTop + node.clientHeight) >= node.scrollHeight) {
                        this.scrollSetIntervalCurTop = 0
                        }
                    }, 120)
                    }
                } else {
                    clearInterval(this.scrollSetInterval)
                    this.scrollSetInterval = null
                    this.scrollSetIntervalCurTop = node.scrollTop
                }
            }
        },
        mounted() {
            this.setScrollState(true)
        }
    })
</script>
<style>
    .schedule {
        width: 600px;
        height: 200px;
        background: #043e40;
        color: #fff;
    }
    
    .schedule_list {
        width: 100%;
        height: 100%;
        /* background: url('http://troop.isuke.com.cn/static/img/bottombox.a9501974.png') no-repeat;
        background-size: 100% 100%; */
        font-size: 14px;
    }
    
    .schedule_list_title {
        width: 100%;
        height: 44px;
        background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAiEAAAAyCAYAAACKygubAAAAAXNSR0IArs4c6QAABABJREFUeF7t3UFrlFcUBuBzqiKWSmkpLaWpUirVtmgtQyYZRQyW0lLyO7Jz4cakZibeTJKF/8o/4K9wK7gpFBdCuQ3TGeh2cmaRZzYf3+olD/e8uXyXbybDhwABAgQIECCwAoGM1j5YQa5IAgQIECBA4JwLZGy1i7EVES8jXDlYB+ZAD+gBPaAHqnogBzs7lyIGC3uxV+Gex3xBWA/mwTyYh5mAPtAHy+2DvPH48eVz/jTIn0+AAAECBAisQCDXnjy58vVC8OuIcD8H4WE9mAfzMBPQB/pAHyy3D3LQ2ocr2PyIJECAAAECBM65QP7Q2kfn3MCfT4AAAQIECKxAIG8+fXH1s4h4ExGuHKwDc6AH9IAe0ANVPZCD3d2PIz6JiLfhysE6MAd6QA/oAT1Q1QN5e2+vT5wPAQIECBAgQKBUIEetfVqaKIwAAQIECBAgEBH97Zh+9ONDgAABAgQIECgVyDutfT5L7K/J/LUQ756H9TAfCPNgHsyDefD/8lRgWX2YG89Ovijd9ggjQIAAAQIECPx7HPPs5EsSBAgQIECAAIFqgfxpf/+r6lB5BAgQIECAAIHcaG0NAwECBAgQIECgWiAHz59fqw6VR4AAAQIECBDIe/vH1/9juBIRfy+guOdhPcwHwjyYB/NgHmYC+mApfZDD8fibiMsLO4934Z7HfEFYD+bBPJiHmYA+0AfL7YMcjMffnqLOFpcrD+vBPOgBPaAH9MDZ90D/2vYbTqUIECBAgAABAtUCOWztu+pQeQQIECBAgACBHPzZbmEgQIAAAQIECFQL5Ob46PvqUHkECBAgQIAAgVyfTH/EQIAAAQIECBCoFsjRZHq7OlQeAQIECBAgQKB/bfsdDAQIECBAgACBaoFcb+1udag8AgQIECBAgEA/jvkZAwECBAgQIECgWiA3JtNBdag8AgQIECBAgEB/O2YdAwECBAgQIECgWiBH7XhYHSqPAAECBAgQINDfjtnEQIAAAQIECBCoFug/YHevOlQeAQIECBAgQCA3J9P7GAgQIECAAAEC1QI5nEwfVIfKI0CAAAECBAj07wl5iIEAAQIECBAgUC2Qm61tRVxYyH0f7nnMF4T1YB7Mg3mYCegDfbDcPsjhuD2q3vnII0CAAAECBAjk6ODwFwwECBAgQIAAgWqB3Dg4/LU6VB4BAgQIECBAIIcHh7/1E57ZSZ/r6YkfBw7WgTnQA3pAD5xtD+SoHf1uL0aAAAECBAgQqBboX9v+h72uve7Z7nX58vVMQc/qAT3w/x7ob8dsV+985BEgQIAAAQIE+nGMTYh1QIAAAQIECJQL9B+w2/aY0GNCjwkdF+gBPaAH9EB1D3gSUr7vE0iAAAECBAh0gX8Auw/iROkL+g4AAAAASUVORK5CYII=') no-repeat;
        background-size: 100% 100%;
        display: flex;
        line-height: 44.0006px;
        font-size: 15.9994px;
        font-weight: 700;
    }
    .schedule_list_title > div{
        text-align: center;
    }
    .schedule_list_data{
        position: relative;
        height: calc(100% - 48px);
        overflow: auto;
    }
    .schedule_list_item{
        display: flex;
        padding: 12px 6px;
    }
    .schedule_list_item > div{
        text-align: center;
    }
    .schedule_list_data::-webkit-scrollbar {
        display: none;
    }
</style>

</html>