Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

动画

Eval

Ranim 的叶子动画核心是一个归一化纯函数:输入进度 alpha,输出对应状态 T

pub trait Eval {
    type Output;
    fn eval_alpha(&self, alpha: f64) -> Self::Output;
}

具体 evaluator 同时保存求值所需的数据。例如 Static<T> 始终返回同一个值,Morph<T> 保存插值需要的源状态和目标状态。

Eval 自动成为叶子动画

只要 Eval::Output 可以提取为场景元素,该 evaluator 就自动获得默认 linear、1 秒、enabled 的 Animation 实现:

pub struct FadeIn<T: FadingRequirement> {
    src: T,
    dst: T,
}

impl<T: FadingRequirement> Eval for FadeIn<T> {
    type Output = T;
    fn eval_alpha(&self, alpha: f64) -> Self::Output { /* ... */ }
}

因此 FadeIn<T> 本身就是一个可组合动画,不需要 marker 或宏。Fn(f64) -> T 闭包也自动实现 Eval<Output = T>,可以直接设置播放参数:

let animation = (|alpha| Square::new(alpha)).with_duration(2.0);

具名 evaluator 和闭包都不会在进入动态容器前擦除类型。AnimSequence::pushAnimStack::push 或 Scene build 时会将直接子节点转换为保留层级的运行时节点。

Paramed<A>

所有尚未固定父时间坐标的 Placeable 动画通过 AnimationExt 获得统一的播放参数 API:

animation
    .with_duration(2.0)
    .with_rate_func(smooth)
    .with_enabled(true)

第一次调用会生成 Paramed<A>。它只属于 Animation 层,负责 duration override、rate function 和 enabled,不再实现 Eval。裸动画的默认值是 linear、1 秒和 enabled。Sequence 或 Stack 被包装时,rate function 重映射整个组合的局部时间轴。

At<A> 表示已经固定在父时间坐标中的 entry,不再实现 Placeable,因此参数必须在 placement 之前设置:

animation.with_duration(2.0).at(3.0); // At<Paramed<A>>

Animation 与 build

所有可组合动画实现:

pub trait Animation: Sized {
    fn build(self) -> AnimationCell;
}

Animation 不再提前暴露 time range 或 duration,它只负责将静态定义 lower 为局部坐标中的 AnimationCell

  • 普通叶子 build 为 0.0..1.0
  • Paramed<A> build 内层后,在外层应用 duration override、rate function 和 enabled;
  • At<A> build 内层后移动根 time range;
  • Sequence push 时先 build 子动画,再将它移动到 cursor;
  • Stack push 时先 build 子动画,再根据 built range 更新整体 duration。

AnimSequenceAnimStack 仍提供自己的 duration_secs() 查询,但通用 Animation trait 不再要求每个静态类型重复提供时间信息。

AnimationCell

Sequence、Stack 和 Scene 需要保存异构动画,因此每个直接子动画会生成一个 AnimationCell

AnimationCell
├─ Box<dyn EvalDyn>
├─ time range
├─ rate function
├─ enabled
└─ evaluator name

EvalDyn 是私有的 object-safe 求值接口:所有 E: Eval 通过 blanket impl 进入类型擦除,AnimSequenceAnimStack 也直接实现该接口。Paramed 直接修改内层 build 出来的 cell,不再额外嵌套一个 AnimationCellhold 保存的已求值结果直接使用 Static<Vec<DynItem>>

动态求值会将结果追加到 Vec<DynItem>,但组合树本身不会被展开。类型擦除只隐藏直接子动画的 Rust 类型,不删除组合层级。时间范围位于 Box 外,供父动画调度和 preview 查询。

Requirement Trait 模式

用户通常不直接构造 evaluator,而是通过 Item 的动画扩展 Trait:

let animation = square
    .fade_in()
    .with_duration(2.0)
    .with_rate_func(smooth);

返回值就是具体 evaluator 类型,例如:

FadeIn<Square>

只有当它进入 AnimSequence::pushAnimStack::pushRanimScene::play 时才会被 build 和擦除。