你是不是也遇到过:动画效果刚上线,用户说“卡”,测试机上一跑,帧率掉到30以下,连产品经理都默默把“丝滑”这个词从PRD里删了?别急着改代码——问题大概率不在动画本身,而在浏览器怎么“看”它。

我做过十年前端动效,踩过的坑比写过的transform还多。今天不讲理论,只聊你明天就能用上的实操平衡点。

为什么GPU加速有时反而更卡?

很多人一听说“硬件加速”,立马给元素加will-change: transform。我见过最猛的:一个列表页,20个商品卡片全开了will-change。结果在一台老款安卓机上,手指一滑,页面直接卡成幻灯片。

因为GPU内存不是无限的。每个开启硬件加速的元素,浏览器都会单独给它划一块合成层。层数多了,GPU带宽吃紧,浏览器还得反复把它们“拼”回主画面——这个“拼”的过程,叫合成,它自己就耗资源。

更好的做法是:只在动画真正开始前加will-change,动完立刻移除。或者用contain: layout style paint,告诉浏览器:“这个元素的变化,别影响外面”。

之前帮一个电商团队优化购物车拖拽,他们原来是一进页面就把50个商品全加速。改成只在拖拽瞬间临时开启,帧率一下从飘忽不定稳到了60fps。

GPU不是永动机。你开的层越多,它喘得越急。

requestAnimationFrame还是setTimeout?80%的人选错

你是不是也习惯这么写动画循环:setTimeout(fn, 16)?听起来很准,但现实很骨感——setTimeout的回调时间,得排队等JS主线程空下来。前面如果有个长任务(比如一次大数组排序),你的动画帧就只能干等。

之前优化一个H5小游戏,用setTimeout做角色移动,在低端机上回调间隔忽快忽慢,16ms、40ms来回跳。用户一拖,角色像在瞬移。

换成requestAnimationFrame后,事情简单了:浏览器自动把它和屏幕刷新对齐;页面切到后台,它自动暂停,不白耗电。

操作很简单:把动画的状态更新逻辑,包进requestAnimationFrame回调里。如果真要控帧率,用时间戳差值判断跳不跳帧就行。

你不用算16ms。屏幕的事,交给屏幕自己管。

3个让重排和重绘“隐形”的布局技巧

动画卡顿的第一大元凶,是“布局抖动”。你每改一次widthheightleft,浏览器就得重新算一遍所有元素的位置。如果在动画循环里一边读offsetWidth一边设left,它就会被逼着同步执行布局——这一步,最伤帧率。

之前修过一个轮播图:每次切图先读offsetWidth,再设left。在iPhone 6上,每次切换都卡顿半秒。换成transform: translateX(),延迟缩短了不少。

三个马上能用的技巧:

  • 动画只用transformopacity。这两个属性不触发重排,浏览器能直接甩给GPU。
  • 如果非得读布局信息(比如clientHeight),先把所有“写”操作一口气做完,再统一去“读”。别读一句、写一句,来回折腾。
  • 对首屏外的内容,加上content-visibility: auto。它会让浏览器先不渲染,等用户快滚到那儿了,再加载。之前帮一个资讯站加了这个,首页加载明显变快。

如何用“帧预算”思维砍掉50%的动画工作量

接到动效需求,先别急着敲代码。问自己一句:这个动画,真的需要死磕60fps吗?
很多场景下,30fps人眼根本看不出卡——比如页面淡入、视差滚动、菜单展开。

之前参与一个品牌官网项目,设计师要求首页所有模块“浮入”。我拿帧预算一算:如果30个元素同时动,低端机一帧根本扛不住。最后定了个方案:首屏前3个元素60fps,其余延迟300ms启动、按30fps跑。用户没觉得哪里不对,但帧率全程稳在55fps以上。

帧预算的核心就一条:一帧只有16ms。你得把时间分给最该流畅的地方。
实操方法:打开Chrome DevToolsPerformance面板 → 录一段动画 → 看每帧的“Rendering”和“Painting”耗时。如果经常超10ms,说明该砍了。

移动端最容易被忽视的“像素对齐”陷阱

你写的translateX(10.5px),在Retina屏上会让元素边缘落在“半像素”位置。浏览器为了糊出平滑边缘,得额外做抗锯齿计算——这步运算,比整数像素多花好几倍GPU力气。

之前优化一个跟手拖拽组件,用户反馈“偶尔跳一下”。查了半天,发现每次拖拽计算出来的transform值都带小数。加了一行Math.round()取整,拖拽立马顺了。

另一个坑是scale动画。如果原始尺寸是奇数,缩放后边缘照样容易落半像素。解法有两个:要么确保元素宽高是偶数;要么加backface-visibility: hidden,强制走合成层绕过这个问题。

今天就能执行的1个操作

打开你正在开发的项目,找到那个最卡的动画页面。
F12 → 切到Performance面板 → 点录制按钮,滑动/触发动画10秒 → 停止。
在“Main”线程里找红色标记:有没有LayoutRecalculate Style
如果有,定位到对应代码行,把里面的lefttopwidthheight,全部替换成transform
改完,重录一次对比帧率。你会看到明显提升。

别想着一次治好所有动画。先挑最疼的那个下手——你点下保存的那一刻,卡顿就已经退了一半。