告别数字动画繁琐实现:Vue3组合式API封装countUp.js全指南
你还在为实现数字滚动动画编写冗长代码吗?还在纠结如何将第三方库优雅集成到Vue3项目中吗?本文将带你用组合式API(Composition API)封装countUp.js,打造一个可复用、易维护的数字动画组件,让数值从0平滑滚动到目标值的效果实现只需3行代码。
读完本文你将获得:
- 掌握countUp.js核心API及Vue3组合式封装技巧
- 学会使用自定义hooks抽象动画逻辑
- 获得一个支持12种配置项的通用数字动画组件
- 解决滚动触发、暂停续播等常见业务场景问题
认识countUp.js:轻量级数字动画库
countUp.js是一个零依赖的数值动画库(src/countUp.ts),通过平滑计数到目标值实现动态视觉效果。其核心原理是利用requestAnimationFrame API逐帧更新数值,支持自定义动画时长、小数位数、千分位分隔符等配置。
基础用法示例
// 原生JS初始化
const countUp = new CountUp('targetId', 1000, {
duration: 2, // 动画时长(秒)
useGrouping: true // 启用千分位分隔
});
countUp.start();
Vue3组合式API封装优势
相比Vue2的选项式API,组合式API在封装第三方库时有三大优势:
- 逻辑复用:通过自定义hooks抽离动画逻辑,可在多个组件中复用
- 类型安全:配合TypeScript获得完整类型提示(tsconfig.json)
- 响应式集成:天然适配ref/reactive数据响应系统
传统实现vs组合式封装对比
| 实现方式 | 代码量 | 复用性 | 维护成本 |
|---|---|---|---|
| 选项式API | 80行/组件 | 低(需mixins) | 高 |
| 组合式API | 30行/hook + 10行/组件 | 高(hooks调用) | 低 |
从零开始封装组件
1. 安装与引入
通过npm安装核心依赖:
npm install countup.js --save
国内用户可使用淘宝npm镜像加速安装:
npm install countup.js --save --registry=https://registry.npmmirror.com
2. 创建组合式Hook:useCountUp
新建src/hooks/useCountUp.ts文件,封装核心动画逻辑:
import { ref, onMounted, onUnmounted, Ref } from 'vue';
import { CountUp, CountUpOptions } from 'countup.js';
export function useCountUp(
targetRef: Ref<HTMLElement | null>,
endVal: number,
options?: CountUpOptions
) {
const countUpInstance = ref<CountUp | null>(null);
const isAnimating = ref(false);
// 初始化实例
const initCountUp = () => {
if (!targetRef.value) return;
countUpInstance.value = new CountUp(targetRef.value, endVal, {
startVal: 0,
duration: 2,
...options
});
// 处理初始化错误
if (countUpInstance.value.error) {
console.error('CountUp初始化失败:', countUpInstance.value.error);
}
};
// 开始动画
const start = () => {
if (countUpInstance.value && !isAnimating.value) {
countUpInstance.value.start(() => {
isAnimating.value = false;
});
isAnimating.value = true;
}
};
// 暂停/继续动画
const pauseResume = () => {
countUpInstance.value?.pauseResume();
isAnimating.value = !isAnimating.value;
};
// 生命周期管理
onMounted(initCountUp);
onUnmounted(() => {
countUpInstance.value?.reset();
});
return { start, pauseResume, isAnimating };
}
3. 开发数字动画组件
创建src/components/CountUpNumber.vue组件:
<template>
<span ref="countRef" class="count-up-number"></span>
</template>
<script setup lang="ts">
import { ref, toRefs, defineProps } from 'vue';
import { useCountUp } from '../hooks/useCountUp';
// 定义组件Props
const props = defineProps<{
endVal: number;
duration?: number;
prefix?: string;
suffix?: string;
decimalPlaces?: number;
enableScrollSpy?: boolean;
}>();
const countRef = ref<HTMLElement | null>(null);
const { endVal, ...options } = toRefs(props);
// 调用组合式Hook
const { start, pauseResume, isAnimating } = useCountUp(
countRef,
endVal.value,
options
);
// 暴露控制方法
defineExpose({ start, pauseResume });
</script>
4. 实现滚动触发动画
利用IntersectionObserver API增强组件,实现元素进入视口时自动开始动画:
// 在useCountUp hook中添加
import { useIntersectionObserver } from '@vueuse/core';
// ...省略原有代码
onMounted(() => {
initCountUp();
if (options.enableScrollSpy) {
const { stop } = useIntersectionObserver(
targetRef,
([{ isIntersecting }]) => {
if (isIntersecting) {
start();
stop(); // 只触发一次
}
},
{ threshold: 0.1 } // 元素可见10%时触发
);
onUnmounted(stop);
}
});
组件应用与配置项
基础使用示例
在页面中引入组件并传入基础参数:
<template>
<div class="dashboard">
<count-up-number
:endVal="12345"
:duration="3"
prefix="$"
suffix="/月"
/>
</div>
</template>
<script setup lang="ts">
import CountUpNumber from '@/components/CountUpNumber.vue';
</script>
完整配置项说明
| 参数名 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| endVal | number | - | 目标数值 |
| duration | number | 2 | 动画时长(秒) |
| startVal | number | 0 | 起始数值 |
| decimalPlaces | number | 0 | 小数位数 |
| useGrouping | boolean | true | 是否启用千分位 |
| separator | string | ',' | 千分位分隔符 |
| prefix | string | '' | 数值前缀 |
| suffix | string | '' | 数值后缀 |
| enableScrollSpy | boolean | false | 是否滚动触发 |
| scrollSpyDelay | number | 200 | 滚动触发延迟(ms) |
| onComplete | Function | - | 动画完成回调 |
| onStart | Function | - | 动画开始回调 |
常见问题解决方案
1. 动态更新目标值
当需要响应式更新目标值时,可通过update方法实现:
<template>
<count-up-number ref="counter" :endVal="currentVal" />
<button @click="updateVal">更新数值</button>
</template>
<script setup lang="ts">
import { ref } from 'vue';
const currentVal = ref(1000);
const counter = ref(null);
const updateVal = () => {
currentVal.value = 5000;
counter.value.update(currentVal.value);
};
</script>
2. 暂停与续播控制
利用组件暴露的方法实现动画控制:
<template>
<div>
<count-up-number ref="counter" :endVal="10000" />
<button @click="toggleAnimation">
{{ isAnimating ? '暂停' : '继续' }}
</button>
</div>
</template>
<script setup lang="ts">
import { ref, watch } from 'vue';
const counter = ref(null);
const isAnimating = ref(true);
const toggleAnimation = () => {
counter.value.pauseResume();
isAnimating.value = !isAnimating.value;
};
</script>
性能优化建议
- 懒加载组件:对非首屏数字使用Vue的
defineAsyncComponent延迟加载 - 防抖动处理:滚动触发时添加200ms防抖避免频繁触发
- 共享实例:在大数据看板场景下,通过工厂函数共享CountUp实例
- CSS优化:为数字元素添加
will-change: transform提升动画性能
总结与展望
通过组合式API封装的countUp.js组件,既保留了原库的强大功能,又充分发挥了Vue3的响应式优势。这种封装方式不仅实现了逻辑复用,还使代码结构更清晰、维护成本更低。
后续可扩展方向:
- 支持自定义动画缓动函数
- 实现数字滚动方向控制
- 添加数字变化过渡效果
收藏本文,下次实现数字动画时直接复用组件!关注作者获取更多Vue3组合式API实战技巧。
本文示例代码已同步至项目仓库demo/demo.js,可直接运行查看效果。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考




