前言
在前两篇文档中,我们系统梳理了 视图属性(view-*)的两大体系:视图进度时间线和视图过渡命名机制。然而,视图过渡的完整实现离不开另一套核心语法 —— 视图伪元素(::view-*)系列。
如果把视图过渡比作一场舞台剧:
view-transition-name 是演员的 身份证(唯一标识)。
view-transition-class 是演员的 戏服分类(共享样式组)。
::view-*伪元素 则是 舞台灯光与机位(动画效果的具体施加点)。
本文将对 ::view-* 伪元素系列进行深入剖析,并阐明 视图属性 与 视图伪元素 如何协同工作,构成完整的CSS视图过渡动画体系。
一、视图过渡伪元素全景图
1.1 伪元素层级结构
视图过渡在底层会为每个命名的过渡元素创建以下伪元素层级树:
::view-transition
└── ::view-transition-group(root) 或 ::view-transition-group(名称)
└── ::view-transition-image-pair(名称)
├── ::view-transition-old(名称) /* 旧状态快照 */
└── ::view-transition-new(名称) /* 新状态快照 */1.2 五大伪元素速览
角色 | 核心作用 | 动画适用性 |
|---|---|---|
::view-transition | ||
根容器 | 覆盖整个视口的叠加层 | ⚠️ 有限 |
::view-transition-group | ||
分组容器 | 控制整个过渡组的变换 | ✅ 常用 |
::view-transition-image-pair | ||
图像对容器 | 控制新旧状态的混合与叠加 | ⚠️ 特殊 |
::view-transition-old | ||
旧状态快照 | 动画开始前的页面状态 | ✅ 常用 |
::view-transition-new | ||
新状态快照 | 动画结束后的页面状态 | ✅ 常用 |
二、伪元素深度解析
2.1 ::view-transition —— 舞台本身
核心概念
::view-transition 是视图过渡的 根伪元素。它在过渡期间创建一个覆盖在整个页面上方的叠加层,z-index 极高,确保动画不被页面其他元素干扰。
特性解析
/* 默认样式(浏览器内置) */
::view-transition {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none; /* 关键:不拦截鼠标事件 */
}典型应用场景
场景1:全局过渡背景色
/* 为整个过渡添加深色背景层 */
::view-transition {
background-color: rgba(0, 0, 0, 0.5);
backdrop-filter: blur(8px);
}场景2:调试视图过渡
/* 开发时高亮显示过渡层边界 */
::view-transition {
outline: 2px solid red;
outline-offset: -2px;
}重要限制
无法通过 view-transition-name 或 view-transition-class 筛选。全局唯一,始终代表整个过渡根层。
全局唯一,始终代表整个过渡根层。
动画效果有限,主要用于全局装饰。
2.2 ::view-transition-group —— 舞台上的演员区
核心概念
::view-transition-group 是 每个命名元素的独立容器。它是动画应用的最主要目标,控制整个过渡组的变换、位置、尺寸。
选择器语法
/* 通过名称选择(单一演员) */
::view-transition-group(header) {
animation-duration: 0.5s;
}/* 通过类选择(群组演员)- Level 2 */
::view-transition-group(.card-group) {
animation-timing-function: cubic-bezier(0.34, 1.56, 0.64, 1);
}典型应用场景
场景1:统一控制动画时长
/* 所有卡片组共享相同的动画节奏 */
::view-transition-group(.product-card) {
animation-duration: 0.6s;
animation-fill-mode: both;
}场景2:位置变换动画
/* 元素从原位置平滑移动到新位置 */
::view-transition-group(cart-icon) {
animation: moveToCart 0.4s ease-out;
}
@keyframes moveToCart {
from {
transform: translateX(0) translateY(0);
}
to {
transform: translateX(200px) translateY(-50px);
}
}场景3:尺寸过渡动画
/* 卡片展开为详情页的效果 */
::view-transition-group(product-card-1) {
animation: expandCard 0.5s cubic-bezier(0.34, 1.56, 0.64, 1);
}
@keyframes expandCard {
from {
width: 280px;
height: 360px;
}
to {
width: 800px;
height: 600px;
}
}设计意图
::view-transition-group 的存在是为了 隔离不同命名元素的动画作用域。每个 view-transition-name 对应一个独立的 group ,互不干扰。
2.3 ::view-transition-image-pair —— 叠画转场台
核心概念
::view-transition-image-pair 是 旧状态快照和新状态快照的父容器。它控制着两张快照如何混合、叠加、交叉淡化。
默认行为
/* 浏览器内置默认样式 */
::view-transition-image-pair(*) {
position: absolute;
inset: 0;
isolation: isolate; /* 关键:创建独立层叠上下文 */
pointer-events: none;
}典型应用场景
场景1:自定义混合模式
/* 为模态框创建叠加混合效果 */
::view-transition-image-pair(modal-container) {
mix-blend-mode: multiply;
}/* 为离开的元素创建发光效果 */
::view-transition-image-pair(.premium-card) {
mix-blend-mode: screen;
}场景2:控制交叉淡化方式
/* 使用亮度混合实现电影式转场 */
::view-transition-image-pair(.hero-image) {
mix-blend-mode: luminosity;
isolation: auto; /* 解除隔离,允许混合穿透 */
}关键注意事项
isolation: isolate 是默认行为,防止混合模式影响外部。
大多数情况下不需要直接操作 image-pair。
修改 mix-blend-mode 时需谨慎,容易产生视觉混乱。
2.4 ::view-transition-old —— 记忆中的旧画面
核心概念
::view-transition-old 代表 过渡开始前的元素状态快照。它是一个由浏览器自动生成的 替换元素(类似 <img>),内容是不可访问的渲染像素。
特性解析
/* 默认样式(浏览器内置) */
::view-transition-old(*) {
position: absolute;
inset: 0;
/* 关键属性 */
display: block;
width: 100%;
height: 100%;
object-fit: contain; /* 控制快照缩放方式 */
}典型应用场景
场景1:淡出动画
/* 旧元素优雅消失 */
::view-transition-old(.card-transition) {
animation: fadeOut 0.4s ease-in forwards;
}
@keyframes fadeOut {
from { opacity: 1; }
to { opacity: 0; }
}场景2:向左滑出
/* 页面离开时向左滑动 */
::view-transition-old(.page-transition) {
animation: slideOutLeft 0.3s ease-in forwards;
}
@keyframes slideOutLeft {
from {
opacity: 1;
transform: translateX(0);
}
to {
opacity: 0;
transform: translateX(-100px);
}
}场景3:缩放消失
/* 卡片选中后缩起消失 */
::view-transition-old(.product-card) {
animation: shrinkAway 0.5s cubic-bezier(0.68, -0.55, 0.27, 1.55) forwards;
}
@keyframes shrinkAway {
to {
opacity: 0;
transform: scale(0.5);
}
}设计意图
::view-transition-old 负责 旧状态的退出表演。它是观众对元素“之前在哪里”的视觉记忆。
2.5 ::view-transition-new —— 崭新登场的主角
核心概念
::view-transition-new 代表 过渡完成后的元素状态快照。与 old 相同,它也是由浏览器自动生成的替换元素。
特性解析
/* 默认样式(浏览器内置) */
::view-transition-new(*) {
position: absolute;
inset: 0;
display: block;
width: 100%;
height: 100%;
object-fit: contain;
/* 默认新元素是透明的,需要动画使其显现 */
opacity: 0;
}典型应用场景
场景1:淡入动画
/* 新元素优雅出现 */
::view-transition-new(.card-transition) {
animation: fadeIn 0.4s ease-out 0.1s both;
}
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}场景2:从右滑入
/* 新页面从右侧滑入 */
::view-transition-new(.page-transition) {
animation: slideInRight 0.3s ease-out forwards;
}
@keyframes slideInRight {
from {
opacity: 0;
transform: translateX(100px);
}
to {
opacity: 1;
transform: translateX(0);
}
}场景3:弹性弹出
/* 卡片展开时的弹性效果 */
::view-transition-new(.product-card) {
animation: bounceIn 0.6s cubic-bezier(0.34, 1.56, 0.64, 1);
}
@keyframes bounceIn {
from {
opacity: 0;
transform: scale(0.6);
}
70% {
opacity: 1;
transform: scale(1.05);
}
to {
transform: scale(1);
}
}三、视图属性与视图伪元素的协作体系
3.1 核心协作模型
【声明阶段】 【执行阶段】 【呈现阶段】
────────────────────────────────────
view-transition-name ::view-transition-group 动画实际发生
│ │
│ —— 映射为 ——> │
│ │
└─────────────────> ::view-transition-group(名称)
view-transition-class ::view-transition-group 样式共享复用
│ │
│ —— 聚合为 ——> │
│ │
└─────────────────> ::view-transition-group(.类名)3.2 五阶段协作流程
阶段1:注册演员(view-transition-name)
.card-1 { view-transition-name: card-1; }
.card-2 { view-transition-name: card-2; }阶段2:分配戏服(view-transition-class)
.card-1, .card-2 {
view-transition-class: card-group;
}阶段3:搭建舞台(::view-transition)
/* 浏览器自动为整个过渡创建根舞台 */阶段4:设置机位(伪元素选择器)
/* 通过名称选择单个演员 */
::view-transition-group(card-1) {
animation-duration: 0.5s;
}/* 通过类选择群组演员 */
::view-transition-group(.card-group) {
animation-timing-function: ease-out;
}阶段5:指导表演(old/new动画)
::view-transition-old(.card-group) {
animation: fadeOut 0.4s;
}
::view-transition-new(.card-group) {
animation: fadeIn 0.4s;
}3.3 协作关系速查表
协作伪元素 | 协作关系 | 作用阶段 |
|---|---|---|
视图属性:view-transition-name: foo | ||
::view-transition-group(foo) | 1:1映射 | 标识绑定 |
::view-transition-image-pair(foo) | 1:1映射 | 容器嵌套 |
::view-transition-old(foo) | 1:1映射 | 状态快照 |
::view-transition-new(foo) | 1:1映射 | 状态快照 |
视图属性:view-transition-class: bar | ||
::view-transition-group(.bar) | N:1聚合 | 样式复用 |
::view-transition-image-pair(.bar) | N:1聚合 | 样式复用 |
::view-transition-old(.bar) | N:1聚合 | 样式复用 |
::view-transition-new(.bar) | N:1聚合 | 样式复用 |
四、完整示例:视图属性与伪元素协同
以下示例展示一个 产品卡片→详情页 的过渡,完整演示 view-* 与 ::view-* 的协作关系。
4.1 HTML结构
<div class="product-grid">
<div class="product-card" id="card-1">
<img src="watch.jpg" alt="智能手表">
<h3>智能手表</h3>
<span class="price">¥1299</span>
</div>
<!-- 更多卡片 -->
</div>
<div class="detail-modal" id="modal-1">
<div class="modal-content">
<!-- 详情内容 -->
</div>
</div>4.2 CSS协作体系
/* ========== 视图属性层(声明演员)========== */
.product-card {
/* 唯一标识:每个卡片都有自己的名字 */
view-transition-name: var(--card-id);
/* 共享分类:所有卡片属于同一个组 */
view-transition-class: card-group;
}
.detail-modal {
view-transition-name: modal-container;
view-transition-class: modal-transition;
}/* ========== 视图伪元素层(指导表演)========== */
/* 1. 群组动画:所有卡片共享进入动画 */
::view-transition-group(.card-group) {
animation-duration: 0.6s;
animation-timing-function: cubic-bezier(0.34, 1.56, 0.64, 1);
animation-fill-mode: both;
}/* 2. 个体差异:特定卡片可覆盖动画 */
::view-transition-group(card-1) {
animation-duration: 0.8s; /* 覆盖群组设置 */
}/* 3. 旧状态退场:卡片消失动画 */
::view-transition-old(.card-group) {
animation: cardExit 0.5s ease-in forwards;
}
@keyframes cardExit {
to {
opacity: 0;
transform: scale(0.8);
}
}/* 4. 新状态入场:模态框出现动画 */
::view-transition-new(.modal-transition) {
animation: modalEnter 0.5s ease-out forwards;
}
@keyframes modalEnter {
from {
opacity: 0;
transform: translateY(50px) scale(0.9);
}
to {
opacity: 1;
transform: translateY(0) scale(1);
}
}/* 5. 全局氛围:整个过渡层添加微光效果 */
::view-transition {
background: radial-gradient(circle, transparent 30%, rgba(0,0,0,0.2) 100%);
pointer-events: none;
}4.3 JavaScript触发协作
async function openProductDetail(productId, cardElement) {
// 1. 动态设置唯一名称(确保不重复)
cardElement.style.viewTransitionName = `product-${productId}`;
// 2. 启动视图过渡
const transition = document.startViewTransition(() => {
// 3. 切换状态:隐藏卡片,显示模态框
cardElement.classList.add('hidden');
modalElement.classList.remove('hidden');
});
// 4. 等待动画完成
await transition.finished;
}五、高级协作模式
5.1 多阶段动画协作
/* 阶段1:旧元素淡出 */
::view-transition-old(.card-group) {
animation: fadeOut 0.3s ease-in;
}/* 阶段2:新元素缩放进入(延迟执行) */
::view-transition-new(.card-group) {
animation: scaleIn 0.5s ease-out 0.2s; /* 等待旧元素消失 */
}
@keyframes scaleIn {
from { opacity: 0; transform: scale(0.8); }
to { opacity: 1; transform: scale(1); }
}/* 阶段3:整个组位置移动(并行执行) */
::view-transition-group(.card-group) {
animation: moveToCenter 0.6s cubic-bezier(0.34, 1.56, 0.64, 1);
}
@keyframes moveToCenter {
from { transform: translateX(0) translateY(0); }
to { transform: translateX(200px) translateY(-50px); }
}5.2 条件性协作(基于媒体查询)
/* 移动设备:快速切换 */
@media (prefers-reduced-motion: no-preference) {
::view-transition-group(.card-group) {
animation-duration: 0.4s;
}
}/* 桌面设备:优雅的慢速动画 */
@media (min-width: 1024px) {
::view-transition-group(.card-group) {
animation-duration: 0.8s;
}
::view-transition-new(.modal-transition) {
animation: scaleIn 0.6s cubic-bezier(0.34, 1.56, 0.64, 1);
}
}/* 省电模式:完全禁用动画 */
@media (prefers-reduced-motion: reduce) {
::view-transition-group(*),
::view-transition-old(*),
::view-transition-new(*) {
animation: none !important;
}
}5.3 错误处理与回退协作
class ViewTransitionCoordinator {
static async safelyTransition(updateCallback, options = {}) {
// 1. 检查是否支持视图过渡
if (typeof document.startViewTransition !== 'function') {
updateCallback();
return;
}
// 2. 检查是否支持过渡类(Level 2)
const supportsTransitionClass = CSS.supports('view-transition-class', 'test');
try {
const transition = document.startViewTransition(updateCallback);
// 3. 根据支持能力应用不同动画
if (supportsTransitionClass) {
// Level 2: 使用类选择器,更简洁
document.documentElement.classList.add('level2-support');
} else {
// Level 1: 仅使用名称选择器,需要更精细控制
document.documentElement.classList.add('level1-support');
}
await transition.finished;
} catch (error) {
console.warn('视图过渡失败,使用降级方案:', error);
updateCallback();
}
}
}六、协作最佳实践
6.1 推荐模式
1. 职责分离清晰
/* ✅ 视图属性只负责标识 */
.card { view-transition-class: product; }
.header { view-transition-name: site-header; }/* ✅ 伪元素只负责动画 */
::view-transition-group(.product) {
animation: reveal 0.5s;
}2. 群组优先,个体覆盖
/* ✅ 先定义通用规则 */
::view-transition-group(.card) {
animation-duration: 0.5s;
}/* ✅ 再覆盖特殊案例 */
::view-transition-group(premium-card) {
animation-duration: 0.8s; /* 高级卡片更优雅 */
}3. 动画逻辑集中管理
/* ✅ 所有视图过渡动画定义在一起 */
.view-transition-animations {
/* 空类,仅作为语义分组 */
}
::view-transition-group(.fade-transition) { /* ... */ }
::view-transition-group(.slide-transition) { /* ... */ }
::view-transition-group(.scale-transition) { /* ... */ }6.2 避免模式
1. 过度嵌套
/* ❌ 伪元素不支持嵌套选择器 */
::view-transition-group(.card) ::view-transition-new {
/* 无效!不会生效 */
}2. 名称与类混用
/* ❌ 不可在同一选择器中混用 */
::view-transition-group(.card card-1) {
/* 无效!不支持 */
}3. 直接操作快照内容
/* ❌ 无法修改快照内部样式 */
::view-transition-old(.card) img {
/* 无效!快照是替换元素,无内部结构 */
}七、总结:完整的视图过渡协作模型
7.1 双维度协作矩阵
维度 | 视图属性 (view-*) | 视图伪元素 (::view-*) |
|---|---|---|
时机 | 过渡 开始前 声明 | 过渡 进行中 执行 |
职责 | 标识元素、分组归类 | 施加动画、控制表演 |
语法 | 属性声明在元素本身 | 伪元素声明在样式表 |
选择 | view-transition-name | ::view-transition-group(名称) |
聚合 | view-transition-class | ::view-transition-group(.类名) |
特异性 | 无(属性本身无权值) | (0,0,1) 与类选择器同级 |
作用域 | 单个元素 | 所有匹配的命名元素 |
7.2 协作本质
view-* 与 ::view-* 的协作,本质上是“声明”与“执行”的分离:
view-transition-name/class:告诉浏览器 —— “这些元素要参与过渡,请记住它们”。
:view-*伪元素:告诉浏览器—— “对于记住的元素,请这样播放动画”。
这种分离带来了巨大的架构优势:
HTML/CSS/JS各司其职:标识在元素上,动画在样式表中,触发在脚本里。
关注点完全解耦:修改动画无需触碰DOM,修改参与元素无需触碰动画代码。
复用性最大化:相同的动画可应用于无数不同的元素组。
总结
核心属性 | 核心伪元素 | 协作产物 |
|---|---|---|
体系: 视图时间线 | ||
view-timeline-name | view()函数 | 滚动驱动动画 |
体系: 视图过渡 | ||
view-transition-name | :view-transition-group() | 状态切换动画 |
view-transition-class | ::view-transition-old/new() | 群组动画复用 |
视图过渡的完整工作流:
【准备阶段】 【执行阶段】 【呈现阶段】
view-transition-name → document.startViewTransition → ::view-transition
+ + ├─ group(名称)
view-transition-class → 状态更新回调 → ├─ image-pair
│ │ │ ├─ old
└──────── 映射/聚合 ────────┘ │ └─ new
└─ ...这五个伪元素与两个视图过渡属性共同构成了CSS视图过渡的完整叙事语法—— 命名演员、分配角色、搭建舞台、指导表演。掌握这套协作体系,您就能以声明式的优雅姿态,构建出以往需要复杂JavaScript状态管理和时间轴控制的流畅过渡体验。