vue component: list automatically loops infinitely

 Foregoing: After using the vue-seamless-scroll plug-in, a blank phenomenon appeared at the bottom of the manually scrolled list. Some of the performances did not meet the project scenario, so I wrote a component for my own use. If anyone needs it, you can use it directly. If it’s not enough, please advise and don’t spray!

The main function:

  1. List automatically scrolls infinitely
  2. Move the mouse in to stop scrolling, move the mouse out to continue scrolling
  3. Do not scroll when the content to be scrolled does not fill up and exceeds the height of the container.
  4. Supports scrolling speed, single scrolling time interval, single scrolling height, three parameter control
  5. You can manually scroll the list yourself

Rendering:

 

Component code: 

<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>
  
  

Usage example: 

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>

Notes on use:

  1. By default, the autoScroll container occupies 100% of the width and height of the outer container. You need to add a container outside of autoScroll.
  2. Only when the parameters waitTIme and singleHeight exist at the same time can the effect of rolling animation interval execution appear. 
  3. The style uses sass. If there are any problems, you can remove or import sass.

Guess you like

Origin blog.csdn.net/qq_36764510/article/details/129821226