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

前言

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

解决方案

  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;
          });
        });
  }
}
相关推荐
Martin -Tang26 分钟前
vite和webpack的区别
前端·webpack·node.js·vite
迷途小码农零零发27 分钟前
解锁微前端的优秀库
前端
王解1 小时前
webpack loader全解析,从入门到精通(10)
前端·webpack·node.js
我不当帕鲁谁当帕鲁1 小时前
arcgis for js实现FeatureLayer图层弹窗展示所有field字段
前端·javascript·arcgis
那一抹阳光多灿烂1 小时前
工程化实战内功修炼测试题
前端·javascript
放逐者-保持本心,方可放逐2 小时前
微信小程序=》基础=》常见问题=》性能总结
前端·微信小程序·小程序·前端框架
毋若成4 小时前
前端三大组件之CSS,三大选择器,游戏网页仿写
前端·css
红中马喽4 小时前
JS学习日记(webAPI—DOM)
开发语言·前端·javascript·笔记·vscode·学习
Black蜡笔小新5 小时前
网页直播/点播播放器EasyPlayer.js播放器OffscreenCanvas这个特性是否需要特殊的环境和硬件支持
前端·javascript·html
秦jh_6 小时前
【Linux】多线程(概念,控制)
linux·运维·前端