【vue自定义组件】实现大屏数字滚动翻转效果

8,482 阅读2分钟

小知识,大挑战!本文正在参与“  程序员必备小知识  ”创作活动

本文同时参与 「掘力星计划」  ,赢取创作大礼包,挑战创作激励金

本文参考 汪图南 大佬的 《你可能需要这样的大屏数字滚动效果》

前言

佛祖保佑, 永无bug。Hello 大家好!我是海的对岸!

实际项目中并没有用到这个,但是这个也算是比较常见的功能,趁此机会,记录一个

实现思路

  1. 首先要有个具体的值(value),比如这个值是102346
  2. 然后 这个组件一加载,显示出来的效果要从0跳动到102346
  3. 这个跳动的时间time,我们可以控制,可以让它跳快点,也可以让它慢慢跳

第二步拆开来,用代码来表达,就是“

  • a. 设置一个步进值(增量)step,
  • b. 整个计时器time,设置一个开始值startstart 和 传过来的具体值(value)比较,startvalue小,继续使用计时器time,给start加上增量step,再比较startvalue的大小,比到startvalue大的时候,结束

具体实现

效果

comRollNumber2.gif

代码实现

// 文字变化效果
      numberGrow (ele) {
        // debugger;
        //【这里调速度 1 ,步进值, 通俗地讲,就是每次跳的时候,增加的一个增量】
        let step = parseInt((this.value * 100) / (this.time * 1000));
        // 设置当前值(这个值是计时器持续运行时,每次页面上显示的跳动值,不是最终的那个具体值)
        let current = 0
        // 设置开始值
        let start = 0
        // 设置定时器,用来反复横跳的,哈哈哈
        let t = setInterval(() =>{
          // 每次增加一点步进值
          start += step
          // 开始值大于传过来的的值,说明 到点了,不用 继续横跳了
          if (start > this.value) {
            clearInterval(t)
            // 把穿过的值赋给start,结束
            start = this.value
            // 清掉计时器
            t = null
          }
          if(start == 0){
            start = this.value;
            clearInterval(t)
          }
          // 当前值等于开始值,那就结束
          if (this.value === 0) {
          return
          }
          current = start
          // 正则
          ele.innerHTML = current.toString().replace(/(\d)(?=(?:\d{3}[+]?)+$)/g, '$1,')
        }, this.time * 100)  // 【这里调速度 2, 通俗地讲,这里是页面上,肉眼能看到的跳动频率】
        // 本来想设置成 秒 *1000的,但是实在太慢了,就改成 *100了
      }

完整代码:

<template>
  <div class="number-grow-warp">
    <span ref="numberGrow" :data-time="time" class="number-grow" :data-value="value">0</span>
  </div>
  </template>
  <script>
  export default {
    props: {
      value: {
        type: Number, // 具体数值
        default() {
          return 720;
        },
      },
      time: {
        type: Number, // 从0-具体数值之间变化的速度,单位秒
        default() {
          return 2;
        },
      },
    },
    data() {
      return {
      }
    },
    mounted() {
      this.numberGrow(this.$refs.numberGrow);  // 取消注释--查看效果
    },
    methods: {
      // 文字变化效果
      numberGrow (ele) {
        // debugger;
        //【这里调速度 1 ,步进值, 通俗地讲,就是每次跳的时候,增加的一个增量】
        let step = parseInt((this.value * 100) / (this.time * 1000));
        // 设置当前值(这个值是计时器持续运行时,每次页面上显示的跳动值,不是最终的那个具体值)
        let current = 0
        // 设置开始值
        let start = 0
        // 设置定时器,用来反复横跳的,哈哈哈
        let t = setInterval(() =>{
          // 每次增加一点步进值
          start += step
          // 开始值大于传过来的的值,说明 到点了,不用 继续横跳了
          if (start > this.value) {
            clearInterval(t)
            // 把穿过的值赋给start,结束
            start = this.value
            // 清掉计时器
            t = null
          }
          if(start == 0){
            start = this.value;
            clearInterval(t)
          }
          // 当前值等于开始值,那就结束
          if (this.value === 0) {
          return
          }
          current = start
          // 正则
          ele.innerHTML = current.toString().replace(/(\d)(?=(?:\d{3}[+]?)+$)/g, '$1,')
        }, this.time * 100)  // 【这里调速度 2, 通俗地讲,这里是页面上,肉眼能看到的跳动频率】
        // 本来想设置成 秒 *1000的,但是实在太慢了,就改成 *100了
      }
    },
  }
  </script>
  <style scoped>
  .number-grow-warp{
  transform: translateZ(0);
  }
  .number-grow {
  display: block;
  }
  </style>

因为这是一个组件,我们来引用一下

<template>
  <div>
    <module :value="102346"  :time="2"/>
  </div>
</template>

<script>
// 旋转展示数值组件
import module from './../../components/comRollNumber'
export default {
  name: 'test',
  components: {
    module,
  },
  data() {
    return {
    }
  },
  methods: {
  },
  mounted() {
  },
}
</script>

<style scope>
</style>

强化版

强化版用到了 css中的transform: translate(-50%,-50%),让跳过的过程更加丝滑

效果:

comRollNumber.gif

原理大同小异

直接展示代码

<template>
  <div class="chartNum">
     <h3 class="orderTitle">XX模块展示:</h3>
     <div class="box-item">
      <li :class="{'number-item': !isNaN(item), 'mark-item': isNaN(item) }"
       v-for="(item,index) in orderNum"
       :key="index">
        <span v-if="!isNaN(item)">
         <i ref="numberItem" :myIndex="index" :myValue="item">0123456789</i>
        </span>
       <span class="comma" v-else>{{item}}</span>
      </li>
     </div>
    </div>
 </template>
 <script>
  export default {
    props: {
      value: {
        type: Number, // 具体数值
        default() {
          return 0;
        },
      },
      time: {
        type: Number, // 滚动要花的时间,单位秒
        default() {
          return 3;
        },
      },
    },
   data() {
    return {
     orderNum: ['0', '0', ',', '0', '0', '0', ',', '0', '0', '0'], // 默认订单总数
    }
   },
   mounted() {
    this.toOrderNum(this.value) // 这里输入数字即可调用
    this.increaseNumber(this.time);
   },
   methods: {
    // 定时增长数字
    increaseNumber (time) {
      let self = this
      this.timer = setInterval(() => {
      self.newNumber = self.newNumber + self.getRandomNumber(1, 100)
      self.setNumberTransform()
      }, time * 1000)
    },
     // 设置文字滚动
    setNumberTransform() {
     // 拿到数字的ref,计算元素数量 numberItems数组自身顺序有问题,需要处理成和数值保持一致
     const numberItems = this.$refs.numberItem
     numberItems.sort((a, b) => {
        // 如果value相同,按照value的降序
        if (a.attributes.myIndex.value === b.attributes.myIndex.value) {
          return b.attributes.myIndex.value - a.attributes.myIndex.value;
        }
        return a.attributes.myIndex.value - b.attributes.myIndex.value;
      });
      
      // 在 template 里面加上 myIndex 和 myValue 属性,是因为有次发现,显示出来的结果不准确
      // 经排查,发现是 numberItems字段里面获取的 html元素顺序不对,所以重新给他排好序
      // 注意:numberItems字段里面获取的 html元素顺序不对 是偶尔发生的现象,但是为了安全起见,还是处理下
      
     const numberArr = this.orderNum.filter(item => !isNaN(item))
     // 结合CSS 对数字字符进行滚动,显示订单数量
     for (let index = 0; index < numberItems.length; index++) {
     const elem = numberItems[index]
     elem.style.transform = `translate(-50%, -${numberArr[index] * 10}%)`
     }
    },
    getRandomNumber(min, max) {
      return Math.floor(Math.random() * (max - min + 1) + min)
    },
    // 处理传过来的具体值value
    toOrderNum(num) {
     num = num.toString()
     // 把具体值value变成字符串
     if (num.length < 8) {
     num = '0' + num // 如未满八位数,添加"0"补位
     this.toOrderNum(num) // 递归添加"0"补位
     } else if (num.length === 8) {
     // 具体值value中加入逗号
     num = num.slice(0, 2) + ',' + num.slice(2, 5) + ',' + num.slice(5, 8)
     this.orderNum = num.split('') // 将其便变成数据,渲染至滚动数组
     } else {
     // 具体值value数字超过八位显示异常
     this.$message.warning('xxx数量过大,显示异常,请联系后台管理员')
     }
    },
   }
  }
 </script>
 <style scoped lang='scss'>
   /*具体值value总量滚动数字设置*/
  .box-item {
   position: relative;
   height: 100px;
   font-size: 54px;
   line-height: 41px;
   text-align: center;
   list-style: none;
   color: #2D7CFF;
   writing-mode: vertical-lr;
   text-orientation: upright;
   /*文字禁止编辑*/
   -moz-user-select: none; /*火狐*/
   -webkit-user-select: none; /*webkit浏览器*/
   -ms-user-select: none; /*IE10*/
   -khtml-user-select: none; /*早期浏览器*/
   user-select: none;
   /* overflow: hidden; */
  }
  /* 默认逗号设置 */
  .mark-item {
   width: 10px;
   height: 100px;
   margin-right: 5px;
   line-height: 10px;
   font-size: 48px;
   position: relative;
   & > span {
    position: absolute;
    width: 100%;
    bottom: 0;
    writing-mode: vertical-rl;
    text-orientation: upright;
   }
  }
  /*滚动数字设置*/
  .number-item {
   width: 41px;
   height: 75px;
   background: #ccc;
   list-style: none;
   margin-right: 5px;
   background:rgba(250,250,250,1);
   border-radius:4px;
   border:1px solid rgba(221,221,221,1);
   & > span {
    position: relative;
    display: inline-block;
    margin-right: 10px;
    width: 100%;
    height: 100%;
    writing-mode: vertical-rl;
    text-orientation: upright;
    overflow: hidden;
    & > i {
     font-style: normal;
     position: absolute;
     top: 11px;
     left: 50%;
     transform: translate(-50%,0);
     transition: transform 1s ease-in-out;
     letter-spacing: 10px;
    }
   }
  }
  .number-item:last-child {
   margin-right: 0;
  }
 </style>

引用方法同上边一样:

<template>
  <div>
    <module :value="102346"  :time="2"/>
  </div>
</template>

<script>
// 旋转展示数值组件
import module from './../../components/comRollNumber2'
export default {
  name: 'test',
  components: {
    module,
  },
  data() {
    return {
    }
  },
  methods: {
  },
  mounted() {
  },
}
</script>

<style scope>
</style>

参考文章:

  1. vue 实现数字滚动增加效果的实例代码
  2. Vue.js实现大屏数字滚动翻转效果

都看到这里了,求各位观众大佬们点个赞再走吧,你的赞对我非常重要