动画的基本定义是通过随时间变化改变图像而产生的运动或变化的视觉效果。此示例的目标是创建月球绕其垂直轴旋转的视觉效果。然而,对于动画而言,您可以忽略该示例中球形扭曲方面的问题。假设已加载并用作月球图像数据源的实际图像如下:
如您所见,该图像并不是一个或几个球体;它是月球表面的一张矩形照片。因为该照片刚好是在月球赤道上拍摄的,所以图像中靠近图像顶部和底部的部分发生拉伸和扭曲。若要消除图像的扭曲使其具有球形外观,我们将使用置换图滤镜(在后面进行介绍)。但是,因为该源图像是矩形,所以若要产生旋转球体的视觉效果,代码只要能完成水平滑动月球表面照片的操作即可。
请注意,该图像实际上由月球表面照片的两个副本彼此相接而成。该图像是要从中重复复制图像数据来创建动画外观的源图像。通过两个图像副本彼此相接,会更容易产生连续、不间断的滚动效果。让我们逐步浏览动画生成的过程来看看这是如何实现的。
该过程实际上涉及两个单独的 ActionScript 对象。首先,有加载的源图像,在代码中由名为 textureMap 的 BitmapData 实例表示。如前所述,外部图像加载后,将用图像数据来填充 textureMap,使用的代码如下:
textureMap = event.target.content.bitmapData;
textureMap 的内容是矩形的月球图像。另外,为了产生旋转动画,该代码使用名为 sphere 的 Bitmap 实例,该实例是在屏幕上显示月球图像的实际显示对象。与 textureMap 一样,sphere 对象也是在 imageLoadComplete() 方法中创建并使用其初始图像数据填充,使用的代码如下:
sphere = new Bitmap();
sphere.bitmapData = new BitmapData(textureMap.width / 2, textureMap.height);
sphere.bitmapData.copyPixels(textureMap,
new Rectangle(0, 0, sphere.width, sphere.height),
new Point(0, 0));
如代码所示,sphere 被实例化了。其 bitmapData 属性(通过 sphere 显示的原始图像数据)具有与 textureMap 相同的高度和一半的宽度。换句话说,sphere 的内容将是一幅月球照片的大小(因为 textureMap 图像包含并排的两幅月球照片)。接下来,用图像数据填充 bitmapData 属性,填充时使用的是 copyPixels() 方法。copyPixels() 方法调用中的参数指示以下几点:
第一个参数指示从 textureMap 复制图像数据。
第二个参数(新的 Rectangle 实例)指定图像快照应该从 textureMap 的哪部分拍摄;在本例中,快照是从 textureMap 左上角开始的一个矩形(由前两个 Rectangle() 参数 0, 0 指示),矩形快照的宽度和高度与 sphere 的 width 和 height 属性一致。
第三个参数(新的 Point 实例)的 x 和 y 值都为 0,它定义了像素数据的目标位置 — 本例中为 sphere.bitmapData 的左上角 (0, 0)。
从视觉表示形式上看,该代码将复制下图中用轮廓线标出的、textureMap 的像素,并将其粘贴到 sphere 上。换句话说,sphere 的 BitmapData 内容是这里加亮的 textureMap 部分:
然而请记住,这只是 sphere 的初始状态 — 复制到 sphere 上的第一项图像内容。
加载源图像并创建 sphere 之后,由 imageLoadComplete() 方法执行的最终任务是设置动画。动画由名为 rotationTimer 的 Timer 实例驱动,该实例由以下代码创建并启动:
var rotationTimer:Timer = new Timer(15);
rotationTimer.addEventListener(TimerEvent.TIMER, rotateMoon);
rotationTimer.start();
代码首先创建名为 rotationTimer 的 Timer 实例;传递给 Timer() 构造函数的参数指示 rotationTimer 应每 15 毫秒触发一次其 timer 事件。接下来将调用 addEventListener() 方法,以指定在发生 timer 事件 (TimerEvent.TIMER) 时调用 rotateMoon() 方法。最后,计时器实际上是通过调用其 start() 方法启动的。
根据 rotationTimer 的定义方式,约每 15 毫秒 Flash Player 调用一次 MoonSphere 类中的 rotateMoon() 方法(用于产生月球动画)。rotateMoon() 方法的源代码如下:
private function rotateMoon(event:TimerEvent):void
{
sourceX += 1;
if (sourceX > textureMap.width / 2)
{
sourceX = 0;
}
sphere.Data.copyPixels(textureMap,
new Rectangle(sourceX, 0, sphere.width, sphere.height),
new Point(0, 0));
event.updateAfterEvent();
}
该代码实现以下三方面的操作:
变量 sourceX 的值(最初设为 0)增加 1。
sourceX += 1;
您将看到,sourceX 用于确定 textureMap 中的位置(从该位置将像素复制到 sphere),因此该代码会产生在 textureMap 上将矩形向右移动一个像素的效果。返回到视觉表示形式,经过几个动画循环之后,源矩形将向右移动几个像素,如下所示:
经过几个循环之后,矩形将进一步移动:
像素复制位置的这种平稳渐进式移动是动画制作的关键。通过缓慢、连续地将源位置移动到右侧,sphere 中显示在屏幕上的图像显示为连续地滑向左侧。这就是源图像 (textureMap) 需要两个月球表面照片副本的原因。由于矩形连续移动到右侧,因此大多数时间该矩形不是在一张月球照片上而是与两张月球照片发生重叠。
随着源矩形缓慢移到右侧,会出现一个问题。最后,矩形将到达 textureMap 的右边缘,它将用完要复制到 sphere 上的月球照片像素:
下一行代码将解决这个问题:
if (sourceX >= textureMap.width / 2)
{
sourceX = 0;
}
该代码检查 sourceX(矩形的左边缘)是否已到达 textureMap 的中部。如果是,它会将 sourceX 重置为 0,将其移回到 textureMap 的左边缘并重新开始循环:
计算出适当的 sourceX 值后,创建动画的最后一步是将新的源矩形像素实际复制到 sphere 上。实现这一操作的代码与最初填充 sphere 的代码(如前面所述)非常类似;唯一的不同是:在本例的 new Rectangle() 构造函数调用中,矩形的左边缘位于 sourceX:
sphere.bitmapData.copyPixels(textureMap,
new Rectangle(sourceX, 0, sphere.width, sphere.height),
new Point(0, 0));
请记住,此代码每 15 毫秒重复调用一次。由于源矩形的位置是连续移动的,并且像素被复制到 sphere 上,因此在屏幕上显示为由 sphere 表示的月球照片图像发生连续滑动。换句话说,月球显示为连续旋转。