View Transitions API 实战:跨页面/跨文档动画过渡方案
前端工程(更新于 2026年6月13日)
页面过渡的演进
| 方案 | 适用范围 | 实现复杂度 | 浏览器原生 | 性能 |
|---|---|---|---|---|
| CSS transition | 单组件 | 低 | ✅ | 高 |
| Framer Motion / GSAP | SPA 路由 | 高 | ❌ | 中 |
| Shared Element Transition | SPA + MPA | 低 | ✅ | 高 |
| View Transitions API | SPA + MPA | 低 | ✅ | 高 |
View Transitions API 让浏览器自动处理旧→新状态的快照、动画和清理,开发者只需声明"何时过渡"和"如何动画"。
SPA 中的 View Transitions
基本用法:document.startViewTransition
function navigateTo(newRoute: string) {
if (!document.startViewTransition) {
updateDOM(newRoute);
return;
}
document.startViewTransition(() => {
updateDOM(newRoute);
});
}
浏览器自动执行:
- 截取旧状态快照(
::view-transition-old) - 执行回调,更新 DOM
- 截取新状态快照(
::view-transition-new) - 在两组快照之间执行交叉淡入淡出动画
自定义过渡动画
默认交叉淡入淡出
::view-transition-old(root) {
animation: 0.3s ease-in both fade-out;
}
::view-transition-new(root) {
animation: 0.3s ease-out both fade-in;
}
滑动切换效果
::view-transition-old(root) {
animation: 0.4s ease-in both slide-to-left;
}
::view-transition-new(root) {
animation: 0.4s ease-out both slide-from-right;
}
@keyframes slide-to-left {
to { transform: translateX(-100%); }
}
@keyframes slide-from-right {
from { transform: translateX(100%); }
}
共享元素过渡:跨组件动画
给元素设置 view-transition-name,浏览器自动在旧→新位置间做 FLIP 动画:
.card-image {
view-transition-name: card-img;
}
.card-title {
view-transition-name: card-title;
}
实战:列表→详情页的 Hero 动画
function openDetail(cardId: string) {
document.startViewTransition(() => {
document.querySelector('.list-page')!.style.display = 'none';
document.querySelector('.detail-page')!.style.display = 'block';
// 设置共享元素名称
document.querySelector(`#card-${cardId} img`)!
.style.viewTransitionName = 'hero-image';
document.querySelector('.detail-hero img')!
.style.viewTransitionName = 'hero-image';
});
}
列表卡片图片 → 详情页大图之间自动做平滑缩放+位移动画。
MPA 跨文档过渡
Chrome 111+ 支持跨文档(跨页面导航)的 View Transitions:
启用跨文档过渡
<meta name="view-transition" content="same-origin">
只需一行 meta 标签,同源导航自动触发交叉淡入淡出!
配合 CSS 自定义
/* 仅在前进导航时滑动 */
::view-transition-old(root) {
animation: 0.25s ease-in both slide-to-left;
}
::view-transition-new(root) {
animation: 0.25s ease-out both slide-from-right;
}
/* 后退导航反向滑动 */
::view-transition-old(root) {
animation: 0.25s ease-in both slide-to-right;
}
::view-transition-new(root) {
animation: 0.25s ease-out both slide-from-left;
}
使用 Navigation Type 区分方向
@media (navigation: forward) {
::view-transition-old(root) { animation: slide-to-left; }
::view-transition-new(root) { animation: slide-from-right; }
}
@media (navigation: back) {
::view-transition-old(root) { animation: slide-to-right; }
::view-transition-new(root) { animation: slide-from-left; }
}
高级技巧
排除不需要动画的元素
/* 固定头部和底部不参与过渡 */
header, footer {
view-transition-name: none;
}
动态设置 view-transition-name
// 列表中每张卡片有唯一名称
document.querySelectorAll('.card').forEach((card, i) => {
card.style.viewTransitionName = `card-${i}`;
});
等待图片加载后再过渡
document.startViewTransition(async () => {
updateDOM(newRoute);
await document.querySelector('.detail-hero img')?.decode();
});
与工具库的结合
性能考量
| 方面 | 建议 |
|---|---|
| 快照大小 | 避免对大尺寸元素设置 view-transition-name |
| 过渡时长 | 200-400ms 是感知最佳区间 |
| 并发过渡 | 多个共享元素同时动画时 GPU 负载增加 |
| 布局抖动 | 回调中避免触发布局重排,否则快照不准确 |
常见问题
startViewTransition 回调中可以异步更新 DOM 吗?
可以。回调支持 async 函数,浏览器会等待 Promise resolve 后再截取新快照。但等待期间旧快照一直显示,过长的等待会导致卡顿感。
跨文档过渡和 SPA 过渡的区别?
SPA 用 document.startViewTransition() 手动触发,MPA 用 <meta name="view-transition"> 自动触发。两者共享同一套 CSS 伪元素和动画定义。
如何调试 View Transitions?
Chrome DevTools → Elements 面板可查看 ::view-transition-group 等伪元素树。Animation 面板可逐帧调试过渡动画。
总结
View Transitions API 将页面过渡从手动 FLIP 计算变为浏览器原生快照+动画,大幅降低实现复杂度。SPA 用 document.startViewTransition(),MPA 用 meta 标签,共享元素用 view-transition-name——三种场景统一在同一套 CSS 伪元素体系下,是现代 Web 应用页面过渡的最佳方案。
本站提供浏览器本地工具,免注册即可试用 →
#View Transitions#页面过渡#MPA#SPA#跨文档动画