feat(motion2): 右栗折叠过渡 + 状态层 hover/pressed 过渡

A2 侧边栏(右栗 AI 栏)折叠/展开:
- 帧泵第三条轨道(pump/rail):展开 medium1 250ms + Emphasized、收起 short4 200ms +
  EmphasizedAccelerate;拆出档走 snap(不做过渡);MotionScale::NONE 下不插轨道
- 宽度在 metrics::rail_width 逐帧插值;面板内容按目标宽排版 + 按动画宽裁切
  (不重排 / 不溢出),宽度回到 48 时换成图标条(图标贴右端,位置不跳)
- pages/agent/mod.rs 的展开档面板纯搬移到 panel.rs(371 → 289 行)

A8 状态层过渡:
- interaction/layer:令牌化的进度时钟(short1 50ms),NONE 当帧落终值
- interaction/hover:取样控件把起跑搬到 Widget::update(早于订阅表求值)
- 按钮五式 + IconButton + 导航项药丸统一走 interaction::layer_color
  (没有取样控件时逐位退回旧口径,出图基线不变)
- components/motion/mod.rs 的 needs_frames / advance 各加一句委托,
  帧订阅仍然只有一处判断点(app/subscription.rs 未改)
This commit is contained in:
AskaEth committed 2026-11-12 20:05:58 +08:00
1 parent c4706c759c
commit b61f342431
22 files changed
+1837 -145

No files matched your search

+10 -2
View File
@@ -54,7 +54,7 @@ use unlucky_design::shape::ShapeScale;
use crate::icons;
use crate::theme::Ui;
use super::interaction::{Base, Interaction};
use super::interaction::{self, Base, Interaction};
use super::layout;
mod assemble;
@@ -359,6 +359,7 @@ pub fn icon_on<'a, M: Clone + 'a>(
let base = base.into();
let geometry = IconGeometry::of(ui);
let (fill, content, _) = variant.roles();
let enabled = on_press.is_some();
let forced = state.or(if on_press.is_some() {
None
} else {
@@ -383,8 +384,15 @@ pub fn icon_on<'a, M: Clone + 'a>(
.padding(Padding::default())
.style(style);
match on_press {
let element: Element<'a, M> = match on_press {
Some(message) => element.on_press(message).into(),
None => element.into(),
};
// A8:与文字按钮同一条口径——强制态不套取样控件,禁用不跟踪。
if state.is_some() {
element
} else {
interaction::tracked(element, enabled)
}
}