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

相关推荐
2分钟速写快排18 分钟前
什么是 RAG?如何用 RAG 实现一个用户记忆?
前端·后端·ai编程
passerby60611 小时前
如何自己造一个时间处理库
前端·javascript·github
走到天涯海角2 小时前
react里面的长列表渲染优化
前端·react.js·前端框架
小羊没烦恼!2 小时前
Hello Web API系列教程——Web API与国际化
java·服务器·前端·javascript·php
北岛贰2 小时前
迷茫焦虑期,我做了一个带支付带官网的 AI 聊天虚拟恋人 App
前端·人工智能·后端
mayaairi4 小时前
Vue2 组件通讯(三):全局事件总线、PubSub、插槽与组件实例属性
前端·javascript·vue.js
kyriewen4 小时前
面试官问我:AI 都能写代码了,前端凭什么还值 25K
前端·javascript·人工智能
风骏时光牛马5 小时前
AI源码分析:拆解模型底层实现逻辑
前端
IT_陈寒5 小时前
React子组件莫名其妙重渲染?你可能漏了这个Hook
前端·人工智能·后端
乘风gg6 小时前
DeepSeek V4.1 Flash 来了,明天中午 Flash 降价 60%
前端·ai编程·claude