Skip to content

View Transitions API

约 1647 字大约 5 分钟

2025-11-17

基本简介

View Transitions API 是一个浏览器原生功能,允许在页面状态变化时创建平滑过渡效果,无需复杂的 CSS 或 JavaScript 代码。它通过以下方式工作:

在更新 DOM 前捕获当前页面的快照 执行 DOM 更新 创建新旧视图之间的动画过渡

属性方法伪元素

startViewTransition()

Document.startViewTransition(callback): 开始一个新的视图过渡,并返回一个 ViewTransition 对象,callback 通常用于更新 DOM 的回调函数,它返回一个 Promise

这个回调函数在 API 截取了当前页面的屏幕截图后被调用。当回调函数返回的 Promise 兑现时,视图过渡将在下一帧开始。如果回调函数返回的 Promise 拒绝,过渡将被放弃。

ready

viewTransition.ready: 只读属性是一个 Promise。会在伪元素树被创建且过渡动画即将开始时兑(即执行注册的 then 回调)

如果视图过渡无法开始,ready 就会被拒绝。这可能是由于错误的配置,例如重复的 view-transition-name,或者是因为 Document.startViewTransition() 的回调函数抛出异常或返回的 Promise 被拒绝。

finished

viewTransition.finished: 只读属性是一个 Promise。会在过渡动画完成(新的页面视图对用户可见且可交互)时兑现

仅当传递给 document.startViewTransition() 的回调函数抛出异常或返回的 Promise 被拒绝时,finished 才会被拒绝,这表示页面的新状态未被创建。

如果过渡动画无法开始,或在动画期间使用 ViewTransition.skipTransition() 跳过了过渡动画,那么视图过渡依旧可以到达最终状态,因此 finished 依旧会被兑现。

updateCallbackDone

viewTransition.updateCallbackDone: 只读属性是一个 Promise。startViewTransition(callback) 回调函数返回的 Promise 兑现时,该 Promise 也会兑现,当回调函数返回的 Promise 被拒绝时,该 Promise 也会被拒绝

当你不关心过渡动画的成功或失败,而只关心 DOM 是否更新以及何时更新时,updateCallbackDone 非常有用

skipTransition()

viewTransition.skipTransition(): 跳过视图过渡的动画部分,但不跳过更新 DOM 的 startViewTransition(callback) 回调函数

伪元素

::view-transition - 根伪元素

::view-transition-old(root) - 旧视图的快照

::view-transition-new(root) - 新视图的快照

示例-主题切换动画

主题切换动画

基础准备

1、基本结构

<div class="toolbar">
  <el-switch
    class="theme-switch"
    :model-value="isDark"
    @click="onToggleClick"
    :active-icon="Moon"
    :inactive-icon="Sunny"
    active-text="深色"
    inactive-text="浅色"
  ></el-switch>
</div>

2、基本样式

.toolbar {
  position: sticky;
  top: 0;
  z-index: 10;
  padding: 12px 16px;
  display: flex;
  justify-content: flex-end;
  align-items: center;
  background-color: rgba(127, 127, 127, 0.05);
  backdrop-filter: saturate(180%) blur(8px);
  border-bottom: 1px solid var(--el-border-color);
}

3、辅助函数

/**
 * 切换亮暗主题
 */
function setupThemeClass(isDark) {
  document.documentElement.classList.toggle("dark", isDark);
  localStorage.setItem("theme", isDark ? "dark" : "light");
}

/**
 * 计算过渡动画半径
 * 创建从点击点向外扩散的效果,需要计算从点击点到屏幕上最远点的距离
 */
function computeMaxRadius(x, y) {
  const maxX = Math.max(x, window.innerWidth - x);
  const maxY = Math.max(y, window.innerHeight - y);
  return Math.hypot(maxX, maxY);
}

核心步骤

1、创建视图过渡动画

const transition = document.startViewTransition(async () => {
  // 执行主题切换逻辑
  isDark.value = !isDark.value;
  setupThemeClass(isDark.value);
});

2、计算过渡动画半径

// event 点击事件
const x = event.clientX;
const y = event.clientY;
const endRadius = computeMaxRadius(x, y);

3、创建圆形展开动画效果

transition.ready.then(() => {
  const clipPath = [
    `circle(0px at ${x}px ${y}px)`,
    `circle(${endRadius}px at ${x}px ${y}px)`,
  ];
  document.documentElement.animate(
    {
      clipPath: isDark.value ? [...clipPath].reverse() : clipPath,
    },
    {
      duration: 450,
      fill: "both",
      easing: "ease-in",
      pseudoElement: isDark.value
        ? "::view-transition-old(root)"
        : "::view-transition-new(root)",
    }
  );
});

4、设置伪元素的层叠顺序

/* 
  关闭默认的 CSS 动画并防止新旧视图状态以任何方式混合(新状态从旧状态上方“擦除”,而不是过渡
*/
::view-transition-new(root),
::view-transition-old(root) {
  animation: none;
  mix-blend-mode: normal;
}

::view-transition-old(root) {
  z-index: 1;
}

::view-transition-new(root) {
  z-index: 2147483646;
}

html.dark::view-transition-old(root) {
  z-index: 2147483646;
}

html.dark::view-transition-new(root) {
  z-index: 1;
}

完整示例

豫ICP备18027433号 萌ICP备20240840号 本网站由又拍云提供CDN加速/云存储服务