应使用requestAnimationFrame配合缓动函数(如easeOutCubic)对数字进行逐帧插值计算,为每个商品维护独立的animatingNum状态并隔离动画实例,且须在nextTick后执行DOM相关操作以确保坐标准确。

加减按钮点击后数字跳变太生硬,怎么加缓动效果
直接改 num 值再绑定到 {{item.num}},Vue 会立刻重绘,没有过渡。要让数字变化“滑”过去,得用数值插值 + requestAnimationFrame 控制帧节奏,而不是依赖 CSS transition(它只对样式生效,对纯文本数字无效)。
常见错误是给 <text class="num">{{item.num}}</text> 加 transition: all 0.2s——没用,text 标签不支持 transition 动画数字内容。
- 点击加/减时,先记录起始值
startNum和目标值endNum - 用
requestAnimationFrame每帧计算当前值:current = startNum + (endNum - startNum) * easeOutCubic(t),其中t是归一化时间(0→1) - 推荐缓动函数:
easeOutCubic = t => --t * t * t + 1,比线性更自然 - 总帧数控制在 12–16 帧(约 200ms),太长用户会觉得卡,太短看不出动效
v-for 列表里多个商品共用一个动画实例,相互覆盖怎么办
如果所有商品的加减都用同一个 animationRef 或共享一个 animatingId,后点的会中断前一个,导致前一个数字停在中间值不动。必须按商品粒度隔离动画状态。
- 每个商品数据项上挂字段,如
item.animatingNum,只渲染这个字段,item.num仍是最终值 - 加减操作时,生成唯一 key:
const animKey = `num-${item.id}-${Date.now()}`,避免复用旧动画 - 用
clearTimeout或标记位终止前序动画,例如item.cancelAnimation = () => { ... } - 真机调试时注意:安卓 WebView 对
requestAnimationFrame调度不稳,建议加超时兜底(600ms 强制结束)
数字变化动效和购物车图标飞入动画同时触发,坐标错乱
加减动效本身不涉及 DOM 位置,但很多开发者会把「数字跳变」和「小球飞入」写在同一点击逻辑里,结果 uni.createSelectorQuery() 拿到的起点坐标还是旧的——因为 Vue 的响应式更新还没完成,DOM 还没重排。
- 必须等 DOM 真实更新后再查坐标:加减完成后调用
uni.$nextTick(() => { /* 查坐标、启飞入 */ }) - 不要在
add(item)函数里直接调exec(),否则拿到的是点击前的位置 - 如果用了 uView 或自定义组件库,确认其
u-number-box类组件是否已内部封装了动效;若已封装,外部再套一层插值会冲突 - iOS 微信中,
getBoundingClientRect()在滚动后可能返回错误值,坚持用uni.createSelectorQuery().select('.add-btn').boundingClientRect()替代
APP端数字动效掉帧严重,H5正常,怎么优化
APP(尤其是 Android)的 JS 执行环境调度不如 H5 稳定,requestAnimationFrame 容易被 GC 或主线程阻塞打断,导致数字跳变卡顿或直接跳到终点。
- 避免在动画帧内做复杂计算:提前算好
easeOutCubic查表数组(如长度 32 的预计算值),用索引取值代替实时运算 - 禁用不必要的响应式监听:动画期间临时设
item.animating = true,在 computed 或 watch 中跳过该 item - 用
transform: scale(0.95)配合 opacity 做轻微反馈动画,比纯数字插值更省资源 - 低端安卓机可降级:检测
uni.getSystemInfoSync().platform === 'android'且uni.getSystemInfoSync().SDKVersion 时,直接跳变,不启动插值
实际项目里最常被忽略的一点:数字动效不是为了炫技,而是给用户明确的“操作已被接收”反馈。如果加减按钮本身有按压态(active-class 或 :class="{ active: isPressed }"),再叠加数字滑动,信息就过载了。优先保证按压反馈清晰、响应及时,动效只是锦上添花。


















