Vue 使用JS 实现一个文字横向无限滚动

685 阅读1分钟

上图 gif有问题丢帧,不是组件有这样的问题

1.gif

组件代码

<template>
  <div class="textScroll" @mouseenter="mouseHover" @mouseleave="mouseLevel">
    <div class="wrapper" ref="wrapper">
      <div ref="marquee" style="text-align: left;">
        <span class="text-content" ref="marqueeContent">
          <slot></slot>
        </span>
      </div>
    </div>
  </div>
</template>

<script>
export default {
  // 组件参数 接收来自父组件的数据
  props: {
    //是否从尾部开始滚动 默认从元素开头开始
    startFromTheEnd: {
      type: Boolean,
      default: true
    },
    //滚动速度越小越快
    scrollSpeed: {
      type: Number,
      default: 25
    },
    //是否开启hover时暂停滚动 默认开启
    isHoverStop: {
      type: Boolean,
      default: true
    },
    //滚动开始之前延迟多少秒 默认不延迟
    delay: {
      type: Number,
      default: 0
    }
  },
  // 局部注册的组件
  components: {},
  // 组件状态值
  data() {
    return {
      timer: null,
      isPlay: null, //是否可滚动
      nowPosition: 0//当前位置
    }
  },
  created() {
  },
  mounted() {
    this.init()
  },
  // 计算属性
  computed: {},
  // 侦听器
  watch: {},
  // 组件方法
  methods: {
    move(startPosition) {
      let wrapper = this.$refs.wrapper
      let wrapperWidth = wrapper.getBoundingClientRect().width
      let marquee = this.$refs.marquee
      // marquee.style.transform = 'translateX(' + this.startFromTheEnd ? 0 : wrapperWidth + 'px)'
      let marqueeContent = this.$refs.marqueeContent //外层容器
      let contentWidth = marqueeContent.getBoundingClientRect().width //容器宽度
      let distance = startPosition || (this.startFromTheEnd ? 0 : wrapperWidth) //滚动开始位置
      this.timer = setInterval(() => {
        //当文字移动超出wrapper的左侧时 则归位到尾部
        if (marquee.getBoundingClientRect().x + contentWidth < wrapper.getBoundingClientRect().x) {
          distance = wrapperWidth
        }
        distance--
        this.nowPosition = distance //时刻保存当前滚动位置方便后续暂停后使用
        marquee.style.transform = 'translateX(' + distance + 'px)'
      }, this.scrollSpeed)
    },
    init() {
      let wrapper = this.$refs.wrapper
      let content = this.$refs.marqueeContent
      let wrapperWidth = wrapper.getBoundingClientRect().width
      let contentWidth = content.getBoundingClientRect().width
      this.isPlay = contentWidth > wrapperWidth
      //如果子元素宽度超过父元素则进行滚动  加入延迟时间
      setTimeout(() => {
        this.isPlay && this.move()
      }, this.delay * 1000)
    },
    mouseHover() {
      //暂停
      if (this.isHoverStop && this.isPlay) {
        clearInterval(this.timer)
      }
    },
    mouseLevel() {
      //启动
      if (this.isHoverStop && this.isPlay) {
        this.move(this.nowPosition) //重新激活定时器并传入当前位置
      }
    }
  },
  beforeDestroy() {
    //组件即将销毁之前将定时器清空
    this.isPlay = false
    this.timer && clearInterval(this.timer)
    this.timer = null
  }
}
</script>

<style lang="scss" scoped>
.textScroll {
  overflow: hidden;
  width: 100%;
  .text-content {
    white-space: nowrap;
  }
}
</style>

使用方式

<!--父元素固定宽度 子元素字体样式可以继承父元素 可配置延迟 滚动速度 是否hover悬停 -->
<div style="width: 200px;border:1px solid red;color: red">
  <textScrolling  >
    哈哈哈哈嘻嘻嘻啊啊啊啊啊啊啊啊
  </textScrolling>
  <textScrolling>
    没超过父元素是不会滚的
  </textScrolling>
</div>