Flutter动画分析之Tween&Curve
在上篇文章中,我们分析了 Flutter 中主要由AnimationController控制动画的开始、结束,但是默认情况下其只能产生线性变化的 double 类型的 value,如果想随时间变化,让 Widget 产生 Size、Offset 等属性的变化,亦或者控制这些值变化的速度快慢,这时候就需要用到 Tween 和 Curve 了。
下图是 Tween/Curve/AnimationController 等类的关系简单示意:
如上图所示:
- Tween 和 CurveTween 都继承自 Animatable,可以按照给定的 Animation<double>生产 T 类型的值,从而可以从 AnimationController 中衍生出其他类型的 Animation;
- 而 Curve 继承自 _ParametricCurve_,除了之前文章中分析的传入
AnimationController.animateTo
和AnimationController.animateBack
中从而作用于其创建的_InterpolationSimulation.x()
方法之外,也可以被传入 CurveTween 或 CurvedAnimation 中,将 Curve 应用于 AnimationController。
上述关于 Tween 和 CurveTween 和 AnimationController 作用的方式,其实现都是依赖于 Animation<T> Animatable.animate(Animation<double> parent)
方法,根据传入的 Animation<double>(一般会是 AnimationController 对象)创建新的 Animation<T>(实际上是继承自 Animation 的_AnimatedEvaluation,其 Animation.value 取自 Animatable.evaluate(Animation<double>)
)。
源码分析
Animatable
An object that can produce a value of type
T
given anAnimation<double>
as input.
Animatable 根据传入的Animation<double>
对象创建 T 类型的对象,也就是说其将 Animation 产生的 double 类型“转化”为 T 类型,从而使得 Flutter 支持更加丰富的动画。
一般情况下这个
Animation<double>
的值范围是[0.0,1.0],但是也可能超出此范围。
而这一切都通过他的animate
方法实现:
1 | Animation<T> animate(Animation<double> parent) { |
在此方法中,将接收 Animation<double>对象作为parent
,将自身作为_evaluatable
属性创建了 Animation 的子类_AnimatedEvaluation 并返回:
1 | class _AnimatedEvaluation<T> extends Animation<T> with AnimationWithParentMixin<double> { |
再看一下 Animatable 的源码:
1 | T evaluate(Animation<double> animation) => transform(animation.value); |
其evaluate
方法内部通过其唯一的抽象方法transform
方法实现,Animatable 的各个子类也只需要实现transform
方法即可。
除了上述与 Animation 有关的三个方法外,Animatable 还有一个链接两个 Animatable 的方法——Animatable.chain()
:
1 | Animatable<T> chain(Animatable<double> parent) { |
与 Animatable.animate 方法类似,不同的是此方法返回的是 Animatable<T>的子类——_ChainedEvaluation
1 | class _ChainedEvaluation<T> extends Animatable<T> { |
可以此方法作用是结合两个 Animatable 的效果。
Animatable 的主要作用是根据传入的 Animation 创建对应的 T 类型的值;其主要的子类有 Tween、CurveTween、TweenSequence。
TweenSequence 的作用于_chainedEvaluation 类似,只不过它可以将多个 Animatable 按照所占比重 weight 在 Animatable.transform 中应用。
Tween
A linear interpolation (插值) between a beginning and ending value.
Tween 是 Animatable 的主要子类之一,作用根据传入的 Animation(通常是 AnimationController)是在传入的begin 和 end 值之间创建线性的插值。
1 | class Tween<T extends Object?> extends Animatable<T> { |
从 Tween 的源码可以看到,它实现了父类 Animatable.transform 方法,并在 t 在(0.0,1.0)之间时调用 Tween.lerp 方法获取对应的值,默认线性的在 T 上应用加减乘运算,并返回结果。Tween 的子类只需要重写 Tween.lerp 方法而非 Animatable.transform 方法。
这也就要求:
支持
lerp
静态方法的类通常有对应的 Tween 子类,一般以 FooTween 命名,比如 ColorTween 就是借助 Color.lerp 方法实现:1
2
3
4
5
6
7
8class ColorTween extends Tween<Color?> {
// 如果需要渐变透明,请传入null而非Color.transparent,后者实际是黑色透明,会导致渐变为黑色
ColorTween({ Color? begin, Color? end }) : super(begin: begin, end: end);
Color? lerp(double t) => Color.lerp(begin, end, t);
}Tween<T> 的类型 T 必须支持
+-*
三种运算,并且返回值还是 T;对于 int 来说,因为 int*double=num 而非 int,有对应的特殊类:
- IntTween,使用 double.round 实现近似线性插值
- StepTween,使用 double.floor 确保结果永远不会大于使用 Tween<double>的值
在使用时,如果 Tween 确定不会变化,就可以将其保存在static final
对象中以便在需要的地方共享同一个对象,而非在 State.build 方法中实时创建。
CurveTween
CurveTween 继承自 Animatable<double>,常见的用法是传入AnimationController.drive
方法中获取一个新的 Animation<double>:
1 | class CurveTween extends Animatable<double> { |
Tween 和 CurveTween 的主要区别在于,Tween 需要 T?类型的 begin 和 end 来创建线性插值,而 CurveTween 则需要 Curve 以便为 Animation<double>创建(非)线性插值。
Curve
ParametricCurve<T>是 Curve 的父类,其提供 ParametricCurve.transform 方法将 double t(在[0.0,1.0]之间)转化为曲线在 t 处对应的值 T t:
1 | abstract class ParametricCurve<T> { |
从其源码可以看出,ParametricCurve.transform 主要是检查入参是否合规,其主要逻辑在 ParametricCurve.transformInternal 中,一般子类只需要实现后者即可。
Curve 继承自 ParametricCurve<double>,也就是说它只能产生 double 类型的插值:
1 | abstract class Curve extends ParametricCurve<double> { |
Curve 重写了父类的 transform 方法以规范对 double t 的处理,但还是建议子类只需要实现 ParametricCurve.transformInternal
方法。
我们以 Curve 的子类_Linear 为例,查看实现 Curve 的过程:
1 | class _Linear extends Curve { |
总的来说,Curve 及其子类定义了一个曲线(可能是线性变化,也可能不是),并提供了double Curve.transform(double t)
供使用者获取指定时间 double t 时曲线上对应的值 double。
Flutter 为我们预置了很多丰富的 Curve,可以在这里预览:
Curves。
总结
经过上述分析,我们可以知道,无论是 Tween 还是 CurveTween,作为 Animatable,他们提供了Animation<T> Animatable.animatee(Animation<double> parent)
方法,可以返回一个新的,相当于使用Animatable<T>.transform(double parent.value)
计算Animation<T>.value
的,Animation<T>。
而 Curve,只能通过double transform(double t)
计算曲线在 t 位置的值的类,一般可以在 CurveTween、CurveAnimation 的构造方法或者 AnimationController 的 animateTo/animateBack 方法中,以便产生非线性的动画。