View Transitions API跨文档导航工作原理
View Transitions API是浏览器原生的页面过渡动画方案,从Chrome 111开始支持。核心机制是在页面切换时自动捕获新旧状态的截图,生成一对pseudo-element树(旧状态和新状态),通过CSS动画实现两者的平滑过渡。与SPA中手动管理的过渡动画不同,View Transitions API无需维护复杂的挂载/卸载生命周期。
跨文档导航(Cross-Document View Transitions)是Chrome 126引入的重要扩展,允许MPA(多页应用)在页面跳转时也享有原生过渡效果。以前只有单页应用通过document.startViewTransition()才能使用,现在传统的全页面刷新导航也支持了。
/* 启用跨文档View Transitions - 最简配置 */
@view-transition {
navigation: auto;
}
/* 默认过渡动画 - 300ms淡入淡出 */
::view-transition-old(root) {
animation: 300ms ease-out both fade-out;
}
::view-transition-new(root) {
animation: 300ms ease-in both fade-in;
}
@keyframes fade-out {
to { opacity: 0; }
}
@keyframes fade-in {
from { opacity: 0; }
}
navigation: auto声明后,浏览器在同源页面间的前进/后退/点击链接等导航行为自动触发View Transition,不需要任何JavaScript代码。对于大多数内容型网站,仅此一条CSS规则就足够了。
SPA路由切换中的startViewTransition用法
单页应用的路由切换需要手动调用document.startViewTransition()。这个API接收一个回调函数,回调中执行DOM更新操作,浏览器在回调前后分别截图,生成过渡动画。
// Vue Router + View Transitions
const router = createRouter({
history: createWebHistory(),
routes: [...]
});
router.beforeEach(async (to, from) => {
if (!document.startViewTransition) return;
// 返回false阻止默认导航,由View Transition接管
return new Promise((resolve) => {
document.startViewTransition(async () => {
resolve();
await nextTick(); // 等待Vue完成DOM更新
});
});
});
React框架下需要配合useTransition或flushSync使用。关键点在于确保DOM更新在startViewTransition回调内同步完成,避免异步更新导致截图时捕获到中间状态。
// React Router + View Transitions
function App() {
const [isTransitioning, startTransition] = useTransition();
const handleNavigate = (path) => {
if (!document.startViewTransition) {
navigate(path);
return;
}
document.startViewTransition(() => {
flushSync(() => {
navigate(path);
});
});
};
}
View Transition命名与独立元素动画
默认情况下,View Transitions对整个页面做统一的淡入淡出。通过view-transition-name属性,可以指定特定元素独立参与过渡,实现更精细的动画效果——例如列表页的缩略图滑入详情页的大图位置。
/* 列表页缩略图 */
.card-image {
view-transition-name: hero-image;
}
/* 详情页大图 */
.detail-hero {
view-transition-name: hero-image;
}
/* 命名元素的过渡动画 */
::view-transition-old(hero-image) {
animation: 400ms ease-in-out both scale-down;
}
::view-transition-new(hero-image) {
animation: 400ms ease-in-out both scale-up;
}
@keyframes scale-down {
to { transform: scale(0.8); opacity: 0; }
}
@keyframes scale-up {
from { transform: scale(1.2); opacity: 0; }
}
新旧页面中相同view-transition-name的元素会被浏览器自动配对,生成从旧位置/大小到新位置/大小的补间动画。这个特性被称为共享元素过渡(Shared Element Transition),是实现Page Swap效果的基石。
命名元素必须满足唯一性约束:同一时刻页面中不能有两个相同view-transition-name的元素,否则浏览器会跳过该元素的过渡。动态列表场景下需要用JavaScript在导航时动态设置view-transition-name。
降级方案与浏览器兼容性处理
View Transitions API的浏览器支持截至2026年仍不完整:Chrome/Edge 111+完整支持,Firefox和Safari仅部分实现跨文档导航。生产环境必须提供降级方案。
// 特征检测 + 降级
function navigateWithTransition(path) {
if (document.startViewTransition) {
document.startViewTransition(() => {
window.location.href = path;
});
} else {
// 降级:CSS class过渡或直接跳转
document.body.classList.add('page-leaving');
setTimeout(() => {
window.location.href = path;
}, 300);
}
}
/* CSS降级过渡 */
.page-leaving {
opacity: 0;
transition: opacity 300ms ease;
}
prefers-reduced-motion媒体查询也需要处理。部分用户设置了减少动画的系统偏好,View Transitions应尊重这一设置:
@media (prefers-reduced-motion: reduce) {
::view-transition-group(*),
::view-transition-old(*),
::view-transition-new(*) {
animation-duration: 0.01ms !important;
}
}
将动画时长设为极小值而非0,确保过渡逻辑仍被执行(避免状态不一致),但视觉上几乎瞬间完成,兼顾了无障碍访问需求。
原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/cssviewtransitionsapi-kua-wen-dang-guo-du-dong-hua-yu-spa/