封装一个自动循环滚动的列表

前言

在做数据大屏开发的过程中,经常出现需要对列表进行自动滚动的需求,为了个性化的实现各种需求,就封装了一个函数方法

解决方案

  1. 生成一个父元素,并给其固定的高度,通过$refs获取并赋值给container变量
  2. 获取一些关于container和其子元素的信息,如父容器高度(parentHeight)、滚动高度(scrollHeight)、第一个子元素的高度(childHeight)、子元素数量(childCount)、最大高度(maxHeight)、当前滚动位置(toHeight)和当前子元素索引(current),并初始化一个名为timer的计时器。
  3. 定义一个名为scrollCore的函数,该函数用于实现滚动效果。
  4. scrollCore函数中检查如果scrollHeight大于parentHeight,则创建一个定时器,以便每隔一段时间执行一次滚动操作。
  5. 在定时器中,scrollCore函数中逐渐增加容器的scrollTop属性,从而实现滚动效果。

一、匀速自动滚动,并实现方法可对多个列表复用

思路:通过对ref的命名,来实现在方法中使用for循环来对多个列表生效

js 复制代码
<template>
<div>
    <ul class="shiJianGaoJingContent_right" ref="scroll0">
        <li class="gaojing_info_list" v-for="(item, index) in scrollInfoList" :key="index">
        </li>
    </ul> 
</div>
</template>

<script>
export default {
  data() {
    return {
      scrollInfoList:[],
    };
  },
  methods: {
  // 列表自动滚动
    scrollFn() {
      let dataList = [];
      for (let i = 0; i < 1; i++) {
        let container = this.$refs["scroll" + i];
        let parentHeight = container.offsetHeight || 0;
        let scrollHeight = container.scrollHeight;
        let childHeight = container.firstChild.offsetHeight;
        let childCount = container.childNodes.length;
        let maxHeight = childHeight * childCount;
        let toHeight = container.scrollTop;
        let current = 0;

        dataList.push({
          parentHeight,
          scrollHeight,
          childHeight,
          childCount,
          maxHeight,
          toHeight,
          current,
          timer: null,
          timerMouseWheel: null,
        });
        function scrollCore() {
          {
            if (scrollHeight > parentHeight) {
              dataList[i].timer = setInterval(() => {
                let scrollStep = 1; // 每帧滚动的步长,可以适当调整

                if (container.scrollTop + parentHeight + 5 >= scrollHeight) {
                  // 如果滚动到底部,则将滚动位置重置为0
                  container.scrollTop = 0;
                  dataList[i].toHeight = 0;
                  dataList[i].current = 0;
                } else {
                  if (container.scrollTop < dataList[i].toHeight + dataList[i].childHeight) {
                    // 如果尚未滚动到目标位置,则逐帧更新滚动位置
                    container.scrollTop += scrollStep;
                  } else {
                    // 已滚动到目标位置,更新当前子项索引和子项高度
                    dataList[i].current = (dataList[i].current + 1) % dataList[i].childCount;
                    dataList[i].childHeight = container.childNodes[dataList[i].current].offsetHeight;
                    dataList[i].toHeight = container.scrollTop;
                  }
                }
              }, 2000 / 60); // 设置每秒更新60次,可根据需求调整
            }
          }
        }
        scrollCore()
      }
    },
    
  },
  mounted() {},
};
</script>

注:该方法在自动滚动到底部后会返回到顶部,循环滚动需要更改逻辑,在下方有循环的方法。

二、如何暂时关闭自动滚动

当我们想自己通过鼠标滚轮滚动该列表时,使该列表的自动滚动暂停,我们可以添加了一个名为mousewheel的事件监听器,以便在鼠标滚轮滚动时执行以下操作:

  1. 清除之前的定时器dataList[i].timerdataList[i].timerMouseWheel
  2. 更新dataList[i].toHeight为当前滚动位置,并将滚动位置设置为相同的值。
  3. 设置一个新的定时器dataList[i].timerMouseWheel,在一段时间后重新调用scrollCore函数,以恢复自动滚动效果。
ini 复制代码
 container.addEventListener("mousewheel", () => {
          this.$nextTick(() => {
            clearInterval(dataList[i].timer);
            clearInterval(dataList[i].timerMouseWheel);
            dataList[i].toHeight = container.scrollTop;
            container.scrollTop = container.scrollTop;
            dataList[i].timerMouseWheel = setTimeout(() => {
              scrollCore()
            }, 3000)

          });
        });

二、如何彻底关闭自动滚动

在上面的方法中会一直无限的滚动下去,那么我们如果想停下具体看某一项要把自动滚动关闭掉呢,我们可以通过将关闭方法写在container的某个事件中,并将该事件派发给container来实现

ini 复制代码
// 监听关闭自动滚动的事件
container.addEventListener("closeScroll", () => {
          this.$nextTick(() => {
            clearInterval(dataList[i].timer);
            clearInterval(dataList[i].timerMouseWheel);
            toHeight = container.scrollTop;
            container.scrollTop = container.scrollTop;
          });
        });
ini 复制代码
// 完整代码
// 关闭列表自动滚动
    closeListScroll(container) {
      // 创建一个新的 自定义 事件
      const closeScroll = new Event("closeScroll");
      container.dispatchEvent(closeScroll)
    },
// 列表自动滚动
    scrollFn() {
      let dataList = [];
      for (let i = 0; i < 1; i++) {
        let container = this.$refs["scroll" + i];
        let parentHeight = container.offsetHeight || 0;
        let scrollHeight = container.scrollHeight;
        let childHeight = container.firstChild.offsetHeight;
        let childCount = container.childNodes.length;
        let maxHeight = childHeight * childCount;
        let toHeight = container.scrollTop;
        let current = 0;

        dataList.push({
          parentHeight,
          scrollHeight,
          childHeight,
          childCount,
          maxHeight,
          toHeight,
          current,
          timer: null,
          timerMouseWheel: null,
        });
        function scrollCore() {
          {
            if (scrollHeight > parentHeight) {
              dataList[i].timer = setInterval(() => {
                let scrollStep = 1; // 每帧滚动的步长,可以适当调整

                if (container.scrollTop + parentHeight + 5 >= scrollHeight) {
                  // 如果滚动到底部,则将滚动位置重置为0
                  container.scrollTop = 0;
                  dataList[i].toHeight = 0;
                  dataList[i].current = 0;
                } else {
                  if (container.scrollTop < dataList[i].toHeight + dataList[i].childHeight) {
                    // 如果尚未滚动到目标位置,则逐帧更新滚动位置
                    container.scrollTop += scrollStep;
                  } else {
                    // 已滚动到目标位置,更新当前子项索引和子项高度
                    dataList[i].current = (dataList[i].current + 1) % dataList[i].childCount;
                    dataList[i].childHeight = container.childNodes[dataList[i].current].offsetHeight;
                    dataList[i].toHeight = container.scrollTop;
                  }
                }
              }, 2000 / 60); // 设置每秒更新60次,可根据需求调整
            }
          }
        }
        scrollCore()

        container.addEventListener("mousewheel", () => {
          this.$nextTick(() => {
            clearInterval(dataList[i].timer);
            clearInterval(dataList[i].timerMouseWheel);
            dataList[i].toHeight = container.scrollTop;
            container.scrollTop = container.scrollTop;
            dataList[i].timerMouseWheel = setTimeout(() => {
              scrollCore()
            }, 3000)

          });
        });
        container.addEventListener("closeScroll", () => {
          this.$nextTick(() => {
            clearInterval(dataList[i].timer);
            clearInterval(dataList[i].timerMouseWheel);
            toHeight = container.scrollTop;
            container.scrollTop = container.scrollTop;
          });
        });
      }
    },

通过如上代码 我们就可以通过调用closeListScroll()方法来关闭列表自动滚动,如我们想要关闭ref=scroll0列表的自动滚动

javascript 复制代码
// 示例 关闭ref=scroll0列表的自动滚动
// 某个方法中
clickSomeBtn(){
    ... //其他逻辑
    this.closeListScroll(this.$refs["scroll0"])
}

三、如何使自动滚动无限循环,使其头尾相连

思路:将一份数据复制为两份,在滚动到第二份与第一份重合的时候 立刻将滚动高度归位,这样从视觉效果上来看,就是无限滚动的效果 要实现这个效果,首先是我们需要将一份数据变为两份,最为简单的实现思路为直接将数据变为两份

ruby 复制代码
<li class="gaojing_info_list" v-for="(item, index) in [...scrollInfoList,...scrollInfoList]" :key="index">

但是这样的话,我们需要对所有的列表都进行更改,容易遗漏,不符合封装思路

于是我就想着通过DOM方法直接在封装函数中进行操作,实现思路为

  1. 使用DOM方法获取container的所有子元素,并将它们存储在名为children的变量中。
  2. 创建一个文档片段(Document Fragment)并将子元素逐个克隆并添加到文档片段中。
  3. 一次性将文档片段添加回container中,以提高性能。 当我们实现了克隆两份数据后,通过对container.scrollTop >= scrollHeight / 2的判断,来得到我们已经来到了第二页与初始位置重复的位置,在这个时候将滚动位置重置,在视觉上就会实现首尾相连无限滚动的效果
ini 复制代码
// 列表自动循环滚动
scrollFn() {
  let dataList = [];
  for (let i = 0; i < 1; i++) {
    let container = this.$refs["scroll" + i];
    console.log(container);
    // 使用 DOM 方法获取所有子元素
    let children = container.children;
    // 创建一个文档片段
    let fragment = document.createDocumentFragment();
    // 将子元素逐个重新插入到容器中
    for (let ind = 0; ind < children.length; ind++) {
      const child = children[ind].cloneNode(true);
      console.log(child, "child");
      fragment.appendChild(child);
    }
    // 一次性将文档片段添加回容器中
    this.$refs["scroll" + i].appendChild(fragment);
    let parentHeight = container.offsetHeight || 0;
    let scrollHeight = container.scrollHeight;
    let childHeight = container.firstChild.offsetHeight;
    let childCount = container.childNodes.length;
    let maxHeight = childHeight * childCount;
    let toHeight = container.scrollTop;
    let current = 0;

    dataList.push({
      parentHeight,
      scrollHeight,
      childHeight,
      childCount,
      maxHeight,
      toHeight,
      current,
      timer: null,
    });
    function scrollCore() {
      if (scrollHeight > parentHeight) {
        dataList[i].timer = setInterval(() => {
          let scrollStep = 1; // 每帧滚动的步长,可以适当调整

          if (container.scrollTop >= scrollHeight / 2) {
            // 滚动到与第一行重复的位置后 重置
            container.scrollTop = 0;
            dataList[i].toHeight = 0;
            dataList[i].current = 0;
          } else {
            if (
              container.scrollTop <
              dataList[i].toHeight + dataList[i].childHeight
            ) {
              // 如果尚未滚动到目标位置,则逐帧更新滚动位置
              container.scrollTop += scrollStep;
            } else {
              // 已滚动到目标位置,更新当前子项索引和子项高度
              dataList[i].current =
                (dataList[i].current + 1) % dataList[i].childCount;
              dataList[i].childHeight =
                container.childNodes[dataList[i].current].offsetHeight;
              dataList[i].toHeight = container.scrollTop;
            }
          }
        }, 2000 / 60); // 设置每秒更新60次,可根据需求调整
      }
    }
    scrollCore();

    container.addEventListener("mousewheel", () => {
      this.$nextTick(() => {
        clearInterval(dataList[i].timer);
        clearInterval(dataList[i].timerMouseWheel);
        dataList[i].toHeight = container.scrollTop;
        container.scrollTop = container.scrollTop;
        dataList[i].timerMouseWheel = setTimeout(() => {
          scrollCore();
        }, 3000);
      });
    });
     container.addEventListener("closeScroll", () => {
          this.$nextTick(() => {
            clearInterval(dataList[i].timer);
            clearInterval(dataList[i].timerMouseWheel);
            toHeight = container.scrollTop;
            container.scrollTop = container.scrollTop;
          });
        });
  }
}
相关推荐
JIngJaneIL14 分钟前
助农惠农服务平台|助农服务系统|基于SprinBoot+vue的助农服务系统(源码+数据库+文档)
java·前端·数据库·vue.js·论文·毕设·助农惠农服务平台
云外天ノ☼18 分钟前
待办事项全栈实现:Vue3 + Node.js (Koa) + MySQL深度整合,构建生产级任务管理系统的技术实践
前端·数据库·vue.js·mysql·vue3·koa·jwt认证
一位搞嵌入式的 genius22 分钟前
前端实战开发(三):Vue+Pinia中三大核心问题解决方案!!!
前端·javascript·vue.js·前端实战
塞纳河畔的歌23 分钟前
保姆级教程 | 麒麟系统安装Edge浏览器
前端·edge
前端.火鸡24 分钟前
Vue 3.5 新API解析:响应式革命、SSR黑科技与开发体验飞跃
javascript·vue.js·科技
多睡觉觉24 分钟前
数据字典:从"猜谜游戏"到"优雅编程"的奇幻之旅
前端
嗝屁小孩纸27 分钟前
开发集成热门小游戏(vue+js)
前端·javascript·vue.js
赛博切图仔33 分钟前
深入理解 package.json:前端项目的 “身份证“
前端·javascript
UIUV36 分钟前
JavaScript 学习笔记:深入理解 map() 方法与面向对象特性
前端·javascript·代码规范
太平洋月光1 小时前
MJML邮件如何随宽度变化动态切换有几列📮
前端·css