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

@@ -193,8 +193,30 @@ pub(crate) fn circular_colors(ui: &Ui) -> (iced::Color, iced::Color) {
/// 轨道宽 `Fill`(由宿主决定),指示条按占比分到 `FillPortion` 上——**不需要知道轨道像素宽**,
/// 所以 50% 在任何宿主宽度下都精确是半条。
pub fn linear<'a, M: 'a>(ui: &'a Ui, progress: Progress) -> Element<'a, M> {
linear_with(ui, progress, 0.0)
}
/// 线性进度条 + **到达收束**(M305):`settle`(`0..=1`)是「刚到 100%」那一下的脉冲强度。
///
/// `settle = 0` 时与 [`linear`] **逐像素相同**(同一个函数、同一条取色),所以静息帧与末帧
/// 都不受这一版影响。脉冲只改**指示条的颜色**:`Primary` ⇄ `InversePrimary` 之间按 `settle`
/// 预合成插值(两端都是实色,D27 的「不给渲染器半透明」不破)——不改厚度、不改圆角,
/// 于是它**不引起任何重排**(厚度改 1px 会把下面一行文字顶下去,那是像素级跳动)。
///
/// 为什么是「颜色亮一下」而不是「条涨一点」:100% 时指示条已经占满轨道,再涨没有余量;
/// 而颜色的强调在深浅两个主题下都读得出来(`InversePrimary` 是同一强调色族的另一端)。
pub fn linear_with<'a, M: 'a>(ui: &'a Ui, progress: Progress, settle: f32) -> Element<'a, M> {
let geometry = LinearGeometry::of(ui);
let (track_color, active_color) = linear_colors(ui);
let (track_color, base_active) = linear_colors(ui);
let active_color = if settle > 0.0 {
super::super::motion::mix(
ui.scheme().get(Role::Primary),
ui.scheme().get(Role::InversePrimary),
settle.clamp(0.0, 1.0),
)
} else {
base_active
};
let mut bar = row![]
.width(Length::Fill)