CSS 动画真的比 JS 快?Josh Comeau 做了组实验,结果跟直觉不一样

做前端动画的,几乎都听过这句话:"CSS 动画比 JavaScript 快,能用 CSS 就别用 JS。"

但很少有人追问:为什么快?快多少?JS 库有没有办法绕过这个瓶颈?

Josh W. Comeau------那个写《CSS for JavaScript Developers》的 Josh------最近在他的博客上发了篇文章,直接拿 CSS keyframes、原生 JS requestAnimationFrame、Motion(原 Framer Motion)、GSAP 做了对比实验。

结论跟很多人的直觉不完全一样

一、先做个实验:main thread 被阻塞时,谁的动画会卡?

Josh 设计了一个很直观的对比场景:

两个球,左右来回弹跳。一个用 CSS @keyframes 实现,一个用原生 JS requestAnimationFrame 实现。

CSS 版本:

css 复制代码
@keyframes bounce {
  to {
    transform: translateX(calc(var(--bounce-magnitude) * -1));
  }
}

.ball {
  --bounce-magnitude: 200px;
  animation: bounce 1000ms infinite alternate;
}

JS 版本:

javascript 复制代码
const startTime = performance.now();
const ball = document.querySelector('.ball');

function animate() {
  const elapsedTime = performance.now() - startTime;
  // 计算 x 位置...
  ball.style.transform = `translateX(${x}px)`;
  window.requestAnimationFrame(animate);
}

然后 Josh 在页面里加了一个"阻塞 main thread"的模拟:每隔几秒,故意占用 main thread 一段时间。

结果:CSS 动画丝滑如初,JS 动画直接冻住。

二、为什么 JS 动画会卡?不是计算慢,是"抢不过"

很多人以为 JS 动画慢,是因为"每帧都要计算位置,CPU 算不过来",或者"JS 和 DOM 之间跨桥有开销"。

Josh 的原话:

"Modern browser engines can tackle all of that stuff without breaking a sweat; even on low-end devices, that work happens in a tiny fraction of a millisecond, way too quick to affect the framerate of our animation."

(现代浏览器引擎处理这些完全不在话下;即使在低端设备上,这些工作也发生在不到一毫秒的时间里,远远快于影响动画帧率的程度。)

真正的原因只有一个:JS 动画跑在 main thread 上,跟应用里的所有其他 JS 抢 CPU。

现代 web 应用的 main thread 有多忙?

  • React 框架不断做 DOM diff、更新 UI
  • 每次 fetch 请求,响应解析走 main thread
  • 用户交互、事件处理、状态管理......全在 main thread

Josh 举了个很常见的场景:

"So, if you've ever seen a spinner freeze for a moment before the UI is updated, this is why! JavaScript-based animations have to compete for processing power with the rest of the application."

(所以,如果你见过 spinner 在 UI 更新前卡一下,这就是原因!基于 JavaScript 的动画必须跟应用的其他部分竞争处理能力。)

CSS 动画和 keyframe 动画跑在单独的线程上,main thread 被阻塞时,它们不受影响。

三、但 JS 库不一定都卡:Motion 用了 WAAPI,跟 CSS 一样丝滑

Josh 的实验没有停在原生 JS,他继续测了两个主流动画库:

Motion(原 Framer Motion)GSAP

结果出人意料:

  • Motion 的动画,即使 main thread 被阻塞,仍然流畅
  • GSAP 的动画,跟原生 JS 一样,main thread 一堵就卡

为什么 Motion 不卡?

Josh 的解释:

"The secret is that Motion uses the Web Animations API (WAAPI) under the hood. WAAPI is essentially a JavaScript interface that hooks into the same low-level animation engine as CSS keyframe animations. So, Motion is able to run its animations on a separate thread, avoiding the main pitfall of most other JavaScript animation libraries!"

(秘密在于 Motion 底层使用了 Web Animations API(WAAPI)。WAAPI 本质上是一个 JavaScript 接口,接入的是跟 CSS keyframe 动画相同的底层动画引擎。所以 Motion 能把动画跑在单独的线程上,避开了大多数其他 JavaScript 动画库的主要陷阱!)

WAAPI 是浏览器原生 APIelement.animate()),它跟 CSS 动画共享同一个底层引擎,所以也能跑在独立线程。

GSAP 为什么不这么做?Josh 的解释很公允:

"To be fair to GSAP, it's an enormously powerful library which includes features that probably aren't compatible with WAAPI. So, it's not that GSAP made the wrong choice, it's that they're choosing different trade-offs."

(公平地说,GSAP 是一个极其强大的库,包含了很多可能跟 WAAPI 不兼容的功能。所以不是 GSAP 做了错误的选择,而是它们在做不同的权衡。)

GSAP 的复杂时间轴、SVG 路径动画、自定义缓动函数------这些高级功能 WAAPI 支持不了,所以 GSAP 选择留在 main thread 上,换取更强的控制能力。

四、Josh 的选型建议:三层优先级

基于实验结果,Josh 给出了他自己的选型逻辑:

第一层:原生 CSS 动画/过渡

"In my own work, I try to use native CSS animations/transitions whenever I can."

能用 CSS 解决的,优先 CSS。没有 main thread 竞争问题,浏览器原生优化,代码最少。

第二层:Motion(WAAPI 驱动)

CSS 搞不定的场景(复杂交互、程序化控制、手势驱动),上 Motion。它底层走 WAAPI,性能跟 CSS 同级,但 API 更灵活。

第三层:GSAP(或其他高级库)

需要 GSAP 特有的功能(复杂时间轴、SVG 路径、物理效果)时,再考虑 GSAP。接受 main thread 竞争的代价,换取更强的控制能力。

五、CSS 已经强到不需要 JS 库的场景越来越多了

Josh 在文章末尾补了一句,很多人可能没注意到:

"That said, CSS has become so powerful that there really aren't that many cases where we need to reach for an animation library these days; new APIs like View Transitions, linear(), and Animation Timeline make it possible to do all sorts of stuff without JavaScript."

(话说回来,CSS 已经变得如此强大,以至于 nowadays 我们其实没那么多场景需要伸手去拿动画库;新的 API 比如 View Transitions、linear()、Animation Timeline,让各种效果不用 JavaScript 就能实现。)

  • View Transitions API:页面切换时的平滑过渡,浏览器原生支持
  • linear():自定义缓动曲线,以前只能靠 JS 库
  • Animation Timeline:滚动驱动动画、视图驱动动画,以前要靠 ScrollTrigger 这类库

这些 API 的浏览器支持度在快速提升,意味着"CSS 不够用"的边界正在不断外推。

写在最后

Josh 这篇文章的核心结论,可以总结成三句话:

  1. CSS 动画快,不是因为"计算少",是因为"不跟 JS 抢 main thread"
  2. JS 动画不一定都慢------用 WAAPI 的库(Motion)跟 CSS 一样快
  3. GSAP 慢是"选择性的慢"------为了功能复杂度,接受了 main thread 的代价

选型时不要无脑"CSS 优先",要看场景:

  • 简单过渡、hover 效果 → CSS
  • 需要 JS 控制但性能敏感 → Motion(WAAPI)
  • 需要复杂时间轴、SVG、物理效果 → GSAP

Josh W. Comeau :www.joshwcomeau.com/animation/c... Motion 官方文档:motion.dev/

你平时做动画用什么?CSS、Motion、GSAP、还是其他?评论区聊聊~


各位互联网搭子,要是这篇文章成功引起了你的注意,别犹豫,关注、点赞、评论、分享走一波,让我们把这份默契延续下去,一起在前端的海洋里乘风破浪!

相关推荐
zzqssliu1 小时前
taocarts 跨境独立站 SEO 优化实践(多语言 + 反向海淘场景)
java·javascript·php
Front思1 小时前
调取支付宝支付正式环境不可以唤起来,但是沙箱可以
后端
自进化Agent智能体1 小时前
MCP与Hooks:让AI Agent安全连接一切的治理框架
前端
foggyprojects1 小时前
AI 生成 SQL 模板以后,为什么还需要固定 helper 规则
后端
明天一点1 小时前
Cloudflare 通知转发钉钉机器人
前端·后端
前端Hardy1 小时前
前端日历组件,要变天了?Schedule-X v4.6 彻底杀疯了
前端·javascript·后端
如此风景1 小时前
UniCloud学习真经
javascript
Oo_行者_oO1 小时前
微服务 Feign 从“万能公共服务”到“业务客户端”
后端·架构
wei_shuo1 小时前
别再踩坑了!KingbaseES 存储过程与触发器开发避坑实录
后端