vue2 实现数字滚动特效

首页统计栏数字实现数字滚动效果,废话不多说直接上代码:

javascript 复制代码
// 新增:执行数字滚动动画
    runCountAnimation(targetVal) {
      // 计算步长(目标值越大,步长越大,保证动画速度均匀)
      const step = Math.max(1, Math.ceil(targetVal / 100));
      // 设置定时器,实现平滑递增
      this.countTimer = setInterval(() => {
        // 判断:当前数字未达到目标值,继续递增
        if (this.currentCount < targetVal) {
          // 防止最后一步超出目标值,取最小值
          this.currentCount = Math.min(this.currentCount + step, targetVal);
        } else {
          // 达到目标值,清理定时器
          this.clearCountTimer();
        }
      }, 16); // 约60帧/秒,保证动画流畅
    },
    // 新增:清理定时器
    clearCountTimer() {
      if (this.countTimer) {
        clearInterval(this.countTimer);
        this.countTimer = null;
      }
    }

我这个是一个封装的组件,所以上面的执行方法逻辑如下:

javascript 复制代码
 watch: {
    // 监听目标count变化,触发数字滚动动画
    count(newVal, oldVal) {
      // 先清理原有定时器,防止重复触发
      this.clearCountTimer();
      // 重置当前显示数字为0
      this.currentCount = 0;
      // 若新值为0,直接结束,不执行动画
      if (newVal === 0) return;
      // 执行数字滚动动画
      this.runCountAnimation(newVal);
    }
  },
  mounted() {
    // 组件初始化时,触发一次动画(针对初始传入的count值)
    if (this.count !== 0) {
      this.runCountAnimation(this.count);
    }
  },
  beforeDestroy() {
    // 组件销毁前,清理定时器,防止内存泄露
    this.clearCountTimer();
  },

组件的完整代码,这个是jeecg boot 3.2.0 的前端项目,该兴趣可以看看,里面使用了j-ellispsis进行数字缩略

javascript 复制代码
<template>
  <a-card :loading="loading" :bordered="false">
    <div class="card">
      <div class="logo-c" :style="{background:bgColor}">
        <img :src="src" class="card-l" />
      </div>
      <div class="money-content">
        <div class="title"><j-ellipsis :value="title" :length="8"/></div>
        <div style="white-space: nowrap;">
          <!-- 保持原有格式化和省略逻辑,仅替换数据源为currentCount -->
          <span class="count" :style="{color:fontColor}">
            <j-ellipsis :value="String(formattedCount)" :length="8"/>
          </span>
          <span class="c-font" style="margin-left: 3px">{{formattedUnit}}</span>
        </div>
      </div>
    </div>
  </a-card>
</template>

<script>
export default {
  name: "MoneyCode",
  props: {
    title: {
      type: String,
      default: ''
    },
    total: {
      type: String,
      default: ''
    },
    loading: {
      type: Boolean,
      default: false
    },
    //字体颜色
    fontColor:{
      type: String,
      default: "#FF4D4F"
    },
    //背景颜色
    bgColor:{
      type: String,
      default: "#FFF1F0"
    },
    //数量(目标值)
    count:{
      type: Number,
      default: 0
    },
    //单位
    unit:{
      type: String,
      default: '笔'
    },
    src:{
      default: require("@/assets/day-money-count.svg")
    }
  },
  data() {
    return {
      currentCount: 0, // 新增:当前动画显示的数字(响应式)
      countTimer: null // 新增:定时器实例,用于清理定时器
    }
  },
  computed: {
    // 原有逻辑不变,仅将依赖的this.count改为this.currentCount
    formattedCount() {
      if (this.currentCount >= 10000) {
        return (this.currentCount / 10000).toFixed(3);
      }
      return this.currentCount;
    },
    formattedUnit() {
      // 注意:此处判断目标值count,而非当前动画值currentCount,保证单位格式化准确
      if (this.count >= 10000) {
        let locale = localStorage.getItem('locale');
        if(locale === 'en_GB'){
          return 'W ' + this.unit;
        }else if(locale === 'vi_VN'){
          return 'triệu ' + this.unit;
        } else{
          return '万' + this.unit;
        }
      }
      return this.unit;
    }
  },
  watch: {
    // 监听目标count变化,触发数字滚动动画
    count(newVal, oldVal) {
      // 先清理原有定时器,防止重复触发
      this.clearCountTimer();
      // 重置当前显示数字为0
      this.currentCount = 0;
      // 若新值为0,直接结束,不执行动画
      if (newVal === 0) return;
      // 执行数字滚动动画
      this.runCountAnimation(newVal);
    }
  },
  mounted() {
    // 组件初始化时,触发一次动画(针对初始传入的count值)
    if (this.count !== 0) {
      this.runCountAnimation(this.count);
    }
  },
  beforeDestroy() {
    // 组件销毁前,清理定时器,防止内存泄露
    this.clearCountTimer();
  },
  methods: {
    // 新增:执行数字滚动动画
    runCountAnimation(targetVal) {
      // 计算步长(目标值越大,步长越大,保证动画速度均匀)
      const step = Math.max(1, Math.ceil(targetVal / 100));
      // 设置定时器,实现平滑递增
      this.countTimer = setInterval(() => {
        // 判断:当前数字未达到目标值,继续递增
        if (this.currentCount < targetVal) {
          // 防止最后一步超出目标值,取最小值
          this.currentCount = Math.min(this.currentCount + step, targetVal);
        } else {
          // 达到目标值,清理定时器
          this.clearCountTimer();
        }
      }, 16); // 约60帧/秒,保证动画流畅
    },
    // 新增:清理定时器
    clearCountTimer() {
      if (this.countTimer) {
        clearInterval(this.countTimer);
        this.countTimer = null;
      }
    }
  }
}
</script>

使用组件,里面使用了vue-i18n进行国际化,感兴趣的可以了解我之前的文章
https://blog.csdn.net/a_biggerfish/article/details/147423588?spm=1001.2014.3001.5502

javascript 复制代码
 <a-row :gutter="[24,16]">
      <a-col :sm="24" :md="12" :xl="6" :xxl="4">
        <money-card :title="$t('dashboard.dailyPaymentFrequency')" :unit="$t('dashboard.count')" :count="baseInfo.dayPaymentsCount?baseInfo.dayPaymentsCount:0" :src="require('@/assets/day-money-count.svg')"></money-card>
      </a-col>
      <a-col v-has="'chart:all'" :sm="24" :md="12" :xl="6" :xxl="4">
        <money-card :title="$t('dashboard.dailyPaymentAmount')" :unit="$t('dashboard.RMB')" :count="baseInfo.dayPaymentsMoney?baseInfo.dayPaymentsMoney:0" :src="require('@/assets/day-money-count.svg')"></money-card>
      </a-col>
      <a-col v-has="'chart:all'"  :sm="24" :md="12" :xl="6" :xxl="4">
        <money-card :title="$t('dashboard.dailyAccumulatedUsage')" unit="m³" :count="baseInfo.dayWaterVolume?baseInfo.dayWaterVolume:0" font-color="#FFA940" bg-color="#FFF7E6" :src="require('@/assets/day-money.svg')"></money-card>
      </a-col>
      <a-col :sm="24" :md="12" :xl="6" :xxl="4">
        <money-card :title="$t('dashboard.dailyAccumulatedPayment')" :unit="$t('dashboard.RMB')" :count="baseInfo.dayPaymentsAmount?baseInfo.dayPaymentsAmount:0" font-color="#FFA940" bg-color="#FFF7E6" :src="require('@/assets/day-money.svg')"></money-card>
      </a-col>
      <a-col :sm="24" :md="12" :xl="6" :xxl="4">
        <money-card :title="$t('dashboard.monthlyPaymentFrequency')" :unit="$t('dashboard.count')" :count="baseInfo.monthPaymentsCount?baseInfo.monthPaymentsCount:0" font-color="#BAE637" bg-color="#F6FFED" :src="require('@/assets/month-money-count.svg')"></money-card>
      </a-col>
      <a-col v-has="'chart:all'"  :sm="24" :md="12" :xl="6" :xxl="4">
        <money-card :title="$t('dashboard.monthlyPaymentAmount')" :unit="$t('dashboard.RMB')" :count="baseInfo.monthPaymentsMoney?baseInfo.monthPaymentsMoney:0" font-color="#BAE637" bg-color="#F6FFED" :src="require('@/assets/month-money-count.svg')"></money-card>
      </a-col>
      <a-col v-has="'chart:all'"  :sm="24" :md="12" :xl="6" :xxl="4">
        <money-card :title="$t('dashboard.monthlyAccumulatedUsage')" unit="m³" :count="baseInfo.monthWaterVolume?baseInfo.monthWaterVolume:0" font-color="#FADB14" bg-color="#FEFFE6" :src="require('@/assets/month-money.svg')"></money-card>
      </a-col>
      <a-col :sm="24" :md="12" :xl="6" :xxl="4">
        <money-card :title="$t('dashboard.monthlyAccumulatedPayment')" :unit="$t('dashboard.RMB')" :count="baseInfo.monthPaymentsAmount?baseInfo.monthPaymentsAmount:0" font-color="#FADB14" bg-color="#FEFFE6" :src="require('@/assets/month-money.svg')"></money-card>
      </a-col>
      <a-col :sm="24" :md="12" :xl="6" :xxl="4">
        <money-card :title="$t('dashboard.monthlyAccountOpeningsNumber')" :unit="$t('dashboard.households')" bg-color="#E6F7FF" font-color="#40A9FF" :count="baseInfo.monthOpenAccountCount?baseInfo.monthOpenAccountCount:0" :src="require('@/assets/month-home.svg')"></money-card>
      </a-col>
      <a-col :sm="24" :md="12" :xl="6" :xxl="4">
        <money-card :title="$t('dashboard.totalAccountOpeningsNumber')" :unit="$t('dashboard.households')" :count="baseInfo.totalOpenAccountCount?baseInfo.totalOpenAccountCount:0" font-color="#9254DE" bg-color="#F9F0FF" :src="require('@/assets/all-home.svg')"></money-card>
      </a-col>
    </a-row>

效果如下:

https://live.csdn.net/v/508969

20260106_165126

相关推荐
文心快码BaiduComate1 天前
嫌市面上的刷题App太丑,我让Comate帮我写了个“考证神器”
前端·产品
harrain1 天前
html里引入使用svg的方法
前端·svg
遗憾随她而去.1 天前
Webpack5 基础篇(二)
前端·webpack·node.js
Mintopia1 天前
🧭 一、全栈能力的重心正在从“实现” → “指令 + 验证”转移
前端·人工智能·全栈
Mintopia1 天前
2025,我的「Vibe Coding」时刻
前端·人工智能·aigc
西凉的悲伤1 天前
html制作太阳系行星运行轨道演示动画
前端·javascript·html·行星运行轨道演示动画
C_心欲无痕1 天前
网络相关 - http1.1 与 http2
前端·网络
一只爱吃糖的小羊1 天前
Web Worker 性能优化实战:将计算密集型逻辑从主线程剥离的正确姿势
前端·性能优化
低保和光头哪个先来1 天前
源码篇 实例方法
前端·javascript·vue.js