Transition
Transition 基础原型
这段 Demo 展示纯命令式控制状态机。
- Enter:从任意状态进入
entering(合法路径为closed → entering或leaving → entering);若已在进入中则忽略。 - Leave:从任意状态进入
leaving(合法路径为entered → leaving或entering → leaving);若已在离开中则忽略。 - Complete:将
entering推进到entered,或将leaving推进到closed。
典型完整操作链路:
Enter → Complete → Leave → Complete这段 Demo 展示通过 open prop 受控驱动状态机。宿主平台在 CSS 过渡结束后会调用 complete() 来自动推进状态。
- Toggle Open:翻转
open值。- 若当前是
closed/leaving,切到open=true会进入entering → entered。 - 若当前是
entered/entering,切到open=false会进入leaving → closed。
- 若当前是
- Click Me:点击 Box 本身也能触发 Toggle Open,与按钮行为一致。
典型操作链路:
Toggle Open(显示)→ Toggle Open(隐藏)在 Demo 中 CSS 设置了 0.3s 过渡,因此 entering 和 leaving 状态会在对应时长后自动 complete。
Transition 是一个底层基础组件,用于管理元素的存在状态生命周期。它本身不渲染可见内容,而是提供状态机治理,让宿主平台(CSS、React、Vue 等)根据状态驱动实际动画。
asTransition 被实现为一个特权 hook(privileged hook),而不是普通的 asHook。它通过系统级模块 @proto.ui/module-presence 来控制结构挂载与卸载的时机,这一行为跨越所有适配器(Web Component、React、Vue)。
为什么要做成特权 hook?因为”在入场动画开始前让元素不存在于 DOM 中”,以及”在退出动画完成后再延迟卸载”,这些都属于宿主平台级能力,超出了普通原型作者通过常规 API 能访问的范围。
在 module-presence 管理下的生命周期:
absent → (setIntent enter) → mounting → present → (setIntent leave) → unmounting → absent- 阻塞挂载:当过渡从
closed状态启动且尚未收到进入意图时,运行时的mounted阶段会被延后。宿主适配器只有在 presence handle 解析挂载后,才会真正渲染并挂载组件。 - 延迟卸载:当离开动画开始(
entered → leaving),宿主适配器会保持组件在 DOM 中。只有在complete()将状态推进到closed,并且 presence handle 确认卸载后,适配器才会真正移除组件。
这让状态机能与 CSS 过渡正确配合:即便适配器原本会立即移除元素,entering 和 leaving 阶段也能保证 DOM 元素始终存在。
与 module-presence 的关系
Section titled “与 module-presence 的关系”transition 是 presence 的特化(specialization)。你可以把 @proto.ui/module-presence 想像成底层的结构治理层,而 asTransition 是在它之上构建的、面向用户的状态机。
module-presence决定宿主元素何时可以出现在 DOM 中。它管理absent → mounting → present → unmounting → absent这些相位。asTransition决定状态为何变化。它暴露 4 状态状态机(closed → entering → entered → leaving → closed),并把用户的操作(props、controls)转换成 presence 意图(setIntent('enter')或setIntent('leave'))。
这种分离带来的好处是:
- 适配器只需要关心 presence。 React 或 Vue 适配器不需要了解
entering和entered的区别,只需响应module-presence发出的mount()/unmount()信号即可。 - Transition 使用者只需要关心状态机。 不需要操心宿主层面的结构挂载时机,因为
asTransition已经自动把这些委托给module-presence。 - 物理移除的语义因宿主而异。 在 Web Component 中,浏览器通过
connectedCallback/disconnectedCallback控制结构存在。而在 React 和 Vue 中,当 presence handle 发出卸载信号时,适配器会渲染null,因此在leaving → closed完成后,元素会被真正从 DOM 中移除。为了保证在重新进入(closed → entering)时 CSS 过渡仍能正确播放,React/Vue 适配器会在首次挂载后的第一帧插入一个 baseline:临时强制data-transition-state="closed",触发强制回流(reflow),然后恢复真实状态。这让浏览器在切换到entering之前获得必要的样式锚点。
1. 始终将状态变化与 CSS 过渡配对使用
Section titled “1. 始终将状态变化与 CSS 过渡配对使用”Transition 只管理逻辑状态。如果你没有为 data-transition-state="entering" 和 data-transition-state="leaving" 提供 CSS,元素在 complete() 被调用时就会瞬间出现或消失。
2. 受控模式下,宿主必须在 CSS 结束后调用 complete()
Section titled “2. 受控模式下,宿主必须在 CSS 结束后调用 complete()”使用 open prop 时,适配器(或你的包装代码)负责监听 CSS 过渡结束事件并调用 controls.complete()。如果不做这一步,状态会永远卡在 entering 或 leaving。
典型的受控模式写法:
// 设置 open=true 并应用 CSS 过渡后el.addEventListener('transitionend', () => { exposes.controls.complete();});3. 需要挂载时播放动画请使用 appear
Section titled “3. 需要挂载时播放动画请使用 appear”如果你希望组件在首次渲染时就能播放进入动画,请设置 appear: true。否则组件会在挂载时直接跳到 entered,不会播放进入动画。
4. 根据交互体验选择中断策略
Section titled “4. 根据交互体验选择中断策略”'reverse'(默认):适合大多数 UI(菜单、提示框)。用户快速开关时,动画会平滑反向播放。'wait':适合必须保证每场动画都完整走完的场景(例如分步向导的背景面板不应跳过动画)。'immediate':适合对瞬时状态切换要求高的场景。当前动画会被丢弃,新动画从干净状态重新开始。
5. 不要直接修改 transition 状态
Section titled “5. 不要直接修改 transition 状态”始终通过暴露的 controls(controls.enter()、controls.leave()、controls.complete())或 open prop 来驱动状态。直接修改 transitionState 会绕过 presence 意图层,导致适配器状态不一致。
6. 在 leaving 期间保持 DOM 子树稳定
Section titled “6. 在 leaving 期间保持 DOM 子树稳定”由于 module-presence 可能会在 leaving 阶段保持元素在 DOM 中,请避免在此时销毁子元素状态(例如拆毁 <video> 元素或取消网络请求),直到适配器真正卸载或状态到达 closed。
closed → entering → entered → leaving → closed- closed: 不可见,通常
display: none或从 DOM 移除 - entering: 进入动画中,DOM 存在,应用进入样式
- entered: 完全可见,稳定状态
- leaving: 离开动画中,DOM 仍存在,应用离开样式
| Prop | 类型 | 默认值 | 说明 |
|---|---|---|---|
open | boolean | - | 受控模式:目标存在状态 |
defaultOpen | boolean | false | 非受控模式:初始状态 |
appear | boolean | false | 是否在挂载时播放进入动画 |
enterDuration | number | 300 | 进入动画预期时长(ms) |
leaveDuration | number | 200 | 离开动画预期时长(ms) |
interrupt | 'reverse' | 'wait' | 'immediate' | 'reverse' | 中断策略 |
命令式 API
Section titled “命令式 API”通过 controls expose 访问:
enter(): 触发进入leave(): 触发离开complete(): 标记当前过渡完成,推进到下一状态
v0 范围说明
Section titled “v0 范围说明”base-transition v0 仅管理单个元素自身的存在状态生命周期(closed → entering → entered → leaving → closed)。
若需要 parent/child 嵌套协调退出(例如外层 Dialog 等待内层某个子元素先完成离开动画后再关闭),这属于 transition-group / boundary 类的独立抽象,不在当前 v0 范围内。