feat(app-iced): 页面切换按导航深度定向 + 模态缩放淡入(M256 MOTION1)
- transition.rs:唯一一张动效表。同级换页走水平(±12 / 旧页 ∓8)、进深层走纵向
(新页 +16 上浮 / 旧页 -8),进腿 medium2/medium1(300/250),退腿 short4→medium1(更短),
进用 Standard、退用 StandardAccelerate。幅度全部取自 Space 令牌。
- pump.rs:Slot 三互斥槽 → Kind(Boot/Peer{forward}/Push/Pop)+ 两腿轨道;新增模态状态机
(Opening/Open/Closing,展开后不占帧,退场走完交回一次 take_modal_closed)。
- components/motion/zoom.rs:绕中心缩放的自绘控件(renderer.with_transformation,
tiny-skia 与 wgpu 都实现了,文字也真的跟着缩放);scale==1.0 时不套变换 ⇒ 出图基线不变。
- pages/mod.rs:page_with_settings_transition(ui, current, previous, …) 叠
stack![opaque(旧页@退场位移), 新页@进场位移];旧函数只剩一句委托(blast radius 不变)。
- App.previous_view:换页时用 std::mem::replace **移动**旧模型(冻结快照),过渡结束当拍
置 None(不随换页次数增长)。
- 模态:逻辑状态当拍关(state.dialogs 立刻空),画面多留 150ms;退场期间弹窗消息被闸门吞掉。
- platform/motion.rs:系统「减少动效」三态探针(Windows=SystemParametersInfoW,
Linux/macOS=子进程+300ms 超时),OnceLock 缓存;Unknown **不降级**(探针失败不静默关动效)。
- MotionScale::HALF 语义核清并写进 design 文档:是**时长减半**(更快),不是半速。
This commit is contained in:
1 parent
5a182e80de
commit
b1c09c4c91
21 files changed
+2168
-658
No files matched your search
@@ -0,0 +1,95 @@
|
||||
//! 有界泵的算术守卫(M256 从 `tests/mod.rs` 拆出,守 400 行硬线):采样频率上限、到期即退订、
|
||||
//! stale 帧基准不许一帧杀完、以及 `MotionScale::HALF` 的**语义**(时长减半 ≠ 半速)。
|
||||
//!
|
||||
//! 这些全是「本地 `Pump` + 摆时间轴」就能判死的算术性质,所以不需要窗口、不需要真实时间。
|
||||
|
||||
use super::super::pump::{Pump, FRAME_MS, MAX_STEP_MS};
|
||||
use super::super::transition::PageFrame;
|
||||
use super::{settle, standard};
|
||||
use unlucky_design::motion::{DurationToken, MotionScale};
|
||||
|
||||
// ------------------------------------------------------------------ 有界泵
|
||||
|
||||
/// **空闲不烧帧**:动画跑完 ⇒ `is_animating()` 立刻为假,且不再推进。
|
||||
///
|
||||
/// 抓得住:把「到期即删除」删掉(轨道永远留着 ⇒ 帧订阅永不退订)。
|
||||
#[test]
|
||||
fn a_finished_animation_stops_asking_for_frames() {
|
||||
let mut pump = standard();
|
||||
assert!(pump.observe(Some(1), None));
|
||||
assert!(pump.is_animating(), "起跑后必须有轨道(订阅帧的唯一理由)");
|
||||
|
||||
settle(&mut pump);
|
||||
assert!(!pump.is_animating(), "动画结束必须**当场**退订");
|
||||
assert_eq!(pump.page_frame(), PageFrame::STILL);
|
||||
|
||||
let before = pump.samples();
|
||||
for _ in 0..60 {
|
||||
assert!(!pump.advance(FRAME_MS), "没有轨道时推进必须返回 false");
|
||||
}
|
||||
assert_eq!(pump.samples(), before, "空闲态一次采样都不该发生");
|
||||
}
|
||||
|
||||
/// **采样频率上限**:帧到得再快,时间轴也只按 16 ms 结算(≈62.5 次/秒)。
|
||||
#[test]
|
||||
fn frames_are_capped_at_sixty_per_second() {
|
||||
let mut pump = standard();
|
||||
pump.observe(Some(1), None);
|
||||
|
||||
let mut advanced = 0;
|
||||
for _ in 0..200 {
|
||||
if pump.advance(1.0) {
|
||||
advanced += 1;
|
||||
}
|
||||
}
|
||||
assert!(
|
||||
(11..=13).contains(&advanced),
|
||||
"200 ms 内应 12±1 次推进,实测 {advanced}"
|
||||
);
|
||||
assert_eq!(pump.samples(), advanced);
|
||||
assert!(
|
||||
(pump.now_ms() - 192.0).abs() <= 16.0,
|
||||
"时间轴应约 192 ms,实测 {}",
|
||||
pump.now_ms()
|
||||
);
|
||||
}
|
||||
|
||||
/// **stale 帧基准不许把轨道一帧杀完**(M192 r1 复审 P1-1 的算术面)。
|
||||
#[test]
|
||||
fn a_stale_baseline_cannot_kill_the_track_in_one_sample() {
|
||||
let mut pump = standard();
|
||||
pump.observe(Some(1), None);
|
||||
|
||||
assert!(pump.advance(60_000.0)); // 60 秒的"空闲间隔"当 dt
|
||||
assert!(pump.is_animating(), "一次超长 dt 不许把轨道当场删掉");
|
||||
assert!(pump.now_ms() <= MAX_STEP_MS + 1.0e-6);
|
||||
assert!(pump.page_frame().enter.1 > 0.0, "进度不许一步到位");
|
||||
|
||||
settle(&mut pump);
|
||||
assert!(!pump.is_animating());
|
||||
}
|
||||
|
||||
/// 半时长档(CPU 渲染兜底)真的把时长减半:同样的推进,轨道更早结束。
|
||||
///
|
||||
/// 顺带钉住 `MotionScale::HALF` 的**语义**(M256 核清项②):是「时长 ×0.5」=更快,
|
||||
/// 不是"半速"。抓得住:把 `HALF.factor` 改成 2.0(半速)或 1.0(无效)。
|
||||
#[test]
|
||||
fn half_scale_halves_the_duration_not_the_speed() {
|
||||
assert_eq!(MotionScale::HALF.factor(), 0.5);
|
||||
assert_eq!(MotionScale::HALF.ms(DurationToken::Medium2), 150.0);
|
||||
|
||||
let mut full = standard();
|
||||
full.observe(Some(1), None);
|
||||
let full_frames = settle(&mut full);
|
||||
|
||||
let mut half = Pump::new();
|
||||
half.configure(MotionScale::HALF, None);
|
||||
half.observe(Some(1), None);
|
||||
let half_frames = settle(&mut half);
|
||||
|
||||
assert!(half_frames > 0 && full_frames > 0);
|
||||
assert!(
|
||||
half_frames * 2 <= full_frames + 2,
|
||||
"HALF 应约用一半帧数:full={full_frames} half={half_frames}"
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user