实现大屏数字滚动翻转效果(二次封装)

956 阅读1分钟

项目要做一个数字滚动的效果,网上搜索找到两篇文章你可能需要这样的大屏数字滚动效果【vue自定义组件】实现大屏数字滚动翻转效果,研究下来角色效果不错,于是在两位大佬的基础上做了二次封装,一个细节需要注意下,v-for的key的定义,如果直接使用index可能会导致显示错乱,参考# vue中使用v-for时为什么要用到key?为什么不能用index作为key?

3.gif

<template>
  <div class="box-item">
    <li
      v-for="(item, index) in orderNum"
      :key="item.toString() + index.toString() + getRandomNumber(1, 10000)"
      :class="{ 'number-item': !isNaN(item), 'mark-item': isNaN(item) }"
    >
      <span v-if="!isNaN(item)">
        <i ref="numberItem">0123456789</i>
      </span>
      <span v-else class="comma">{{ item }}</span>
    </li>
  </div>
</template>
<script>
export default {
  name: 'FlipNumber',
  props: {
    // 具体数值
    number: {
      type: Number,
      default() {
        return 0
      }
    },
    // 数字更新的时间
    time: {
      type: Number,
      default() {
        return 1
      }
    },
    // 数字最小长度,长度不足时根据fillZero配置补0
    numberLength: {
      type: Number,
      default: 8
    },
    // 不足numberLength时是否在前面补0
    fillZero: {
      type: Boolean,
      default: false
    }
  },
  data() {
    return {
      orderNum: []
    }
  },
  watch: {
    number(newVal, oldVal) {
      this.orderNum = this.transNum2ShowValue(newVal) // 这里输入数字即可调用
      this.flip(oldVal)
    }
  },
  mounted() {
    this.orderNum = this.transNum2ShowValue(this.number) // 这里输入数字即可调用
    this.flip()
  },
  methods: {
    flip(oldVal) {
      const self = this
      // orderNum更改之后,页面会重新渲染,即数字全部归0,如果不赋值一次旧,会出现全0的情况
      this.$nextTick(() => {
        // 重新渲染之后先给<i>赋旧值
        if (oldVal) {
          this.setNumberTransform(this.transNum2ShowValue(oldVal))
        }
        // 再赋新值,这样就会出现变化的效果
        setTimeout(() => {
          self.setNumberTransform(this.orderNum)
        }, this.time * 1000)
      })
    },
    // 设置文字滚动
    setNumberTransform(numStr) {
      const numberItems = this.$refs.numberItem
      const numberArr = numStr.filter(item => !isNaN(item))
      for (let index = 1; index <= numberArr.length; index++) {
        // 倒序赋值,保证出现位数变化的时候,0出现在第一个,比如从9999变为10001,会先变成09999,在变成10001
        const elem = numberItems[numberItems.length - index]
        elem.style.transform = `translate(-50%, -${numberArr[numberArr.length - index] * 10}%)`
      }
    },
    getRandomNumber(min, max) {
      return Math.floor(Math.random() * (max - min + 1) + min)
    },
    // 处理传过来的具体值value
    transNum2ShowValue(num) {
      if (this.fillZero) {
        if (num.length < this.numberLength) {
          num = this.preZeroFill(num, this.numberLength)
        } else {
          num = this.numberWithDot(num)
        }
      } else {
        num = this.numberWithDot(num)
      }
      return num.split('')
    },
    numberWithDot(number) {
      const str = []
      if (typeof number === 'number') {
        number = String(number)
      }
      if (number.length <= 3) {
        return number
      }
      String(number)
        .split('')
        .reverse()
        .forEach((item, index) => {
          if (index !== 0 && index % 3 === 0) {
            str.push(',', item)
          } else {
            str.push(item)
          }
        })
      return str.reverse().join('')
    },
    preZeroFill(num, size) {
      if (num >= Math.pow(10, size)) {
        // 如果num本身位数不小于size位
        return num.toString()
      } else {
        var _str = Array(size + 1).join('0') + num
        return _str.slice(_str.length - size)
      }
    }
  }
}
</script>
 <style scoped lang='scss'>
/*具体值value总量滚动数字设置*/
.box-item {
  position: relative;
  height: 50px;
  font-size: 27px;
  line-height: 20px;
  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: 50px;
  margin-right: 5px;
  line-height: 10px;
  font-size: 24px;
  position: relative;
  & > span {
    position: absolute;
    width: 100%;
    bottom: 0;
    writing-mode: vertical-rl;
    text-orientation: upright;
  }
}
/*滚动数字设置*/
.number-item {
  width: 41px;
  height: 50px;
  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>
  <el-container>
    <FlipNumber :number="num1" :fill-zero="true" />
  </el-container>
</template>
<script>
import FlipNumber from '@/views/components/common/FlipNumber.vue'
export default {
  components: {
    FlipNumber
  },
  data() {
    return {
        num1: 3200
    }
  },
  mounted() {
    setInterval(() => {
      this.num1 = this.num1 + 1521
    }, 2000)
  },
  methods: {
  }
}
</script>

附上链接:

juejin.cn/post/684490…

juejin.cn/post/702141…

blog.csdn.net/qq_44880532…