vue文字间歇无缝向上滚动

772 阅读1分钟

其实,这种用在vue项目中的需求跟原生js的实现方法基本一致,且实现的方法有多种,今天就单拎出来一种实现方法吧:

<template>
  <div class="marquee-container">
    <!-- 横向无缝滚动效果 -->
    <div v-if="horizontalZ" class="wrap">
      <div id="box">
        <div id="marquee">{{ text }}</div>
        <div id="copy" />
      </div>
      <div id="node">{{ text }}</div>
    </div>
    <!-- 纵向无缝滚动效果 -->
    <div v-else class="marquee-wrap" ref="marqueeListRef">
      <ul class="marquee-list">
        <li v-for="(item, index) in dataList" :key="index">{{ item }}</li>
      </ul>
    </div>
  </div>
</template>
<script>
export default {
  name: 'Marquee',
  props: {
    /** 父组件传入数据, 数组形式 [ "长得帅不是错","错的是一直这么帅下去"] */
    lists: {
      type: Array,
      require: true,
    },
    /**
     * horizontalZ:true :横向
     * horizontalZ:false :纵向
    */
    horizontalZ: {
      type: Boolean,
      default: false,
    },
  },
  data() {
    return {
      text: '',
      timer: null,
      dataList: [],
      speed: 20,
      delay: 1500,
      liHeight: '',
    };
  },
  mounted() {
    this.initData();
    this.$nextTick(() => {
      this.scrollArea = this.$refs.marqueeListRef;
      let li = this.scrollArea.getElementsByTagName("li");
      this.liHeight = li[0].offsetHeight;
      this.scrollArea.scrollTop = 0;
      this.scrollArea.innerHTML += this.scrollArea.innerHTML;
      this.dataList.length > 1 && setTimeout(this.startScroll, this.delay);
    })
  },
  updated() {
    if (this.horizontalZ) {
      this.move();
    }
  },
  destroyed() {
    clearInterval(this.timer);
  },

  methods: {
    move() {
      // 获取文字text 的计算后宽度 (由于overflow的存在,直接获取不到,需要独立的node计算)
      // eslint-disable-next-line prefer-destructuring
      const width = document.getElementById('node').getBoundingClientRect().width;
      const box = document.getElementById('box');
      const copy = document.getElementById('copy');
      copy.innerText = this.text; // 文字副本填充
      let distance = 0; // 位移距离
      // 设置位移
      this.timer = setInterval(() => {
        // eslint-disable-next-line operator-assignment
        distance = distance - 1;
        // 如果位移超过文字宽度,则回到起点
        if (-distance >= width) distance = 40;
        // eslint-disable-next-line prefer-template
        box.style.transform = 'translateX(' + distance + 'px)';
      }, 20);
    },
    startScroll() {
      this.timer = setInterval(this.scrollUp, this.speed);
      this.scrollArea.scrollTop++;
    },
    scrollUp() {
      if (this.scrollArea.scrollTop % this.liHeight == 0) {
        clearInterval(this.timer);
        setTimeout(this.startScroll, this.delay);
      } else {
        this.scrollArea.scrollTop++;
        if (this.scrollArea.scrollTop >= this.scrollArea.scrollHeight / 2) {
          this.scrollArea.scrollTop = 0;
        }
      }
    },
    initData() {
      if (!this.horizontalZ) {
        this.dataList = this.lists;
        this.timer = setInterval(this.scrollAnimate, 1500);
      } else {
        for (let i = 0; i < this.lists.length; i++) {
          this.text += ' ' + this.lists[i];
        }
      }
    }
  },
};
</script>
<style lang="scss" scoped>
.marquee-container {
  color: #fff;
  .wrap {
    width: 80%;
    height: 40px;
    border-radius: 20px;
    background: rgba($color: #000000, $alpha: 0.6);
    margin: 0 auto;
    overflow: hidden;
    #box {
      width: 80000%;
      line-height: 40px;
      div {
        float: left;
      }
      #marquee {
        margin: 0 20px;
      }
    }
    #node {
      position: absolute;
      z-index: -999;
      top: -999999px;
    }
  }
  .marquee-wrap {
    width: 80%;
    height: 40px;
    border-radius: 20px;
    background: rgba($color: #000000, $alpha: 0.6);
    margin: 0 auto;
    overflow: hidden;
    .marquee-list {
      margin: 0;
      padding: 0;
      li {
        width: 100%;
        height: 100%;
        text-overflow: ellipsis;
        overflow: hidden;
        white-space: nowrap;
        padding: 0 20px;
        list-style: none;
        line-height: 40px;
        text-align: center;
        box-sizing: border-box;
      }
    }
  }
}
</style>