Skip to content

Vue 数据滚动效果实现

0123456789
0123456789

上面动画效果的实现最重要的步骤是要去每次改变 0-9 的数字,然后通过 CSS3 的动画来实现滚动效果。

实现数字布局

实现布局非常的简单

0123456789
css
.amount-wrapper1 {
  display: inline-block;
  width: 60px;
  height: 60px;
  text-align: center;
  line-height: 60px;
  background-color: #42b883;
  border-radius: 5px;
}
.amount-wrapper1 em {
  display: block;
}
.amount-wrapper1 em i {
  display: block;
  width: 100%;
  height: 100%;
  text-align: center;
  line-height: 60px;
  font-size: 32px;
  font-weight: bold;
  color: red;
}

可以看到布局的 css 非常简单,主要是限制父级元素的宽度和高度,然后通过子块级元素的特性自动向下堆叠,就可以将数字排列起来了。

让数字动

让数字动起来,这里是用到的 css3 中得 transform 属性,通过 translateY 来实现向下移动的效果。然后在 transition 中设置动画的时间和动画的效果,就可以实现数字的滚动效果了,于此同时将最外层的元素设置为 overflow: hidden,这样就可以在通过数字变动的时候动态设置 transform 的值,从而实现滚动效果。

css
.amount-wrapper2 em {
  display: block;
  transform: translateY(-10%);
  transition: transform 0.5s ease-in-out;
}
0123456789
css
.amount-wrapper2 {
  display: inline-block;
  width: 60px;
  height: 60px;
  text-align: center;
  line-height: 60px;
  background-color: #42b883;
  border-radius: 5px;
  overflow: hidden;
}
.amount-wrapper2 em {
  display: block;
  transform: translateY(-10%);
  transition: transform 0.5s ease-in-out;
}
.amount-wrapper2 em i {
  display: block;
  width: 100%;
  height: 100%;
  text-align: center;
  line-height: 60px;
  font-size: 32px;
  font-weight: bold;
  color: red;
}

计算滚动

在上述代码中可以看到页面展示 1 时,只需要将 em 元素向上移动 10% 的高度就可以了,那么当数字变成 2 的时候,就需要将 em 元素向下移动 20% 的高度,以此类推,当数字变成 9 的时候,就需要将 em 元素向下移动 90% 的高度,这样就可以实现滚动的效果了。

以此类推 emtransform 的值就是 translateY(-10% * num),这里的 num 就是数字的值,那么当数字变化的时候,就需要动态的改变 emtransform 的值。

所以可以在数字变化的时候,动态的改变 emtransform 的值,从而实现滚动的效果。

实现滚动

页面需要展示数字的千分位,所以需要将数字转换成数组,然后通过 v-for 来展示每一位数字,这样就可以实现千分位的展示了。

js
// 数字转千分位数组
amountArr() {
  return this.amount.toString().replace(/(\d)(?=(\d{3})+$)/g, "$1,").split('')
}

接下来是随机增长数字,这里是通过 setInterval 来实现模拟接口效果

js
setInterval(() => {
  this.increaseRandom()
  // increaseInterval 为随机增长的时间间隔
}, this.increaseInterval)

increaseRandom() {
  const random = Math.floor(Math.random() * 100) + 1
  this.amount += random
  // 重新设置位置
  this.setAmoundPosition()
}

setAmoundPosition() {
  // amount 为每个数字的 DOM 元素
  const amount = this.$refs.amount
  // amountNumber 为纯数字转换成数组
  const amountNumber = this.amount.toString().split('')
  amount.forEach((el, index) => {
    // 获取当前位数的数字
    const number = amountNumber[index]
    // 获取当前位数的 em 元素
    const em = el.querySelector('em')
    // 设置 em 元素的 transform 的值
    em.style.transform = `translateY(-${number * 10}%)`
  })
}

通过上述代码就可以实现数字的滚动效果了。

点击查看完整代码
vue
<template>
  <div class="amount-number">
    <div class="amount-number__item" v-for="(n, index) in amountArr" :key="index">
      <span v-if="!isNaN(n)" class="amount-number__item-number" ref="amount">
        <em :style="{transition: `transform ${transitionDuration}s ease-in-out`}">
          <i>0</i>
          <i>1</i>
          <i>2</i>
          <i>3</i>
          <i>4</i>
          <i>5</i>
          <i>6</i>
          <i>7</i>
          <i>8</i>
          <i>9</i>
        </em>
      </span>
      <span v-else class="amount-number__item-comma">{{ n }}</span>
    </div>
  </div>
</template>

<script>
export default {
  props: {
    num: {
      type: Number,
      default: 0
    },
    increaseInterval: {
      type: Number,
      default: 5000
    },
    transitionDuration: {
      type: Number,
      default: 0.5
    }
  },
  data() {
    return {
      amount: this.num
    }
  },
  mounted() {
    this.$nextTick(() => {
      this.setAmoundPosition()
    })
    setInterval(() => {
      this.increaseRandom()
    }, this.increaseInterval)
  },
  computed: {
    // 数字转数组
    amountArr() {
      return this.amount.toString().replace(/(\d)(?=(\d{3})+$)/g, "$1,").split('')
    }
  },
  methods: {
    // 随机增长 1-100
    increaseRandom() {
      const random = Math.floor(Math.random() * 100) + 1
      this.amount += random
      // 重新设置位置
      this.setAmoundPosition()
    },
    // 设置数字位置
    setAmoundPosition() {
      const amount = this.$refs.amount

      const amountNumber = this.amount.toString().split('')
      amount.forEach((el, index) => {
        const number = amountNumber[index]
        const em = el.querySelector('em')
        em.style.transform = `translateY(-${number * 10}%)`
      })
    }
  }
}
</script>

<style scoped>
/* .amount {
  width: 100%;
  color: #fff;
} */
.amount-number {
  display: flex;
  justify-content: center;
  column-gap: 10px;
  width: 100%;
}

.amount-number__item {
  font-size:36px;
  font-weight: bold;
  color: #fff;
}

.amount-number__item-number {
  display: inline-block;
  width: 60px;
  height: 60px;
  text-align: center;
  line-height: 60px;
  background-color: #42b883;
  border-radius: 5px;
  overflow: hidden;
}

.amount-number__item-number em {
  display: block;
  transform: translateY(-10%);
}

.amount-number__item-number i {
  display: block;
  width: 100%;
  height: 100%;
  text-align: center;
  line-height: 60px;
}

.amount-number__item-comma {
  display: flex;
  flex-direction: column;
  justify-content: end;
  height: 60px;
  color: #42b883;
}
</style>