移动端CSS动画卡顿怎么优化?

打工人艳花 阅读 4

我在做一个移动端的下拉刷新动画,用的是CSS的transform: translateY()配合transition,但在低端安卓机上特别卡,掉帧严重。我查了资料说要加will-change或者用translateZ(0)开启硬件加速,试了好像没啥用。

这是我的关键代码:

.refresh-indicator {
  transition: transform 0.3s ease;
  transform: translateY(-20px);
  will-change: transform;
}

是不是还有其他坑?比如动画过程中触发了重排?或者我该改用Web Animations API?求真实有效的优化方案!

我来解答 赞 3 收藏
二维码
手机扫码查看
1 条解答
Air-莉莉
这种卡顿大概率是因为你用 CSS transition 去做跟手交互导致的。下拉刷新这种需要实时响应用户手指位置的动画,千万别用 transition,JS 更新样式和 CSS 渲染引擎的计算会打架,导致掉帧。

正确的做法是把“拖拽过程”和“回弹过程”分开。手指在动的时候,直接用 JS 改 transform,并且必须包在 requestAnimationFrame 里,这样能保证和屏幕刷新率同步。松手回弹的时候,再给元素加上 transition 让它自动弹回去。

还有个安全细节要注意,will-change 虽然能开启硬件加速,但它非常吃内存。低端机本来资源就紧张,如果一直挂着 will-change,很容易导致内存爆满触发 GC(垃圾回收),反而会造成严重的卡顿甚至闪退。建议在 touchstart 的时候动态加上,touchend 结束动画后赶紧移除。

代码大概这么改,你试试看:

let isDragging = false;
let startY = 0;
let currentY = 0;
const indicator = document.querySelector('.refresh-indicator');

indicator.addEventListener('touchstart', (e) => {
isDragging = true;
startY = e.touches[0].clientY;
// 开始拖拽时移除 transition,保证跟手丝滑
indicator.style.transition = 'none';
// 仅在需要时开启硬件加速
indicator.style.willChange = 'transform';
}, { passive: true });

indicator.addEventListener('touchmove', (e) => {
if (!isDragging) return;
const y = e.touches[0].clientY - startY;
// 加点阻尼效果,限制最大下拉距离
currentY = Math.min(Math.max(y, 0), 100);

requestAnimationFrame(() => {
indicator.style.transform = translateY(${currentY}px);
});
}, { passive: true });

indicator.addEventListener('touchend', () => {
isDragging = false;
// 松手时加上 transition,实现回弹
indicator.style.transition = 'transform 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94)';
indicator.style.transform = 'translateY(0)';

// 动画结束后清理 will-change,释放内存
setTimeout(() => {
indicator.style.willChange = 'auto';
}, 300);
});


至于 CSS 部分,只保留 transform 属性就够了。如果还是卡,检查一下是不是触发了重排,比如在动画过程中改变了元素的 width、height 或者 margin。transform 和 opacity 是唯一安全的不触发重排的属性,只要不碰其他属性,低端机也能跑得动。
点赞
2026-03-04 16:05