15.5.2 使用行为创建动画
我们已经有了创建动画的所有基础部件和显示的表单,现在,可以开始创建动画了。我们将从绘制两个彩色的圆开始,以前在 F# 中已经创建了,创建移动的动画。本节中,我们只使用 F#,因为将使用 F# Interactive 进行实验。你会看到,我们写的基元提供了很大的灵活性,我们会考虑可能用动画图形要做的其他操作。之后,还要回到 F# 和 C# 去实现所有的。清单 15.17 创建窗体,并显示第一个动画。
Listing 15.17 Creating simple animation (F# interactive)
> let translate x y img = Behavior.lift3 Drawings.translate x y img;;
val translate :
Behavior<float32> -> Behavior<float32> ->
Behavior<#Drawing> -> Behavior<Drawing>
> let wiggle100 = Behavior.lift2 (*) wiggle 100.0f.forever;;
val wiggle100 : Behavior<float32>
> let af = new AnimationForm(ClientSize = Size(750, 750), Visible = true);;
val af : AnimationForm
> af.Animation <- translate wiggle100 0.0f.forever animDrawing;;
清单 15.17 首先创建 translate 基元的一个版本,使用提升处理动画。原始函数通过固定方式,转换现有绘图,创建新的绘图。这个版本通过变量方式,转换现有动画,创建新的动画。每当窗体要绘制一帧动画,动画会要求两个 float32 行为,它们的值是这个行为所在的点,以得出如何转换绘图。它还要求原始的动画,以绘制自身(同样,对于指定的时间),仅仅适当地转换。
这个实现使用了 Behavior.lift3 基元,它把有三个参数的函数转换成处理行为的函数。正如你从推导出的类型签名看到的,该函数现在取两个指定偏移量的行为,和一个指定的动画(这里,#<type> 的使用并不重要,我们可以把它读作 Behavior<Drawing>),返回类型是另一个动画。
如果开始你觉得这有点混乱,也不用担心[2]:只把它看作是三个完全不同的随时间变值的组合,一个指定水平偏移,一个指定垂直偏移,还有一个指定绘图,如果根本不转换,它看起来的样子。在接下来的两行上,我们使用这个基元,来创建一个动画,可以在图 15.5 中看到。
图 15.5 来自图 15.4 的两个圆,从左到右移动(添加阴影,以便可以看到对象如何移动)
为了定义动画,首先需要创建一个行为,给出合理大小的 X 和 Y 偏移量。我们是通过将 wiggle 基元乘以总是返回 100 的常量行为实现的。这意味着,wiggle100 的值将在 –100 到 +100 之间振荡。接下来,我们创建并显示适当大小的动画效果。最后,我们用新的 translate 函数,取这些行为作为参数值。我们把 wiggle100 值作为 X 坐标,和总是 0.0f 的行为作为 Y 坐标,结果是一个动画,设置绘图值的坐标在 (-100,0) 和 (100,0) 范围之间。
注意
在书中呈现动画是有点挑战性的,为明显原因。要给出动画如何移动的迹象,我们添加了显示对象的早期位置的阴影。实现这种有吸引力的效果是相当容易的:需要做的就是反复绘制动画,将不同的时间传递给行为,使用 .NET 的绘图函数,使旧的动画变得越来越透明。我们不会在书上看这个实现,但可以在本书的网站上找到它(作为表示动画的窗体的另一种类型)。这是组合强大的另一个例子。
清单 15.17 表明,当创建动画时,相当频繁地需要两种操作。我们需要基元函数的提升版本,比如,把 translate 或 compose 应用于动画,并且,如果我们能在行为上执行简单的算法,不显式使用提升作为,也是很好的,比如我们在这里创建 wiggle100。让我们看看如何做。
-------------------
[2] 两个作者当然是做了:当时 Tomas 在理解 Haskell Fran 库,Jon 在阅读此处提供的代码。我们相信你一定能明白。