概述
15.5.3 添加动画基元
我们的目标是使构造动画的代码尽可能声明性和简单,为此,我们要提供专门为创建动画而设计的基元。我们已经看到,已经可以做需要的任何事情,只用现有的处理行为和绘图的函数,但是,如果我们可以使用专门的用于创建动画的基元,而不是显式使用提升,代码看起来会更优雅。让我们首先看一下处理绘图的函数。
创建用于动画的绘图基元
在清单 15.17 中,我们创建了 translate 基元,通过提升 Drawings.translate 函数处理动画。现在,我们需要做同样的事情,为其他绘图基元 circle 和 compose。清单 15.18 显示 F# 为创建一个动画的圆而声明的一个组合运算符和基元。还包括了组合的 C# 版本( 这次是作为扩展方法),来演示如何在 C# 中使用提升。其他提升操作的 C# 实现本质上是一样的,因此,没有包含在这个列表中。
Listing 15.18 Creating animation primitives using lifting in F# and C#
// F# version
> let circle brush size =
Behavior.lift2 Drawings.circle brush size
let ( -- ) anim1 anim2 =
Behavior.lift2 Drawings.compose anim1 anim2
;;
val circle : Behavior<#Brush> -> Behavior<float32> -> Behavior<Drawing>
val (--) : Behavior<#Drawing> -> Behavior<#Drawing> -> Behavior<Drawing>
// C# version
public static class Anims {
public static Behavior<IDrawing> Compose
(this Behavior<IDrawing> anim1, Behavior<IDrawing> anim2) {
return Behavior.Lift<IDrawing, IDrawing, IDrawing>
(Drawings.Compose)(anim1, anim2);
}
}
在 F# 中使用提升是很容易的,由于其先进的类型推断。我们早些时候创建了 translate 基元,因此,在这个清单中,只添加了 circle 函数和一个自定义操作符,用于组合动画。它们都有两个参数,所以,可以使用 Behavior.lift2 函数,一个参数是处理的函数,另一个是把它转换成处理动画的变量。正如你从推导出的类型签名看到的,函数 (或运算符)的结果,是取两个行为作为参数,返回一个行为(更具体地说,是 Behavior<Drawing>,表示动画)。函数 circle 现在取画笔和大小都作为行为,因此,我们可以创建随时间改变颜色和大小的圆。
在 C# 版本中,我们为所有操作创建静态的 Anims 类。第 6 章中,我们比较过 C# 中的扩展方法和 F# 中的自定义运算符,建议应把 Compose 实现为扩展方法。在方法体中,使用提升,获得 Drawings.Compose 方法的提升版本。Lift 方法返回一个我们立即调用的函数,我们给它两个动画作为参数,并返回组合了的动画结果。在讨论如何使用新功能之前,做两个改进,让我们能够用行为做更有趣的事。
用行为计算
我们频繁使用的另一个操作,是为行为乘以或加上一个数字。在示例动画中,我们想为 wiggle 值乘以常量行为 100.0f.forever。没有显式使用提升,而是提供重载运算符,处理数字的行为,更加方便。清单 15.19 显示如何在 F# 中实现加法和乘法两个运算符。
Listing 15.19 Extension operators for calculating with behaviors (F#)
type Behavior<'T> with
static member (+) (a:Behavior<float32>, b) =
Behavior.lift2 (+) a b
static member (*) (a:Behavior<float32>, b) =
Behavior.lift2 (*) a b
在 F# 中,我们可以使用固有的类型扩展,为类型添加运算符。将为类型添加两个静态成员,每个成员都取两个类型为 Behavior<float32> 的参数。添加处理任何数值类型的泛型运算符,将更加困难,所以,我们在这一章中,只创建处理数值类型的运算符。运算符的实现很容易,因为,我们可以用适当的提升函数来表达。C# 的实现是几乎完全相同,所以,我们不谈论它。
我们已经把加法和乘法概念应用到位置,但是,我们的动画处理的其他维度是时间,我们可以做什么呢?好吧,我们可能要加快动画,或延迟一段时间。如果我们有两个旋转的圆,可能要使一个旋转的比另一个快两倍。我们可以创建一个新的基元行为,但是,有一种更优雅的方法,可以创建一个函数,取一个行为作为参数,并返回一个新的行为,它运行得更快,或延迟了指定的秒数。你可以在清单 15.20 中看到 F# 的版本。
Listing 15.20 Speeding up and delaying behaviors (F#)
let wait shift (BehaviorFunc bfunc) =
sample(fun t -> bfunc { t with Time = t.Time + shift })
let faster q (BehaviorFunc bfunc) =
sample(fun t -> bfunc { t with Time = t.Time * q })
清单 15.20 中的函数使用可以处理任何一种行为,而不仅仅是动画。每个函数取一个浮点数作为第一个参数,原始的行为作为第二个参数。为了创建一个新的行为,我们必须使用低级的 sample 基元。我们创建一个新的行为,它调用从原始行为中提取的函数,并把不同的时间给它作为参数。在第一种情况下,时间被推移了指定的秒数,第二种情况中,它乘上了提供的系数。为了解释其含义,让我们看一下第二个函数,假设我们将以两倍的速度运行一个行为。当实际时间为 3 秒,所返回的行为将用 6 秒的时间调用原来的行为,这意味着,动画做的动作,通常会执行 6 秒,只需 3 秒。
我们可以写一个完全通用的函数,用于操作时间。更一般的版本会用行为调整时间,即,取原始时间,返回调整的时间的函数。我们已经创建的 simple 函数的使用和理解将容易。
在最后的演示中,我们将创建太阳、月亮和地球的动画。它将使用我们迄今实现的所有功能,来创建一个复杂、有趣的动画,用它来演示如何组合和可重用解决方案。
最后
以上就是顺利紫菜为你收集整理的15.5.3 添加动画基元的全部内容,希望文章能够帮你解决15.5.3 添加动画基元所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复