# View Transitions API

> document.startViewTransition() 如何为 DOM 拍快照并动画过渡到新状态、ViewTransition 的 Promise、@view-transition 规则与引擎支持。

`document.startViewTransition(updateCallback)` 把当前页面捕获为一组快照，运行改变 DOM 的回调，捕获新状态，再用 CSS 在两者之间做动画，默认是交叉淡入淡出。当两个页面都用 `@view-transition` 规则选入时，同一机制适用于跨文档导航，这让多页面 PWA 得到了过去需要客户端路由器才有的带动画的路由切换。

## 语法

```js
document.startViewTransition(updateCallback)
document.startViewTransition({ update, types })
transition.updateCallbackDone
transition.ready
transition.finished
transition.skipTransition()
```

```css
@view-transition { navigation: auto; }
```

该方法同步返回一个 `ViewTransition`。同文档过渡存在于 Chrome 111、Safari 18 与 Firefox 144（BCD `api.Document.startViewTransition`）；跨文档的 `@view-transition` 规则存在于 Chrome 126 和 Safari 18.2，Firefox 没有（BCD `css.at-rules.view-transition`）。

## 参数

| 参数 | 类型 | 含义 |
|---|---|---|
| `updateCallback` | 返回值或 Promise 的函数 | 改变 DOM。旧快照在它运行前捕获，新快照在它返回的 Promise 落定后捕获；期间渲染暂停，所以要短。 |
| `options.update` | function | 对象形式下与 `updateCallback` 相同。 |
| `options.types` | 字符串数组 | Level 2：由 `:active-view-transition-type()` 匹配的过渡类型，让一份样式表能区别地处理「前进」与「后退」。 |
| `transition.updateCallbackDone` | `Promise<void>` | 回调的 Promise 落定时落定。 |
| `transition.ready` | `Promise<void>` | 伪元素已存在、动画即将开始时落定；是附加 Web Animations API 动画的时机。过渡被跳过时拒绝。 |
| `transition.finished` | `Promise<void>` | 动画结束、伪元素移除之后落定。 |

动画运行在以 `::view-transition` 为根的伪元素树上：每个 `view-transition-name`（默认的 `root` 加上 CSS 中被赋名的每个元素）对应一个 `::view-transition-group(name)`，其中含 `::view-transition-image-pair(name)`，后者包含 `::view-transition-old(name)`（旧状态的位图）与 `::view-transition-new(name)`（新状态的实时视图）。group 在两个状态之间对位置和尺寸做动画，old 与 new 图像交叉淡化，这一切都用普通的 CSS 动画重新定义样式。

## 异常

| 异常 | 触发条件 |
|---|---|
| `InvalidStateError` `DOMException` | 捕获时两个元素共用同一个 `view-transition-name`。过渡被跳过，`ready` 拒绝，DOM 更新仍然生效。 |
| `TimeoutError` `DOMException` | 更新回调的 Promise 超过引擎限制（Chromium 为 4 s）；过渡被跳过，更新生效。 |
| `AbortError` `DOMException` | 第一次过渡结束前又调用了 `startViewTransition()`，第一次被跳过；`skipTransition()` 以同样方式拒绝 `ready`。 |
| `updateCallback` 抛出的任何错误 | 拒绝 `updateCallbackDone`、`ready` 与 `finished`；DOM 保持回调离开时的状态。 |

过渡被跳过不等于更新失败：上述每种情况下回调的改动都会生效，这正是该 API 在特性检测之后可以无条件调用的原因。

## 示例

单页示例为共享元素做动画；多页示例只需要 CSS；最后一个是守卫。

### 在单页应用中为列表到详情的导航做动画

缩略图和详情页主图在过渡前被赋予同一个 `view-transition-name`，于是 group 把图片从一处动画到另一处，而不是整页交叉淡化。

```js
async function openDetail(item) {
  const thumb = document.querySelector(`[data-id="${item.id}"] img`);
  thumb.style.viewTransitionName = 'hero';
  const transition = document.startViewTransition(async () => {
    await renderDetail(item); // 用详情视图替换列表
    document.querySelector('.detail img').style.viewTransitionName = 'hero';
  });
  await transition.finished;
  thumb.style.viewTransitionName = ''; // 下一次捕获时每个名字只能有一个元素
}
```

事后清除名字很重要：之后的过渡若发现两个 `hero` 元素，会以 `InvalidStateError` 被跳过。

```css
::view-transition-old(root),
::view-transition-new(root) {
  animation-duration: 250ms;
}
::view-transition-group(hero) {
  animation-timing-function: cubic-bezier(0.2, 0, 0, 1);
}
```

根的交叉淡化与 hero group 分别定义样式；没有第一条规则，默认交叉淡化同样是 250 ms，第二条只改变 hero 的缓动。

### 让多页应用选入跨文档过渡

离开和进入的页面都带该规则；浏览器在 `pageswap` 时捕获旧页面、在 `pagereveal` 时捕获新页面，同样的伪元素做动画。仅限同源导航。

```css
@view-transition {
  navigation: auto;
}
header {
  view-transition-name: site-header;
}
```

给页眉命名让它在页面其余部分交叉淡化时保持不动；预渲染的下一页（见 [Speculation Rules](/zh/reference/performance/speculation-rules/)）让过渡无需等待网络就能开始。

### 检测支持并跳过动画

没有该 API 的引擎中该方法不存在。兜底直接应用 DOM 改动，最终状态相同，只是没有动画。

```js
function transitionTo(update) {
  if (!('startViewTransition' in document)) {
    return update(); // 没有 View Transitions：不带动画地更新
  }
  if (matchMedia('(prefers-reduced-motion: reduce)').matches) {
    return update(); // 用户要求减少动态效果：跳过动画
  }
  return document.startViewTransition(update).finished;
}
```

返回 `finished` 让调用方可以等动画结束后再做事，例如聚焦新视图的标题。

:::observed
过渡进行期间，Chrome DevTools 的 Elements 面板把伪元素树显示为 `<html>` 的子节点：`::view-transition`，然后是 `::view-transition-group(root)`、`::view-transition-image-pair(root)`、`::view-transition-old(root)` 与 `::view-transition-new(root)`，每个被命名的元素再多一个 `::view-transition-group(…)`（[Same-document view transitions for single-page applications](https://developer.chrome.com/docs/web-platform/view-transitions/same-document)，developer.chrome.com）。在过渡期间暂停 Animations 面板可以让这棵树留在屏幕上以便检查各 group 的样式；`finished` 落定后伪元素就从树中消失。
:::

## 另请参阅

- [CSS View Transitions Module Level 1](https://www.w3.org/TR/css-view-transitions-1/)（w3.org）
- [CSS View Transitions Module Level 2](https://www.w3.org/TR/css-view-transitions-2/)（w3.org）
- [Same-document view transitions for single-page applications](https://developer.chrome.com/docs/web-platform/view-transitions/same-document)（developer.chrome.com）
- [Speculation Rules API](/zh/reference/performance/speculation-rules/)
- [前进后退缓存（bfcache）](/zh/reference/performance/bfcache/)
- [Navigation API](/zh/reference/capabilities/navigation-api/)