配了三年动画,今天才发现 View Transitions 从来不是给整个页面准备的——element.startViewTransition() 把这件事彻底变了

写播放器的人都知道 View Transitions 好用,但一用就发现整个页面都被它接管了——你想给一个小按钮加展开动画,整个页面的 z-index 和 fixed 元素全乱了。

Chrome 147 发布了一个被严重低估的 API:element.startViewTransition()。它把视图转换从全页面锁变成了元素级工具——一个页面可以同时跑多个独立的过渡动画,互不干扰,页面其余部分保持交互。这是 View Transitions 走向组件化的关键一步。

为什么 View Transitions 一直用着别扭

原始的 document.startViewTransition() 是在整个文档级别做的。当它运行时:

  • 整个页面进入快照状态,视觉上暂停
  • 所有 fixed/absolute 定位元素的层叠上下文可能被打乱
  • 如果页面上有多个组件需要独立动画,只能排队等,或者干脆放弃

element.startViewTransition():把转换作用域收窄到一棵 DOM 子树

Chrome 147 把 startViewTransition() 从 Document 接口下放到了 Element 接口:

document.startViewTransition(() => updateDOM());  // 之前:全文档
element.startViewTransition(() => updateDOM());   // 现在:只影响这个元素子树

作用域的变化带来三个本质改变:

一是可以真正并发。 页面上的列表、侧边栏、弹窗各自调用自己的 element.startViewTransition(),三个动画同时跑,不排队,不互相等待。

二是天然嵌套。 一个 element.startViewTransition() 内部可以再调用另一个子元素的转换,形成父子嵌套关系,浏览器自动处理层叠和包含关系。

三是 fixed 元素不再打架。 因为作用域被限定在子树内,父级页面的 fixed 元素不受影响。

实战:实现一个可并发、可嵌套的动画系统

第一步:基础并发转换

用 Chrome 官方 Demo「Shuffling Lists」的模式,两个列表同时打乱,互不等待:

function shuffle(listEl) {
  listEl.startViewTransition(() => {
    const items = Array.from(listEl.children);
    items.sort(() => Math.random() - 0.5);
    items.forEach(item => listEl.appendChild(item));
  });
}
btn.addEventListener("click", () => { shuffle(listA); shuffle(listB); });

第二步:嵌套——列表项加入侧边栏

「我的巴塔哥尼亚之旅」Demo 展示了三层嵌套场景:网格筛选、商品卡片飞入、侧边栏展开同时进行,三层同时存在,嵌套运行。

第三步:查询活跃的转换

Chrome 147 引入了两个新 API:

ViewTransition.transitionRoot  // 返回转换的作用域根元素
Element.activeViewTransition  // 返回元素上当前正在运行的 ViewTransition 实例

第四步:CSS 伪类控制

作用域根会自动应用 contain: layout 和 view-transition-scope: all,浏览器自动隔离处理,不再干扰父级 fixed 元素:

.my-widget { view-transition-name: my-widget; }
::view-transition-old(my-widget) { animation: fade-out 200ms; }
::view-transition-new(my-widget) { animation: fade-in 200ms; }

:active-view-transition-type():给并发动画加类型标识

CSS View Transitions Level 2 引入了新伪类,给特定类型的转换打标签:

element.startViewTransition(() => updateSomething(), { type: "sidebar" });
:is(:active-view-transition-type(sidebar)) { animation-duration: 300ms; }

浏览器支持:现状和策略

浏览器 支持情况
Chrome 147+ 完整支持
Edge 147+ 完整支持
Firefox 无信号
Safari 无信号

建议用 feature detection 渐进增强:

if (HTMLElement.prototype.startViewTransition) {
  // 使用 element.startViewTransition
} else {
  // 回退到 document.startViewTransition
}

和 MPA View Transitions 的关系

很多人会把 element-scoped 和跨页 MPA 跳转搞混。MPA 跳转用 pageswap / pagereveal 事件监听链接跳转,全程由浏览器控制快照;element-scoped 是在 SPA 场景下由 JS 调用 API 主动触发,作用域限定在元素子树内。两者互补——MPA 处理页面间,element-scoped 处理页面内的组件级动画。

下一步

对于已经在用 View Transitions 的项目,element-scoped 是增量的、向后兼容的改进:

  • 现有 document.startViewTransition() 调用不受影响
  • 可以逐步把需要独立动画的组件改成 element.startViewTransition()
  • 嵌套层级控制在两层以内,避免复杂度失控

配了三年动画,今天才发现 View Transitions 从来不是一个全页锁,而是一套可以拆成原子粒度的工具。

评论区

0 条评论

登录后可评论。

阿柯·前端架构 9 阅读