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

动画

本节将对 Ranim 中 动画 的实现思路进行讲解。

EvalDynamic<T> Trait

一个标准化的动画其实本质上就是一个函数 ,它的输入是一个进度值 ,输出是该动画在对应进度处的结果

这个函数 不仅定义了动画的 求值,同时其内部也包含了求值所需要的 信息。对应到计算机世界,其实也就是 算法数据,而对应到编程语言上也就是 方法数据类型

在由 Rust 实现的 Ranim 中也就是 EvalDynamic<T> Trait 和实现了它的类型 T

pub trait EvalDynamic<T> {
    fn eval_alpha(&self, alpha: f64) -> T;
}

它接受自身的不可变引用和一个进度值作为输入,经过计算,输出一个自身类型的结果。

Transform 动画为例,其内部包含了物件初始状态和目标状态,以及用于插值的对齐后的初始和目标状态,在 EvalDynamic<T> 的实现中使用内部的数据进行计算求值得到结果:

/// Transform Anim
pub struct Transform<T: TransformRequirement> {
    src: T,
    dst: T,
    aligned_src: T,
    aligned_dst: T,
}

impl<T: TransformRequirement> EvalDynamic<T> for Transform<T> {
    fn eval_alpha(&self, alpha: f64) -> T {
        if alpha == 0.0 {
            self.src.clone()
        } else if 0.0 < alpha && alpha < 1.0 {
            self.aligned_src.lerp(&self.aligned_dst, alpha)
        } else if alpha == 1.0 {
            self.dst.clone()
        } else {
            unreachable!()
        }
    }
}

AnimationSpan

有了以进度 为输入标准化的动画函数后,加上持续秒数 、速率函数 ,就可以构造一个以秒 为输入的动画函数

在 Ranim 中,这对应着 AnimationSpan 结构:

pub struct AnimationSpan<T> {
    pub(crate) evaluator: Evaluator<T>,
    pub rate_func: fn(f64) -> f64,
    pub duration_secs: f64,
}

impl<T> AnimationSpan<T> {
    pub fn eval_alpha(&self, alpha: f64) -> EvalResult<T> {
        self.eval_sec(alpha * self.duration_secs)
    }
    pub fn eval_sec(&self, local_sec: f64) -> EvalResult<T> {
        self.evaluator.eval_alpha((self.rate_func)(
            (local_sec / self.duration_secs).clamp(0.0, 1.0),
        ))
    }
}

其中的 evaluator: Evaluator<T> 其实就是对 Box<dyn EvalDynamic<T>> 的封装。