15.5.3 添加动画基元

728 篇文章 1 订阅
29 篇文章 0 订阅

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 函数的使用和理解将容易。

    在最后的演示中,我们将创建太阳、月亮和地球的动画。它将使用我们迄今实现的所有功能,来创建一个复杂、有趣的动画,用它来演示如何组合和可重用解决方案。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值