我删了800行动画代码,因为View Transitions API只用了两行就搞定了多页面切换

别再用 JavaScript 写页面切换动画了------你的浏览器 2026 年已经内置了一个动画引擎,而且它的性能比你的代码好十倍。

一、我遇到的一个真实问题

上个月做公司官网改版,设计稿里有一堆页面切换的过渡动画:列表页点进去,缩略图要平滑放大过渡到详情页的大图;切 Tab 时内容区要有滑动效果;从首页进入产品页,导航栏要有一个淡入淡出的衔接。

我第一反应很自然------掏出 framer-motion,配合 React Router 的 AnimatePresence,一顿操作猛如虎。写完一看:动画组件 + 状态管理 + 各种 layoutId + DOM 测量逻辑 + 边界处理......光动画相关的代码就写了将近 800 行。

然后问题来了:

  • 页面多了以后,AnimatePresenceexit 动画和新页面的 enter 动画时间线打架,有时候页面内容已经变了,动画还在播上一个页面的残影
  • 那个「缩略图放大到详情图」的共享元素动画,我用了 layoutId,结果因为两个页面 DOM 结构不一样,framer-motion 的自动测量算出来的偏移量歪到姥姥家去了
  • 最致命的是------多页面应用(MPA)完全没法做过渡动画。我们站有一些页面是服务端渲染的静态页,点个链接页面白屏一闪,体验瞬间回到 2010 年

我一度想放弃,跟设计师说「这个做不了」。直到某天在 Chrome 的 What's New 里看到了 View Transitions API

二、View Transitions API 是什么?

一句话解释:浏览器现在内置了一个「截屏→变 DOM→补间动画」的动画引擎。

它的工作流程特别简单,就四步:

  1. 浏览器给当前页面拍一张「快照」
  2. 你更新 DOM(切页面、换内容随你)
  3. 浏览器再拍一张「新快照」
  4. 浏览器自动在两个快照之间做补间动画

以前这四步你得自己写:手动 clone DOM、用 getBoundingClientRect() 算位置、管理 requestAnimationFrame 的时间线、处理各种边缘情况。现在一行代码就够:

javascript 复制代码
// 就这一行,浏览器帮你搞定所有过渡动画
document.startViewTransition(() => updateTheDOM());

实战一:多页面应用(MPA)的跨页面过渡

这才是 View Transitions API 最让我兴奋的地方。以前多页面应用想做过渡动画?做梦。要么你用 Service Worker 搞一套复杂的预加载 + 缓存策略,要么你干脆把所有页面改成 SPA。

现在呢?在 CSS 里加一行就完事了:

css 复制代码
/* 在全局 CSS 里加这行 */
@view-transition {
  navigation: auto;
}

加上这行之后,用户点击站内链接跳转时,浏览器会自动在旧页面和新页面之间做一个淡入淡出的过渡。白屏闪烁?不存在了。

但光淡入淡出还不够------我们真正需要的是共享元素动画

实战二:共享元素过渡(这是真正的魔法)

还记得我说的那个痛点吗------列表页缩略图点击后平滑过渡到详情页大图。View Transitions API 的做法简直像变魔术:

列表页(list.html):

css 复制代码
.product-thumb {
  view-transition-name: product-hero;
}

详情页(detail.html):

css 复制代码
.product-detail-img {
  view-transition-name: product-hero;
}

就这。两个页面的元素只要设置了相同的 view-transition-name,浏览器会自动追踪它们的位置、大小、甚至圆角,然后生成丝滑的补间动画。不用算坐标,不用管 DOM 结构,不用处理异步加载的时序------浏览器全包了。

说实话,我第一次看到效果的时候沉默了大概 5 秒。我之前写的那 800 行代码像个笑话。

实战三:自定义过渡动画风格

默认的淡入淡出虽然够用,但有时你想要更个性化的效果------比如「旧页面向左滑出,新页面从右滑入」。View Transitions API 暴露了伪元素让你自定义:

css 复制代码
/* 旧页面滑出 */
::view-transition-old(root) {
  animation: 0.3s ease both slide-to-left;
}

/* 新页面滑入 */
::view-transition-new(root) {
  animation: 0.3s ease both slide-from-right;
}

@keyframes slide-to-left {
  to { transform: translateX(-30px); opacity: 0; }
}

@keyframes slide-from-right {
  from { transform: translateX(30px); opacity: 0; }
}

而且你可以对不同类型的过渡定义不同的动画效果------前进是右滑入,后退是左滑入,类原生 App 的导航体验一下子就有了:

javascript 复制代码
// 通过设置过渡类型,配合不同的 CSS 动画
document.startViewTransition(() => {
  navigate(direction === 'forward' ? '/next' : '/prev');
});

三、不只是 View Transitions------2026 年这些 CSS 也在帮你删 JS 代码

View Transitions API 解决的是页面过渡的问题。但 2026 年的 CSS 远不止这一个惊喜。下面这三个特性,每一个都能帮你删掉一大坨 JavaScript。

1. 滚动驱动动画(Scroll-Driven Animations)

以前做「滚动到某个位置触发动画」,套路都是 window.addEventListener('scroll', ...) + 一堆 getBoundingClientRect() 计算。主线程一忙动画就掉帧,用户体验烂得一批。

现在 CSS 原生支持了:

css 复制代码
.fade-in-section {
  opacity: 0;
  transform: translateY(30px);

  animation: fade-in linear forwards;
  animation-timeline: view();
  animation-range: entry 0% cover 30%;
}

@keyframes fade-in {
  to { opacity: 1; transform: translateY(0); }
}

这个动画跑在浏览器的合成器线程 上,完全不占用主线程。你滚动得再快也不会掉帧。我之前那个滚动动画的 IntersectionObserver + requestAnimationFrame 方案,删了,全删了。

2. 容器查询(Container Queries)

十几年来我们一直用 @media (max-width: 768px) 做响应式,但这个思路有个本质缺陷:它关心的是视口宽度,不是组件所在容器的宽度。

同一个卡片组件,放在 1200px 宽的主内容区是两列布局,拖到 300px 宽的侧边栏就炸了。以前我们靠「给组件传 variant prop + 条件渲染 class」来解决。现在 CSS 自己能感知容器大小了:

css 复制代码
.card-container {
  container-type: inline-size;
}

@container (min-width: 400px) {
  .card {
    display: flex;
    flex-direction: row;
  }
}

@container (max-width: 399px) {
  .card {
    display: flex;
    flex-direction: column;
  }
}

组件放进任何容器都能自适应,不用再写一堆 variant="compact" 这种 props。这才是真正的「一次编写,到处适用」。

3. :has() 选择器------CSS 终于能「向上」选择了

前端人吐槽了二十年的「CSS 只能向下选,不能根据子元素状态选父元素」,终于被 :has() 终结了:

css 复制代码
/* 表单字段包含无效 input 时,整行变红 */
.form-field:has(input:invalid) {
  border-color: #ef4444;
  background: #fef2f2;
}

/* 购物车有商品时隐藏空状态 */
.cart:has(.cart-item) .empty-state {
  display: none;
}

/* 卡片里包含视频时换深色背景 */
.article-card:has(video) {
  background: #1a1a2e;
  color: #fff;
}

以前这些逻辑都得写在 JavaScript 里------监听表单验证状态、判断数组长度、检查 DOM 结构......现在一个选择器搞定,页面加载时就已经是正确的状态,不存在「先渲染默认状态,JS 跑完了再闪一下」的闪烁问题。

四、效果对比

场景 传统方案 2026 CSS 方案 代码量变化
页面过渡动画 framer-motion + React Router + 状态管理 document.startViewTransition() + CSS 800行 → ~50行
MPA 跨页面过渡 无法实现(或需 Service Worker 黑科技) @view-transition { navigation: auto } 0行 → 1行CSS
共享元素动画 手动测量 DOM + 计算偏移 + 管理生命周期 view-transition-name: xxx ~200行 → 1行CSS
滚动触发动画 IntersectionObserver + JS 动画 animation-timeline: view() ~100行 → 5行CSS
组件级响应式 @media + JS props 传 variant @container 容器查询 ~50行 → 5行CSS
子元素影响父样式 JS 监听 + class 切换 :has() 伪类选择器 ~30行 → 1行CSS

不是说 JavaScript 没用了------而是该让 CSS 干的活,就让 CSS 去干。CSS 跑在浏览器的样式引擎里,天然比 JS 操作 DOM 快一个数量级。

五、总结

核心收获:2026 年了,别再习惯性地用 JavaScript 解决一切问题。CSS 已经进化成了一个强大的「声明式交互引擎」------View Transitions API 管页面过渡,Container Queries 管响应式布局,Scroll-Driven Animations 管滚动交互,:has() 管条件样式。让每个语言做它最擅长的事,你的代码会少很多,性能会好很多。

给读者的三条建议:

  1. View Transitions API 现在就可以用。Chrome 已全功能支持(包括 MPA 跨页面过渡),Safari 和 Firefox 正在追赶。用渐进增强的方式------支持的就用原生动画,不支持的就退化到无动画,不影响功能。
  2. 先审视你的 JS 动画代码 。打开项目搜一下 addEventListener('scroll'IntersectionObserver,看看有多少可以用 Scroll-Driven Animations 替换。每替换一处,你的页面 FPS 就稳定一点。
  3. 不要把 view-transition-name 当 class 用 。每个页面内 view-transition-name 必须是唯一的,只给关键的视觉引导元素(比如主图、标题)设置。
相关推荐
英勇无比的消炎药2 小时前
一站式汇总TinyVue工具案例与真实落地经验
vue.js·前端框架
放下华子我只抽RuiKe53 小时前
FastAPI 全栈后端(三):数据库与 ORM
前端·数据库·react.js·oracle·性能优化·前端框架·fastapi
2601_961845156 小时前
2026法考资料pdf|电子版|资料已整理
开发语言·前端框架·pdf·c#·xhtml·csrf·view design
超哥--15 小时前
B站视频内容智能分析系统(九):React 前端与管理面板
前端·react.js·前端框架
Maimai108081 天前
Web3 前端交易系统如何落地:从下单 UI 到 Operation 编码、签名与实时状态更新
前端·react.js·ui·架构·前端框架·web3
Maimai108081 天前
Web3 前端实时通信如何落地:从 SSE 订阅到行情、订单与账户状态更新
前端·javascript·react.js·前端框架·web3·状态模式
右耳朵猫AI1 天前
前端周刊2026W23 | React 19.2.7、Conductor重写提速、Lovable切换TanStack Start
前端·react.js·前端框架
卷叶小树2 天前
低代码属性面板-Setter体系与高级配置
低代码·前端框架