配了三年动画,今天才发现 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 从来不是一个全页锁,而是一套可以拆成原子粒度的工具。
评论区
登录后可评论。