View Transition API 是浏览器原生的视图过渡API,用来实现页面/状态切换的平滑动画,支持 SPA单页应用同文档过渡 和 MPA多页应用跨文档页面跳转动画,属于渐进增强特性:不支持的浏览器直接降级为普通页面跳转,不会破坏业务逻辑。
一、它解决什么痛点
过去网页做切换动画有两个难题:
- SPA:路由切换、卡片展开收起,需要手动维护两套DOM、监听位置、写大量JS动画,很容易卡顿;
- MPA:多页面跳转时浏览器会销毁旧页面,原生根本无法做跨页面动画。
View Transition交给浏览器处理快照、图层、DOM更新,开发者只需要标记共享元素、写CSS动画,轻松实现App质感的共享元素转场(卡片图片放大到详情页)。
二、工作原理(核心流程)
调用API触发过渡,浏览器自动执行4步:
- 捕获旧视图快照:对当前页面生成静态图像快照;
- 更新DOM:执行状态变更、路由跳转,渲染新页面DOM;
- 捕获新视图快照:拿到新页面图像;
- 播放过渡动画:浏览器生成伪元素
::view-transition-old、::view-transition-new,自动插值做动画。
共享元素原理:给元素设置
view-transition-name: xxx。新旧页面同名元素,浏览器自动做位置、大小的平滑插值,就是大家常看到的「卡片图片放大动画」。
三、两大使用场景
1. SPA 同文档视图过渡(最常用)
调用 document.startViewTransition(()=>{ 更新DOM/路由 })
// 基础示例
if (document.startViewTransition) {
document.startViewTransition(() => {
// 这里执行DOM更新,例如路由切换
renderDetailPage();
});
} else {
// 降级方案
renderDetailPage();
}给共享图片标记名字:
.card-img {
view-transition-name: hero-img;
}
.detail-img {
view-transition-name: hero-img;
}2. MPA跨文档页面过渡(Level2新增)
不需要JS,同源页面,在前后两个页面CSS都加上:
@view-transition {
navigation: auto;
}点击链接跳转,浏览器自动做页面过渡动画,同样支持view-transition-name共享元素。
⚠️限制:仅支持同源网站,跨域页面无法启用MPA过渡。
四、伪元素(CSS自定义动画)
浏览器自动生成一组伪元素,用来自定义动画时长、缓动、效果:
::view-transition:顶层容器::view-transition-group(name):一组过渡元素容器::view-transition-old(name):旧页面快照::view-transition-new(name):新页面快照
示例自定义淡入淡出:
::view-transition-old(hero-img) {
animation: fade-out 0.3s ease;
}
::view-transition-new(hero-img) {
animation: fade-in 0.3s ease;
}五、浏览器支持
- Chrome / Edge:SPA 111+;MPA跨文档 126+
- Safari:18+支持SPA;18.2+支持MPA
- Firefox:144版本支持同文档SPA过渡,MPA尚在完善
- 属于渐进增强,不支持的浏览器直接忽略动画,业务正常运行。
六、ViewTransition 对象回调(JS监听生命周期)
document.startViewTransition() 返回一个ViewTransition实例,内置Promise:
updateCallbackDone:DOM更新完成ready:快照捕获完成,动画即将开始finished:全部动画播放完毕
const transition = document.startViewTransition(()=>render());
transition.ready.then(()=>{
// 动画准备就绪,可以做额外控制
});
transition.finished.then(()=>{
// 动画结束
});七、常见误区
- ❌
view-transition-name不能重复:同一个页面内,view-transition-name必须唯一,多个元素不能同名; - ❌ 不是所有元素都能做共享动画:display:none、脱离文档流、部分遮挡元素无法生成快照;
- ❌ MPA只能同源:不同域名之间跳转,无法开启跨文档过渡;
- ❌ 不会阻塞页面:动画是在快照图层播放,DOM已经更新完成。
八、适用场景
- 图库、商品卡片点击放大到详情页(共享元素经典场景)
- 列表页 ↔ 详情页路由切换动画
- MPA官网多页面之间平滑转场
- 表单、弹窗、列表排序等局部状态变化动画
九、框架集成
- React:React 19 内置
<ViewTransition>组件封装 - Vue:Vue Router 可以在导航守卫里调用
startViewTransition - Svelte / Solid:原生JS API直接调用
十、总结
View Transition API 是原生Web实现App级转场动画的方案,SPA、MPA都能用,核心优势是不用手写复杂JS动画,浏览器接管快照与图层。利用view-transition-name实现共享元素动画,同时天然支持渐进降级,是现代网站提升UX的重要特性。
十一、Demo
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>View Transition API Demo | 卡片共享元素转场</title>
<style>
*{margin:0;padding:0;box-sizing:border-box;font-family:system-ui;}
body{padding:30px;background:#f4f7fa;}
/* 列表卡片 */
.card{
width:320px;background:#fff;border-radius:14px;overflow:hidden;
box-shadow:0 4px 12px #00000015;cursor:pointer;
}
.card-img{
height:180px;width:100%;object-fit:cover;
/* 共享元素名称:列表图片 */
view-transition-name: hero-img;
}
.card-body{padding:18px;}
.card h2{font-size:18px;margin-bottom:8px;}
.card p{color:#666;font-size:14px;}
/* 详情页面,默认隐藏 */
.detail{
display:none;max-width:700px;margin:0 auto;
}
.detail-img{
width:100%;height:420px;object-fit:cover;border-radius:14px;
/* 和卡片图片同名,触发共享元素动画 */
view-transition-name: hero-img;
}
.detail-body{padding:24px;background:#fff;border-radius:0 0 14px 14px;}
.detail h2{font-size:26px;margin-bottom:12px;}
.detail p{color:#444;line-height:1.7;margin-bottom:16px;}
.back-btn{
padding:10px 16px;background:#2563eb;color:#fff;border:none;border-radius:8px;
cursor:pointer;font-size:15px;
}
/* 自定义过渡动画 */
::view-transition-old(hero-img){
animation: fade-out 0.35s ease-out;
}
::view-transition-new(hero-img){
animation: fade-in 0.35s ease-out;
}
@keyframes fade-out{from{opacity:1}to{opacity:0}}
@keyframes fade-in{from{opacity:0}to{opacity:1}}
</style>
</head>
<body>
<!-- 列表视图 -->
<div class="card" id="cardView">
<img class="card-img" src="https://picsum.photos/id/237/800/400" alt="demo">
<div class="card-body">
<h2>产品卡片</h2>
<p>点击卡片,图片平滑放大到详情页</p>
</div>
</div>
<!-- 详情视图 -->
<div class="detail" id="detailView">
<img class="detail-img" src="https://picsum.photos/id/237/800/400" alt="demo">
<div class="detail-body">
<h2>产品详情</h2>
<p>View Transition API 实现共享元素转场。浏览器自动捕获新旧页面快照,对同名view-transition-name元素做位置、尺寸插值动画。</p>
<button class="back-btn" id="backBtn">返回列表</button>
</div>
</div>
<script>
const cardView = document.getElementById('cardView');
const detailView = document.getElementById('detailView');
const backBtn = document.getElementById('backBtn');
// 切换到详情
cardView.addEventListener('click', ()=>{
// 判断浏览器是否支持API
if(document.startViewTransition){
document.startViewTransition(()=>{
cardView.style.display = 'none';
detailView.style.display = 'block';
})
}else{
// 降级:无动画直接切换
cardView.style.display = 'none';
detailView.style.display = 'block';
}
})
// 返回列表
backBtn.addEventListener('click', ()=>{
if(document.startViewTransition){
document.startViewTransition(()=>{
detailView.style.display = 'none';
cardView.style.display = 'block';
})
}else{
detailView.style.display = 'none';
cardView.style.display = 'block';
}
})
</script>
</body>
</html>