vue组件实现列表自动无限循环的方法

 更新时间:2023年11月14日 09:35:55   作者:The ever Boy  
最近刚好有个功能需要实现列表的无限循环滚动,这篇文章主要给大家介绍了关于vue组件实现列表自动无限循环的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考下

前述:

用过vue-seamless-scroll插件,手动滚动列表到底部出现了空白现象,某些表现不符合项目场景,故自己写了一个自己用的组件,如果有人需要可以直接拿去用,如有不足请指教勿喷!

主要功能:

  • 列表自动无限循环滚动
  • 鼠标移入停止滚动,移出继续滚动
  • 待滚动内容高度未铺满并超过容器高度时不滚动
  • 支持滚动速度、单次滚动时间间隔、单次滚动高度,三个参数控制
  • 可自己手动滚动列表

效果图 :

组件代码: 

<template>
  <div class="scroll-outer" ref="outer" @mouseover="onMouseover" @mouseleave="onMouseleave">
    <div class="scroll-inner-box" ref="scrollBox">
      <div class="scroll-item-box" ref="scrollItemBox">
        <slot></slot>
      </div>
      <div v-if="showSecond" class="scroll-item-box">
        <slot></slot>
      </div>
    </div>
  </div>
</template>
  <script>
export default {
  name: "my-auto-scroll",
  props: {
    list: {
      type: Array,
      default: () => [
        { name: "张三1" },
        { name: "张三2" },
        { name: "张三3" },
        { name: "张三4" },
        { name: "张三5" },
        { name: "张三6" },
        { name: "张三7" },
        { name: "张三8" },
        { name: "张三9" },
        { name: "张三10" },
      ],
    },
    speed: {
      type: Number,
      default: 0.1,
    },
    //滚动作单步运动时的单纯运动距离
    singleHeight: {
      type: Number,
      default: 0,
    },
    //单步运动的时间间隔
    waitTime: {
      type: Number,
      default: 0,
    },
  },
  data() {
    return {
      rafId: null,
      y: 0,
      showSecond: false,
      controleHeight: 0,
    };
  },
  watch: {
    list: {
      handler(newVal) {
        var that = this;
        this.$nextTick(() => {
          console.log(newVal);
          if (newVal && newVal.length > 0) {
            let scrollBox = that.$refs.scrollBox;
            let outer = that.$refs.outer;

            if (this.myReq) {
              cancelAnimationFrame(this.myReq);
            }
            // 开启动画
            if (this.canRun()) this.reqAnimationFrame();
            // this.reqAnimationFrame();
            // 手动滚动到底部时滚动条重置到最上边,同时滚动盒子重置为top:0
            outer.addEventListener("scroll", function () {
              if (
                outer.scrollTop + outer.clientHeight + 4 >=
                outer.scrollHeight
              ) {
                outer.scrollTop = 0;
                that.y = 0;
                scrollBox.style.top = 0;
              }
            });
          }
        });
      },
      deep: true,
      immediate: true,
    },
  },
  mounted() {
    window.addEventListener("resize", this.listenResizeFn);
  },
  methods: {
    listenResizeFn() {
      cancelAnimationFrame(this.myReq);
      if (this.canRun()) this.reqAnimationFrame();
    },
    onMouseover() {
      clearTimeout(this.timer);
      cancelAnimationFrame(this.myReq);
    },
    onMouseleave() {
      if (this.canRun()) this.reqAnimationFrame();
    },
    canRun() {
      let scrollItemBox = this.$refs.scrollItemBox;
      let scrollBox = this.$refs.scrollBox;
      let outer = this.$refs.outer;
      // 开启动画条件:滚动盒子(scrollBox)高度高于外层容器(outer)高度
      if (outer.offsetHeight >= scrollItemBox.offsetHeight) {
        this.showSecond = false;
        outer.scrollTop = 0;
        this.y = 0;
        scrollBox.style.top = 0;
        return false;
      } else {
        this.showSecond = true;
        return true;
      }
    },
    //获取dom元素的高度:content+padding+margin+border
    getComputedHeight(dom) {
      let computedStyle = getComputedStyle(dom);

      let computedHeight =
        dom.offsetHeight +
        parseFloat(computedStyle.marginTop) +
        parseFloat(computedStyle.marginBottom);
      return computedHeight;
    },
    reqAnimationFrame() {
      //外层容器
      let outer = this.$refs.outer;
      //滚动盒子
      let scrollBox = this.$refs.scrollBox;
      //滚动盒子下边的第一个scroll-item-box,
      let scrollItemBox = this.$refs.scrollItemBox;

      //滚动速度
      this.speed = this.speed > 1 ? 1 : this.speed < 0 ? 0.1 : this.speed;

      //取第一个scrollItemBox高度
      let definedHeight = this.getComputedHeight(scrollItemBox);
      //持续滚动
      this.y = this.y + this.speed;
      scrollBox.style.top = -this.y + "px";

      //====添加滚动间隔控制====开始
      if (this.singleHeight >= 20 && this.waitTime > 500) {
        if (this.controleHeight >= this.singleHeight) {
          cancelAnimationFrame(this.myReq);
          this.controleHeight = 0;
          this.timer = setTimeout(() => {
            if (this.canRun) this.reqAnimationFrame();
          }, this.waitTime);
          return;
        } else {
          // 一次移动高度未达到指定距离继续执行动画
          this.controleHeight += this.speed;
        }
      }
      //====添加滚动间隔控制====结束

      //当滚动到第一个scroll-item-box高度时scrollBox重置为top:0,视觉上是无缝滚动
      if (this.y >= definedHeight) {
        this.y = 0;
      }
      this.myReq = window.requestAnimationFrame(this.reqAnimationFrame);
    },
  },
  destroyed() {
    window.removeEventListener("resize", this.listenResizeFn);
    cancelAnimationFrame(this.myReq);
    if (this.timer) clearTimeout(this.timer);
  },
};
</script>
  <style lang="scss">
.scroll-outer {
  height: 100%;
  overflow-x: hidden;
  position: relative;
  &::-webkit-scrollbar {
    width: 0.3vw;
  }
  &:hover::-webkit-scrollbar-track {
    -webkit-box-shadow: inset 0 0 0.1vw rgba(0, 0, 0, 0.3);
    border-radius: 0.1vw;
    background-color: #295099;
    opacity: 1;
    // display: none;
  }
  &:hover::-webkit-scrollbar-thumb {
    opacity: 1;
    border-radius: 0.1vw;
    -webkit-box-shadow: inset 0 0 0.1vw rgba(0, 0, 0, 0.3);
    background-color: #0ba9ea;
  }
}
.scroll-inner-box {
  height: auto;
  position: absolute;
  width: 100%;
  top: 0;
  left: 0;
}
</style>
  
  

使用示例: 

import autoScroll from "@/components/autoScroll";
<autoScroll :list="list" :speed="0.5" :waitTime="2000" :singleHeight="100">
            <div class="t-item" v-for="(item,index) in list" :key="index">
              <div class="tvalue" style="flex: 0 0 30%;">{{ item.jgjc }}</div>
              <span class="tvalue">{{ item.total||'--' }}</span>
              <span class="tvalue" style="color:#0FCBDE">{{ item.ypc||'--' }}</span>
              <span class="tvalue" style="color:#F15730">{{ item.zlz||'--' }}</span>
              <span class="tvalue" style="color:#17DB68">{{ item.zlwc||'--' }}</span>
            </div>
          </autoScroll>

使用注意:

  • autoScroll容器默认是占外层容器宽高百分百,要自己在autoScroll外层加个容器
  • 参数waitTIme和singleHeight同时存在,才能出现滚动动画间隔执行的效果 
  • 样式用了sass,如果有问题可以去掉或者导入sass

总结 

到此这篇关于vue组件实现列表自动无限循环的文章就介绍到这了,更多相关vue列表自动无限循环内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 详解Vuex中getters的使用教程

    详解Vuex中getters的使用教程

    在Store仓库里,state就是用来存放数据。如果很多组件都使用这个过滤后的数据,我们是否可以把这个数据抽提出来共享?这就是getters存在的意义。我们可以认为,getters是store的计算属性。本文将具体介绍一下getters的使用教程,需要的可以参考一下
    2022-01-01
  • vue proxytable代理根路径的同时增加其他代理方式

    vue proxytable代理根路径的同时增加其他代理方式

    这篇文章主要介绍了vue proxytable代理根路径的同时增加其他代理方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-04-04
  • vue+webpack实现异步加载三种用法示例详解

    vue+webpack实现异步加载三种用法示例详解

    这篇文章主要介绍了vue+webpack实现异步加载的三种用法,文中给大家提到了vue+webpack实现异步组件加载的代码,非常不错,具有参考借鉴价值,需要的朋友参考下吧
    2018-04-04
  • Vue.js绑定HTML class数组语法错误的原因分析

    Vue.js绑定HTML class数组语法错误的原因分析

    Vue.js绑定HTML class数组语法错误有哪些原因导致的呢,该如何解决呢?下面小编给大家分享Vue.js绑定HTML class数组语法错误的原因分析,感兴趣的朋友一起看看吧
    2016-10-10
  • vue中可以绑定多个事件吗

    vue中可以绑定多个事件吗

    这篇文章主要介绍了vue中可以绑定多个事件吗,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-10-10
  • Vue项目中使用v-show和v-if指令以及原理、区别和适用场景说明

    Vue项目中使用v-show和v-if指令以及原理、区别和适用场景说明

    Vue中v-show通过display属性控制显示隐藏,元素始终存在DOM,适合频繁切换;v-if条件为false时移除元素,适合初始条件不成立的情况,性能上首次渲染可能更优,但切换频繁时v-show更高效,其他方式包括绑定style/class、计算属性及第三方动画库
    2025-07-07
  • Vue.js常用指令的使用小结

    Vue.js常用指令的使用小结

    这篇文章主要介绍了Vue.js常用指令的使用,非常不错,具有参考借鉴价值,需要的朋友可以参考下
    2017-06-06
  • vite如何实现构建vue3新项目

    vite如何实现构建vue3新项目

    这篇文章主要介绍了vite如何实现构建vue3新项目方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2025-05-05
  • Vue.js中的extend绑定节点并显示的方法

    Vue.js中的extend绑定节点并显示的方法

    在本篇内容里小编给大家整理了关于Vue.js中的extend绑定节点并显示的方法以及相关知识点,需要的朋友们学习下。
    2019-06-06
  • vue3使用jsx语法详解

    vue3使用jsx语法详解

    在绝大多数情况下,Vue 推荐使用模板语法来创建应用,然而在某些使用场景下,我们真的需要用到 JavaScript 完全的编程能力,这时JSX或渲染函数就派上用场了,下面就来介绍一下如何使用
    2026-02-02

最新评论