前言

在前两篇文档中,我们系统梳理了 视图属性(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状态管理和时间轴控制的流畅过渡体验。