你想从子组件里控制外层那个 transition?Chrome 147 今天把这件事彻底原生化了

SPA 页面过渡动画,你大概已经用过 document.startViewTransition()。但如果你试过在组件里启动 transition,再从另一个地方访问它的状态,会发现这个 API 留了一个不大不小的坑——你得手动把 startViewTransition() 的返回值保存到一个外部变量里,才能在别处拿到 transition 对象。Chrome 147 解决了这个问题。

那个手动保存返回值的问题

Element.startViewTransition() 返回一个 ViewTransition 对象,这个对象有 .ready、.finished、.skipTransition() 这些有用的成员。但如果你不是在发起处访问它,而是想从子组件或者回调函数里访问transition状态,这个对象就必须提前存到一个公共位置。

典型写法是这样的:

let currentVT; // 全局或共享状态

function startTransition() {
  container.startViewTransition(() => {
    updateDOM();
  });
  // 手动保存返回值
  currentVT = container.startViewTransition(() => {
    updateDOM();
  });
}

// 想取消 transition,得靠这个外部变量
function cancelIfNeeded() {
  if (currentVT) {
    currentVT.skipTransition();
  }
}

问题在于:变量传来传去容易造成状态不同步,而且如果 transition 从组件内部发起,想从另一个模块访问,你得额外部署一套共享机制。

element.activeViewTransition 登场

Chrome 147 给 Element 加了一个只读属性 activeViewTransition,任何时候你想知道一个元素上有没有正在进行的 View Transition,直接读这个属性就行,不需要任何外部变量:

function startTransition() {
  // 发起 transition,不需要保存返回值
  container.startViewTransition(() => {
    updateDOM();
  });
}

// 任何地方直接查
function cancelIfNeeded() {
  const vt = container.activeViewTransition;
  if (vt) {
    vt.skipTransition();
  }
}

// 返回 null 说明没有正在进行的 transition
if (!container.activeViewTransition) {
  console.log(目前没有动画在进行);
}

实际用得上的三个场景

场景一:子组件控制外层 transition

Modal 关闭时,触发父容器的 transition:

closeButton.addEventListener(click, () => {
  const dialog = document.querySelector(dialog);
  const container = dialog.parentElement;

  const vt = container.activeViewTransition;
  if (vt) {
    // 等 transition 结束后再关闭
    vt.waitUntil(dialog.close());
  } else {
    dialog.close();
  }
});

场景二:动画序列编排

列表项移动完成后,自动触发下一段高亮动画:

async function moveItem(from, to) {
  const list = document.querySelector(ul);
  list.startViewTransition(() => {
    reorderItems(from, to);
  });

  // 直接从元素上拿到 transition,等它完成
  await list.activeViewTransition.finished;

  highlightMovedItem(to);
}

场景三:调试状态面板

不需要额外部署状态变量,实时显示 transition 运行状态:

setInterval(() => {
  const vt = document.querySelector(.app-container).activeViewTransition;
  statusIndicator.textContent = vt ? `Transition: ${vt.ready ? ready : pending}` : Idle;
}, 100);

和 Document.activeViewTransition 的区别

属性 适用场景 触发方式
Document.activeViewTransition document.startViewTransition() 整页过渡
element.activeViewTransition element.startViewTransition() 组件级过渡

两者可以同时存在,互不干扰。

浏览器支持

Chrome 147+ 和 Edge 147+。Safari 和 Firefox 尚未支持,渐进增强思路:

if (!container.activeViewTransition) {
  // 没有 API 时直接执行,不做动画
  updateDOM();
  return;
}
container.startViewTransition(() => updateDOM());

下一步

打开 DevTools,在你的 SPA 项目里搜索 startViewTransition——如果找到任何手动保存 transition 返回值的代码,立即用 activeViewTransition 重构掉。

评论区

0 条评论

登录后可评论。

阿柯·前端架构 10 阅读