vue-通过补全动画关键帧实现首尾相连的文本滚动

1,013 阅读2分钟

前言

近因为工作需要得整个滚动播放的公告栏,上网找了一会儿也没找到什么合适的,故开始琢磨还有没有其他方法实现这个功能。

里先贴一下网上传的比较广的方法(样式就不贴了,大同小异):

<template>
  <div class="wrap">
    <div ref="box" class="box">
      <div ref="marquee" class="marquee">{{text}}</div>
      <div ref="copy" class="copy"></div>
    </div>
    <div ref="node" class="node">{{text}}</div>
  </div>
</template>
<script>
export default {
  name : 'Marquee',
  props: ['lists'], // 父组件传入数据, 数组形式 [ "连雨不知春去","一晴方觉夏深"]
  data () {
    return {
      text: '' // 数组文字转化后的字符串
    }
  },
  methods: {
    move () {
      // 获取文字text 的计算后宽度  (由于overflow的存在,直接获取不到,需要独立的node计算)
      let width = this.$refs.node.getBoundingClientRect().width
      this.$refs.copy.innerText = this.text // 文字副本填充
      let distance = 0 // 位移距离
      // 设置位移
      setInterval(() => {
        distance = distance - 1
        // 如果位移超过文字宽度,则回到起点
        if (-distance >= width) {
          distance = 16
        }
        this.$refs.box.style.transform = 'translateX(' + distance + 'px)'
      }, 20)
    }
  },
  // 把父组件传入的arr转化成字符串
  mounted: function () {
    for (let i = 0; i < this.lists.length; i++) {
      this.text += ' ' + this.lists[i]
    }
  },
  // 更新的时候运动
  updated: function () {
    this.move()
  }
}
</script>

致思路就是用两个相同的内容,每隔20ms向X轴负方向移动1px,通过滚动播放以达到这段文字首尾相接的显示效果。

然首尾相接的效果是通过两条相同内容达到,而且动画的效果并不复杂,那换一种思路应该也可以实现,于是我想到了CSS动画---“丝般顺滑”。


通过CSS动画实现该效果

先贴代码(无关的样式就不贴了):

<template>
  <div>
    <div class="rollWrapper">
        <div class="textItem">
          <span class="textContent" :style="{width:stringLength+'px'}">{{text}}</span>
          <span class="textContent" ref="node">{{text}}</span>
        </div>
    </div>
  </div>
</template>

<script>
  export default {
    name: 'roll',
    data() {
      return {
        text: "幸福,往往是摸得透澈,而堇业的心却常常隐藏。",         
        stringLength:"",
      }
    },
    methods: {
      addKeyframesRule() {
        let ss = document.styleSheets,
          rule, i, x;
        for (i = ss.length-1; i >= 0; --i) {
          if (ss[i].cssRules) {
            for (x = ss[i].cssRules.length - 1; x >= 0; x--) {
              rule = ss[i].cssRules[x];
              if(rule.name && rule.name.includes('textScroll') && rule.type === 7){
                  rule.appendRule('to {transform: translateX(-' + this.stringLength + 'px);}');
                  return rule;
              }
            }
          }
        }
        return null
      },
    },
    mounted() {
      this.stringLength = String(this.$refs.node.getBoundingClientRect().width)
      this.addKeyframesRule()
    }
  }
</script>

<style scoped>
 .textContent{
    /* 此处的padding是为了达到无缝切换的效果,不加导致的问题我说不太清,大伙可以试试 */
    padding-right: 320px;
  }
  .textItem {
    animation: textScroll linear 15s infinite;
  }
  @keyframes textScroll {
    from {
      transform: translateX(0)
    }
    /*to的内容由方法赋予*/
  }

</style>

路很简单,这里直接说重点:获取text中文本的长度赋予stringLength后,通过动画名寻找并使用stringLength补全关键帧缺失的to{}(或者说是100%{})内容,完成css动画(当然也支持hover暂停= =)。

后首尾相连的滚动文本框就简简单单的完成了(可以应付多数情况,当然存在的问题也不少)。

预览

20210319-145157 00_00_00-.gif


相比开头方法的优缺点

优点

点没什么特别能说的,就是单纯的css动画的优点,比如能够被浏览器优化、能够使用硬件提升性能这种,反正就是丝滑

缺点

点包括但不限于组件本身的一些bug(比如代码中提到的需要一个padding)

此之外比较要命的一点是:在text的内容不固定时,stringLength的值也会动态变化,而动画的持续时间是不会变的---也就是说如果文本内容突然变得很长或/很短,滚动的速度就会相应变得很快/很慢。(当然也可以通过跟上面相似的方法修改动画的持续时间,但那么麻烦的话还不如就用js动画算了= =)

写点别的

因为我本来也是个小白,对于实现功能的思路还比较欠缺,不过现在这个写法又能满足需求代码量又少,所以就记录一下,这种简单文本滚动播放的组件以后说不定还能用得到,到时候再来贴一份。。。

= w = 感谢你能看到这里,如果能成为谁的参考,我就不算白码这点字,当然如果有幸帮上了谁的忙,那就是最值得我高兴的事情了。

工作固然重要,不过一定要持续学习。

生活可以很艰苦,但永远不要让好奇心蒙尘。