← Dialog / Popover · 浮层与「关闭」的现代解法 / View Transitions:让 <dialog> 从按钮形变出来 待审核 5 / 9
view-transitions · dialog 本体形变

View Transitions:让 <dialog> 从按钮形变出来

进出动画那页用的是 CSS transition 路线(@starting-style / allow-discrete / overlay):逐属性补间,对话框在原地淡入、滑入。本页是另一条路线——View Transitions:浏览器对 DOM 变更前后各拍一张快照,自动算出两态之间的位移 / 缩放 / 淡入。它能做 CSS transition 做不到的事:让对话框从被点的那个按钮「长」出来。

原因在于:按钮和对话框是两个不同元素,CSS transition 只能补间同一元素的属性,无法跨元素衔接。View Transitions 用一个共享的 view-transition-name 把它俩标成「同一东西的两个状态」,浏览器据此把按钮的盒子形变成对话框的盒子。这与图片预览里缩略图放大成大图是同一手法,只是这次形变的是对话框本体。

View Transitions 与 command / commandfor 都偏新,Firefox 尚未跟上。不支持或用户开了「减少动态效果」时,本页一律降级为直接开 / 关(无过渡,功能不缺)——用 Chrome 最新版体验完整效果。View Transitions 的通用机制另见 animation · 与原生对照animation · shared element

1 · 最小形态:把开 / 关包进 startViewTransition

View Transitions 的入口只有一个函数:document.startViewTransition(cb)。它先给当前画面拍一张旧快照,执行 cb 里的 DOM 变更(这里是 showModal() / close()),再拍一张新快照,然后在两张之间补间。关键是所有会改变可见状态的入口都要包进去:打开按钮、关闭按钮、以及 Esc / 点遮罩(走 cancel 事件)。

为什么 cancel 也要拦?Esc 和点遮罩(light dismiss)不会触发任何 click,而是派发 cancel 事件后由浏览器关闭。若只包了关闭按钮,按 Esc 时对话框会瞬间消失。在 cancelpreventDefault() 拦下默认关闭,改走包了 VT 的 close(),所有关闭路径才一致。cancel / close() / requestClose() 的语义见 关闭的学问

2 · 声明式驱动:Invoker Commands + 拦截 command 事件

上面打开按钮还写了 JS。用 Invoker Commands 可以让触发器零 JS:command="show-modal" / command="request-close"commandfor 指向对话框,点击即开 / 关。要叠加 View Transitions,只需在对话框上挂一个 command 事件监听器:拦下命令、包进 startViewTransition、再亲手执行。一个拦截器覆盖所有触发它的按钮。

request-close,别用 closecommand="close" 会直接关闭、不触发 cancel 事件,于是它和 Esc / 点遮罩走的是两条不同路径,容易出现「按钮关有过渡、Esc 关却瞬消」。request-close 把按钮也并入「关闭请求」这条统一通道(触发 cancel、遵守 closedby),所有关闭方式表现一致。本例直接在 command 拦截器里处理 request-close;若你改在 cancel 里统一处理,记得用 request-close 让按钮也经过 cancel

3 · 两个容易出错的细节

其一,文字被拉伸。若按钮文案「打开」和对话框标题共用同一个形变组,补间时短文字会被横向拉成长标题,中途糊掉。解法是给文字单独一个 view-transition-name,让它交叉淡入而非随盒子缩放——盒子形变、文字淡入,各管各的。

其二,背景滚动与溢出。形变过程中对话框的快照可能临时超出视口、或带出滚动。给 ::backdrop 关掉溢出与滚动链,过渡更干净。

两条路线怎么选?单个浮层原地淡入 / 滑入 / 缩放,用 CSS 三态过渡更轻:可被随时打断、性能好、不锁定整页。需要跨元素的连续性——从按钮 / 缩略图形变成对话框、列表项飞进详情——才用 View Transitions:它拍的是整页快照,同一时刻只能有一个、过渡途中不可打断。两者并不互斥,可按浮层的「出身」分别选用。

延伸阅读:Animating the dialog element using View Transitions(pqina)——本页技法的出处,含 command 拦截、名字交接与文字拉伸坑的完整推演;通用机制见 MDN · View Transitions APIMDN · Invoker Commands API