uni-app中数字增长动画需用js控制帧率,推荐使用uni-number-animation等成熟组件;手写时应避免math.round导致跳变,改用tofixed(1)和浮点数容差判断停止条件。

uni-app里用transition做数字增长动画行不通
直接给number绑定transition没效果,因为Vue的transition只对元素显隐、挂载/卸载生效,不监听数值变化。数字增长本质是文本内容变更,得靠JS控制中间帧。
- 别写
<transition><text>{{ count }}</text></transition>,它不会动 - 真实需求是“从100滚动到1234”,不是淡入淡出,得用计时器或
requestAnimationFrame - uni-app跨端(尤其是小程序)对
setTimeout精度和raf支持不一致,优先选setTimeout+ 步长控制
用countTo组件实现平滑滚动(推荐方案)
社区成熟的countTo组件已处理兼容性问题,比手写更稳。uni-app官方插件市场有多个版本,推荐使用uni-count-down的兄弟组件uni-number-animation(注意不是倒计时),或轻量版count-to(需自行适配)。
- 安装后按文档引入:
import CountTo from '@/components/count-to/count-to.vue' - 关键属性:
start-val(起始值)、end-val(目标值)、duration(毫秒,建议800~1500)、autoplay(设为true) - 触发重播:修改
end-val会自动重置并重新滚动,无需手动调start() - 小程序真机调试时若卡顿,检查是否在
onLoad中过早触发,建议延迟100ms再赋值end-val
手写简易滚动函数:避开Math.round导致的跳变
自己实现时最容易踩的坑是四舍五入——比如从100滚到103,中间算出100.3、100.6、100.9、101.2…直接Math.round会变成100→101→101→101→102,出现“卡住”感。
- 正确做法:用
Math.floor或保留1位小数显示,最终值再取整:current = start + (end - start) * progress,然后current.toFixed(1) - 步长计算公式:
step = (end - start) / (duration / 16)(按16ms一帧),但需防除零,加if (duration - 停止条件别用
===判断浮点数,改用Math.abs(current - end) - 示例片段:
startCount() {<br> const start = this.count;<br> const end = this.target;<br> const duration = 1200;<br> const step = (end - start) / (duration / 16);<br> let current = start;<br> const timer = setInterval(() => {<br> current += step;<br> if (Math.abs(current - end) this.count = end;<br> clearInterval(timer);<br> } else {<br> this.count = parseFloat(current.toFixed(1));<br> }<br> }, 16);<br>}
图标+数字组合点击触发时的性能陷阱
点击图标触发数字增长,如果同时有多个数字块(如购物车角标、点赞数、未读消息),频繁调用startCount会导致setInterval堆积,内存泄漏。
- 每个数字实例必须维护自己的
timer变量,并在组件onUnload或beforeDestroy里clearInterval(this.timer) - 避免在
watch里无条件启动动画,加个开关:if (this.isAnimating) return;,动画开始前设this.isAnimating = true,结束回调里置false - App端(iOS)若出现数字闪烁,大概率是
text组件渲染层刷新问题,换成view包裹text,并加key强制更新:<text :key="count">{{ count }}</text>
真正难的不是让数字动起来,而是动得准、停得稳、关得掉——尤其在小程序双线程模型下,定时器清理和数据响应链路容易断开。











