feat(motion): M305 —— 列表增删(错位淡入/退场后才移除)+ 进度插值(到点一次收束)

- 新轨道两条:components/motion/pump/{rows,progress}.rs(纯算术,连拍 advance(dt) 可判)
  · rows:两条清单(画出来的顺序 / 真值)+ 逐行可见度;退场行钉在原位、走完才移除
  · progress:真实读数一跳,画出来的值沿 short4 追上去;到 100% 一次脉冲(0→1→0,不循环)
- 接线:pages/download/model.rs::from_snapshot(scope 内唯一 update 侧入口)
  · 行清单 observe → 差分;退场行的数据进 leaving 池(否则退场行画不出来)
  · 进度 observe(key = 版本 id)
- 视图:rows/list.rs 换成 keyed::Column(按 id 做连续匹配)+ 逐行 revealed(裁切框收放)
  + 同色遮罩淡出;install.rs 画插值后的值 + 收束
- MotionScale::NONE:不插轨道、不留幽灵,值严格等于最终值、零采样
- 顺手:rows/tests.rs 重新挂上 mod(此前一直没编译),i18n 扫描补两个新文件

判据:motion::tests::{rows,progress}、rows/motion_tests(Tree::diff 后未变行状态盒不变)、
出图 DevDocs/artifacts/m305(probe.py 量行距与墨色)
This commit is contained in:
AskaEth committed 2026-11-13 21:42:18 +08:00
1 parent b9536c5833
commit ae33674293
24 files changed
+2175 -153

No files matched your search

@@ -18,13 +18,17 @@
use unlucky_design::motion::{DurationToken, MotionScale};
mod modal;
mod progress;
mod rail;
mod rows;
use super::transition::{
enter_offset, exit_offset, modal_spec, page_spec, Kind, PageFrame, PageSpec,
};
pub(crate) use modal::Modal;
pub(crate) use progress::{Progress, ProgressFrame};
pub(crate) use rail::RailTrack;
pub(crate) use rows::{RowFrame, Rows};
/// 逐帧**推进时间轴**的最短间隔(16 ms ≈ 60 次/秒)。
///
@@ -73,6 +77,10 @@ pub struct Pump {
nav: Option<RailTrack>,
/// 左轨的目标档(`true` = 展开 88)。出厂展开;启动时按落盘档由 [`Pump::snap_nav`] 对表。
nav_open: bool,
/// 列表行增删的轨道(M305):两条清单 + 在跑的可见度轨道,见 `pump/rows.rs`。
rows: Rows,
/// 进度插值 + 收束脉冲(M305),见 `pump/progress.rs`。
progress: Progress,
/// 退场走完了、等调用方真正关掉那个框([`Pump::take_modal_closed`] 取走一次)。
close_pending: bool,
screen: Option<u64>,
@@ -97,6 +105,8 @@ impl Pump {
rail_open: false,
nav: None,
nav_open: true,
rows: Rows::new(),
progress: Progress::new(),
close_pending: false,
screen: None,
rank: None,
@@ -127,6 +137,9 @@ impl Pump {
// `app/update/motion_sync.rs` 在定档之后 `snap_nav` 对表(那里的判据是「没有轨在跑」)。
self.nav = None;
self.nav_open = true;
// M305:定档即复位两条新轨道。行清单**不**在这里猜(第一次观察只对表,见 `rows.rs`)。
self.reset_rows();
self.reset_progress();
self.close_pending = false;
if scale.is_off() {
// 关掉动效 = 直接落终态:把在跑的轨道清掉,而不是让它跑一遍 0 时长。
@@ -155,6 +168,8 @@ impl Pump {
self.page.is_some()
|| self.rail.is_some()
|| self.nav.is_some()
|| self.rows.is_animating()
|| self.progress.is_animating()
|| matches!(self.modal, Modal::Opening { .. } | Modal::Closing { .. })
}
@@ -327,6 +342,9 @@ impl Pump {
self.nav = None;
}
}
// M305:行与进度各自清理(各自的「到期即删」出口,见两个子模块)。
self.expire_rows(now);
self.expire_progress(now);
let spec = modal_spec();
match self.modal {
Modal::Opening { start_ms } if now - start_ms >= self.leg_ms(spec.enter.duration) => {